設計模式--模版模式
-
涉及到的知識點
-
Java 的接口和抽象類信息
一個類只可以繼承另外一個類,但是可以實現(xiàn)多個接口
抽象類中可以定義屬性粟耻,接口中的屬性都被 定義為 public final static 修飾挤忙,屬于常量
接口和抽象類都可以實現(xiàn)邏輯,接口通過default 修飾方法實現(xiàn)戈泼,抽象類通過普通方法實現(xiàn)
接口中的方法默認修飾為 public abstract
抽象方法在子類中都必須被重寫
-
---別人補充的
抽象類沒有辦法被new大猛,因為有抽象方法沒有實現(xiàn)挽绩。接口相同
抽象類默認修飾為public ,可以使用public 和 protected 修飾
接口中不能含有靜態(tài)代碼塊和靜態(tài)方法驾中,但是抽象類中可以含有
設計層面上,接口是對行為的抽象唠亚,而抽象類是對統(tǒng)一類型東西的抽象(鳥和飛機的例子)
抽象類作為很多子類的父類灶搜,是一種模版的設計。接口定義的是行為規(guī)范前酿,是輻射類設計薪者。接口作用是可以在不修改子類的情況下剿涮,為子類添加公共的方法攻人,接口是不能實現(xiàn)的。
-
門和告警功能的實現(xiàn)的例子瞬浓。門的open() 和close() 屬性屬于固有功能猿棉,alarm()屬于附加功能萨赁。因此應該做如下試下
public abstract class Door{ void open(); void close(); } public interface Alarm{ void alarm(); } public class AlarmDoor extends Door implements Alarm{ void open(); void close(); void alarm(); }
-
-
default修飾的方法
在JDK 1.8 中杖爽,接口中新增了default 修飾的方法紫皇,作用和抽象類中的普通方法類似聪铺。在不修改子類實現(xiàn)情況下,修改接口的公共方法撒桨。算是對歷史遺留問題的解決方案。
調用方式赖阻,可以像調用普通父類方法一樣調用接口中default 修飾的方法
- 多個接口中包含default 修飾相同的方法火欧,則實現(xiàn)接口的類必須重寫接口中default修飾的方法
- 如果接口中和父類都含有相同的方法苇侵,接口中用default修飾企锌,那么子類調用時候會使用父類中的方法
- 接口默認方法增強了Java 8中的Collections API以支持lambda表達式撕攒。
接口中使用靜態(tài)代碼塊 static
- 無法被復寫
- 調用方式為 接口名.function()
-
泛型 T E ?的區(qū)別
在JDK 5 中引入的泛型萍鲸,泛型的本質是參數(shù)化類型脊阴。如果不使用泛型嘿期,使用Object 將會使參數(shù)任意化埋合,任意化的結果是對于類型轉換在運行時才可以確定饥悴,可能會出現(xiàn)類型轉化失敗。另外代碼里面需要強制類型轉化瓣铣。
T E K V ? 都是通配符,一般情況下有如下語義
- T type E element K key v value
- ? 表示不確定java類型
泛型和不確定類型的通配符需要單獨說明
for(;;) 的用法 沒有搜索到
-
Obejct...的意思
Java 中的可變參數(shù)
- 可變參數(shù)必須放在最后棠笑,所以一個方法只能有一個可變參數(shù)
- 可變參數(shù)的本質是數(shù)組
- 可變參數(shù)本身是提供的語法糖蓖救,這個在python中使用非常多,在不確定參數(shù)長度時候可以使用
-
protected的含義
作為java中權限訪問控制的一個修飾符
訪問控制的邊界為
- public 所有類都可以使用
- producted 子類可以訪問prodected修飾的變量或者方法斩例,或者相同包下的類可以調用
- Default 基于同包的訪問
- private 只有當前類可以使用變量或者方法
-
模版方法的功能
定義一個操作中的算法的骨架念赶,而將一些步驟延遲到子類中恰力。模板方法使得子類可以不改變一個算法的結構即可重定義該算法的某些特定步驟踩萎。
個人理解: 模版方法是將一些action,通用的部分寫在父類的抽象類中實現(xiàn),各個子類獨立的部分在各個子類中實現(xiàn)香府。減少不必要的重復又解耦的方案
如果我們將編程增加可拓展性和調用修改復雜度兩個方面看回还,模版模式就是對這些目的的實現(xiàn)柠硕。
-
例子1 运提,JDK中AbstrictList 類使用了模版方法,這里的代碼寫的有點奇怪民泵,在AbstrictList中,add(int index, E e )沒有使用抽象方法栈妆,但是同樣需要子類重寫改方法
public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> { public boolean add(E e) { add(size(), e); return true; } /** 在AbstrictList中鳞尔,沒有將add(int index, E e );方法作為一個抽象方法寥假,而是定義方法,同時拋出異常枫振。這樣做的效果是 * 在子類中,如果不重寫這個方法斧拍,編譯器沒有異常杖小,但是在調用父類這個方法時候會拋出異常窍侧。 * 所以,子類必須重寫這個方法硼啤,實現(xiàn)自己的add邏輯 */ public void add(int index, E element) { throw new UnsupportedOperationException(); } } // 子類ArrayList 中斧账,重寫了add(int index, E e ) 這個方法 public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable{ public void add(int index, E element) { rangeCheckForAdd(index); ensureCapacityInternal(size + 1); // Increments modCount!! System.arraycopy(elementData, index, elementData, index + 1, size - index); elementData[index] = element; size++; } }
-
第二個例子是工作中遇到的嗓袱,需求是做一個混合引擎調度器,在執(zhí)行SQL時候渠抹,可以選擇多種引擎去執(zhí)行梧却,在優(yōu)先選擇的引擎執(zhí)行失敗時候使用其他引擎降級放航。
使用模版方法的思路是: 每一個引擎執(zhí)行代碼的邏輯大致是相同的圆裕,比如提前清理文件,校驗執(zhí)行結果等赊时。但是具體的執(zhí)行蛋叼,每個引擎各部想不狈涮。
-
引擎的抽象父類
public abstract class Engine { private List<CheckRule> rules; private String engineName; public Engine(String engineName, List<CheckRule> rules) { this.rules = rules; this.engineName = engineName; } public String getEngineName(){return engineName;} public List<CheckRule> getRules() { return rules; } public boolean run(EngineDispatcher dispatcher, String sql, Properties config){ System.out.println("dear , your task is running..."); long startTime = System.currentTimeMillis(); clearDatePath(config.getProperty("dataPath")); boolean result = runInternal(sql, config); long endTime = System.currentTimeMillis(); if (! result){ return fallBack(dispatcher,config.getProperty("roiginsql"),config); } return result; } protected void clearDatePath(String dataPath) { File file = new File(dataPath); try { if(file.exists()){ FileWriter fileWriter = new FileWriter(file); fileWriter.write(""); fileWriter.flush(); fileWriter.close(); } } catch (IOException e) { e.printStackTrace(); } } protected abstract boolean fallBack(EngineDispatcher dispatcher, String roiginsql, Properties config); protected abstract boolean runInternal(String sql, Properties config); protected abstract void cancel(Properties config); }
Hiveserver2引擎的實現(xiàn)
public class Hiveserver2Engine extends Engine { static final int DEFAULT_QUERERY_TIME_OUT = 10 * 1000; HiveStatement stmt = null; Connection conn = null; public Hiveserver2Engine(String engineName) { super(engineName); } @Override protected boolean fallBack(EngineDispatcher dispatcher, String roiginsql, Properties config) { return false; } @Override protected boolean runInternal(String sql, Properties config) { //具體的實現(xiàn)邏輯 return false; } @Override protected void cancel(Properties config) { } }
Presto的實現(xiàn)
public class PrestoJDBCEngine extends Engine { public PrestoJDBCEngine(String engineName) { super(engineName); } @Override protected boolean fallBack(EngineDispatcher dispatcher, String roiginsql, Properties config) { // 這里需要降級 return false; } @Override protected boolean runInternal(String sql, Properties config) { return runPresto(sql,config); } @Override protected void cancel(Properties config) { } private boolean runPresto(String sql , final Properties config){ return false; } }
-
```java
public class EngineDispatcher {
public static final String HIVE_SERVER2 = "";
public static final String PRESTO_JDBC = "";
public static String DEFAULT_ENGINE = HIVE_CLI;
public Map<String, Engine> engines = new LinkedHashMap<String, Engine>();
private Engine runningEngine;
public EngineDispatcher() {
engines.put(PRESTO_JDBC, new PrestoJDBCEngine(PRESTO_JDBC));
engines.put(HIVE_CLI,new HiveCliEngine(HIVE_CLI));
}
// 選擇引擎的邏輯,這里會有很多條件判斷
public Map<String,String> dispatch(String sql, Properties config){
// 分發(fā)和解析sql規(guī)則松却,確定執(zhí)行的引擎
}
private String dispatchInternal(HiveClient hiveClient,
String selectSql, Properties config) throws SQLException, RuleCheckException, ExplainResultException {
}
public Engine getRunningEngine() {
return runningEngine;
}
public void setRunningEngine(Engine runningEngine) {
this.runningEngine = runningEngine;
}
}
```
真實調用
```java
public class ExecuteSql {
private static Properties config;
private static EngineDispatcher dispatcher = new EngineDispatcher();
private static String address = "";
public static boolean executeSql() {
// 返回引擎信息和對應的SQL
Map<String, String> engineInfo = dispatcher.dispatch(sql, config);
String engineName = engineInfo.get("engine");
String convertedSql = engineInfo.get("convertedsql");
config.setProperty("comment", engineInfo.get("comment"));
config.setProperty("convertedsql", convertedSql);
if (engineName.equals(EngineDispatcher.NO_ENGINE)) {
LOG.info("引擎選擇失敗,執(zhí)行終止!");
return false;
}
LOG.info("自動路由選擇引擎: " + engineName);
dispatcher.setRunningEngine(dispatcher.engines.get(engineName));
//這里是真實的調用
return dispatcher.getRunningEngine().run(dispatcher, convertedSql, config);
}
}
```
參考連接: