设计模式--模版模式

设计模式--模版模式

  1. 涉及到的知识点

    • 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 只有当前类可以使用变量或者方法
  1. 模版方法的功能

    定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。

    个人理解: 模版方法是将一些action,通用的部分写在父类的抽象类中实现,各个子类独立的部分在各个子类中实现。减少不必要的重复又解耦的方案

    如果我们将编程增加可拓展性和调用修改复杂度两个方面看,模版模式就是对这些目的的实现。

  2. 例子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++;
        }
    }
    
  3. 第二个例子是工作中遇到的,需求是做一个混合引擎调度器,在执行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);
   }
   
 }
 ```

参考连接:

https://segmentfault.com/a/1190000038823160

https://blog.csdn.net/wf13265/article/details/79363522

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,186评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,858评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,620评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,888评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,009评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,149评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,204评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,956评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,385评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,698评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,863评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,544评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,185评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,899评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,141评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,684评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,750评论 2 351

推荐阅读更多精彩内容