设计模式--模版模式
-
涉及到的知识点
-
Java 的接口和抽象类信息
一个类只可以继承另外一个类,但是可以实现多个接口
抽象类中可以定义属性,接口中的属性都被 定义为 public final static 修饰,属于常量
接口和抽象类都可以实现逻辑,接口通过default 修饰方法实现,抽象类通过普通方法实现
接口中的方法默认修饰为 public abstract
抽象方法在子类中都必须被重写
-
---别人补充的
抽象类没有办法被new,因为有抽象方法没有实现。接口相同
抽象类默认修饰为public ,可以使用public 和 protected 修饰
接口中不能含有静态代码块和静态方法,但是抽象类中可以含有
设计层面上,接口是对行为的抽象,而抽象类是对统一类型东西的抽象(鸟和飞机的例子)
抽象类作为很多子类的父类,是一种模版的设计。接口定义的是行为规范,是辐射类设计。接口作用是可以在不修改子类的情况下,为子类添加公共的方法,接口是不能实现的。
-
门和告警功能的实现的例子。门的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 修饰的方法,作用和抽象类中的普通方法类似。在不修改子类实现情况下,修改接口的公共方法。算是对历史遗留问题的解决方案。
调用方式,可以像调用普通父类方法一样调用接口中default 修饰的方法
- 多个接口中包含default 修饰相同的方法,则实现接口的类必须重写接口中default修饰的方法
- 如果接口中和父类都含有相同的方法,接口中用default修饰,那么子类调用时候会使用父类中的方法
- 接口默认方法增强了Java 8中的Collections API以支持lambda表达式。
接口中使用静态代码块 static
- 无法被复写
- 调用方式为 接口名.function()
-
泛型 T E ?的区别
在JDK 5 中引入的泛型,泛型的本质是参数化类型。如果不使用泛型,使用Object 将会使参数任意化,任意化的结果是对于类型转换在运行时才可以确定,可能会出现类型转化失败。另外代码里面需要强制类型转化。
T E K V ? 都是通配符,一般情况下有如下语义
- T type E element K key v value
- ? 表示不确定java类型
泛型和不确定类型的通配符需要单独说明
for(;;) 的用法 没有搜索到
-
Obejct...的意思
Java 中的可变参数
- 可变参数必须放在最后,所以一个方法只能有一个可变参数
- 可变参数的本质是数组
- 可变参数本身是提供的语法糖,这个在python中使用非常多,在不确定参数长度时候可以使用
-
protected的含义
作为java中权限访问控制的一个修饰符
访问控制的边界为
- public 所有类都可以使用
- producted 子类可以访问prodected修饰的变量或者方法,或者相同包下的类可以调用
- Default 基于同包的访问
- private 只有当前类可以使用变量或者方法
-
模版方法的功能
定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
个人理解: 模版方法是将一些action,通用的部分写在父类的抽象类中实现,各个子类独立的部分在各个子类中实现。减少不必要的重复又解耦的方案
如果我们将编程增加可拓展性和调用修改复杂度两个方面看,模版模式就是对这些目的的实现。
-
例子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 );方法作为一个抽象方法,而是定义方法,同时抛出异常。这样做的效果是 * 在子类中,如果不重写这个方法,编译器没有异常,但是在调用父类这个方法时候会抛出异常。 * 所以,子类必须重写这个方法,实现自己的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++; } }
-
第二个例子是工作中遇到的,需求是做一个混合引擎调度器,在执行SQL时候,可以选择多种引擎去执行,在优先选择的引擎执行失败时候使用其他引擎降级。
使用模版方法的思路是: 每一个引擎执行代码的逻辑大致是相同的,比如提前清理文件,校验执行结果等。但是具体的执行,每个引擎各部想不。
-
引擎的抽象父类
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引擎的实现
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) { //具体的实现逻辑 return false; } @Override protected void cancel(Properties config) { } }
Presto的实现
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){
// 分发和解析sql规则,确定执行的引擎
}
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("引擎选择失败,执行终止!");
return false;
}
LOG.info("自动路由选择引擎: " + engineName);
dispatcher.setRunningEngine(dispatcher.engines.get(engineName));
//这里是真实的调用
return dispatcher.getRunningEngine().run(dispatcher, convertedSql, config);
}
}
```
参考连接: