Template Method模式----将具体处理交给子类

1. Template Method 模式
   1)、什么是模板
   2)、什么是Template Method 模式
       》》 Template Method 模式是带有模板功能的模式,组成模板的方法被定义在父类中。
           由于这些方法是抽象方法,所以只查看父类的代码是无法知道这些方法最终会进行
           何种具体处理的,唯一能知道的就是父类是如何调用这些方法的。
       》》实现上述这些抽象方法的是子类。在子类中实现了抽象方法也就是决定了具体的处理。
          也就是说,只要在不同的子类中实现不同的具体处理,当父类的模板方法被调用时
          程序行为也会不同。但是,不论子类中的具体实现如何,处理的流程都会按照父类中
          所定义的那样进行。
       》》像这样在父类中定义处理流程的框架,在子类中实现具体处理的模式称为Template Method
          模式。
2. 示例程序
       》》 这里的示例程序是将一段字符和字符串循环显示 5 次的简单程序。
       》》 在示例程序中出现 AbstractDisplay 、 CharDisplay 、 StringDisplay 、Main 这四个类。
       》》 在 AbstractDisplay 类中定义了 display 方法,而且在该方法中依次调用了 open 、
             print 、 close 这3个方法。虽然这 3 个方法已经在 AbstractDisplay 中被声明了,
             但是没有实体的抽象方法。这里,调用抽象方法的 display 方法就是模板方法。
       》》 而实际上实现了 open 、 print 、 close 这3个抽象方法的是 AbstractDisplay  的子类
            CharDisplay 类和 StringDisplay 类。
       》》 Main 类是用于测试程序行为的类。
       》》 类的一览表:

     

      》》示例程序的类图:

     

     

 1)、AbstractDisplay 类
         package module.template_method;

/**
 * 抽象类 AbstractDisplay
 */
public abstract class AbstractDisplay {
    public abstract void open();    // 交给子类去实现的抽象方法 open()
    public abstract void print();   // 交给子类去实现的抽象方法 print()
    public abstract void close();   // 交给子类去实现的抽象方法 close()

    /**
     * 本抽象类中实现的 display() 方法
     *
     */
    public final void display(){
        open();                       // 首先打开
        for(int i = 0 ; i <5 ; i++){ // 循环调用 5 次print()...
            print();
        }
        close();                      // 最后关闭。这就是 display 方法所实现的功能
    }
}

   2)、CharDisplay 类
        package module.template_method;

/**
 * 实现抽象类 AbstractDisplay 的子类CharDisplay
 */
public class CharDisplay extends AbstractDisplay{
    // 需要显示的字符
    private char ch;
    // 构造函数中接收的字符保存在字段中
    public CharDisplay(char ch){
        this.ch = ch;
    }

    /**
     * 重写父类的抽象方法 open()
     * 显示开始字符"<<"
      * @return
     */

    public void open(){
        System.out.print("<<");

    }

    /**
     * 重写父类的抽象方法 print()
     * 显示保存在字段 ch 中的字符
     */
    public void print(){
        System.out.print(ch);

    }
    /**
     * 重写父类的抽象方法 close ()
     * 显示结束字符">>"
     * @return
     */
    public void close(){
        System.out.println(">>");
    }
}

   3)、StringDisplay 类
        package module.template_method;

/**
 *  实现抽象类 AbstractDisplay 的子类 StringDisplay
 */

public class StringDisplay extends AbstractDisplay{
    private  String string;             // 需要显示的字符串
    private  int width;                 // 以字节为单位计算出的字符串长度

    /**
     * 构造函数中接收的字符串被保存在字段中,同时将字符串的长度也保存在字段中,以供后面使用
     * @param string
     *
     */
    public StringDisplay(String string ){
        this.string = string;
        this.width = string.getBytes().length;
    }

    /**
     * 重写父类中的抽象方法 open()
     */
    public void open(){
        printLine();                  // 调用该类的 printLine() 方法划线
    }

    /**
     * 重写父类中的抽象方法  print()
     */
    public void print(){
         System.out.println("|"+string+"|");   // 给保存在字段中的字符串前后分别加上“|”,并显示出来
    }

    /**
     * 重写父类中的抽象方法 close()
     */
    public void close(){
        printLine();
    }

    public void printLine(){
        System.out.print("+");            // 显示表示方框的角的“+”
        for(int i = 0 ; i < width ; i++){
            System.out.print("-");        // 显示 width 个“-”,组成方框的边框
        }
        System.out.println("+");            // 显示表示方框的角的“+”
    }
}

   4)、Main 类
        package module.template_method;

public class Main {
    public static void main(String[] args){
        AbstractDisplay d1 = new CharDisplay('H');
        AbstractDisplay d2 = new StringDisplay("Hello,world");
        AbstractDisplay d3 = new StringDisplay("你好,世界。");

        d1.display();
        d2.display();
        d3.display();

    }
}
   5)、上面程序的执行效果如下:

    

3. Template Method 模式中的登场角色
    1)、AbstractClass (抽象类)
          AbstractClass 不仅负责实现模板方法,还负责声明在模板方法中所使用到的抽象方法。这些抽象方法由子类
       ConcreteClass 角色负责实现。在示例程序中,由 AbstractDisplay  类扮演此角色。
    2)、ConcreteClass (具体类)
         该角色负责具体实现 AbstractClass 角色中定义的抽象方法。这里实现的方法将会在 AbstractClass 角色的
         模板方法中被调用。在示例程序中,由 CharDisplay 类和 StringDisplay 类扮演此角色。
    3)、 Template Method 模式的类图

  

4. 扩展思路的要点
   1)、可以使用逻辑处理通用化
        Template Method 模式 究竟能带来什么好处呢?这里,它的优点是由于在父类的模板方法中编写了算法,因此
      无需在每个子类中再编写算法。
        如果是使用 Template Method 模式进行编程,当我们在模板方法中发现 bug 时,只需要修改模板方法即可解决问题。
   2)、父类与子类之间的协作
        在 Template Method 模式中,父类和子类是紧密联系、共同工作的。因此,在子类中实现父类中声明的抽象方法
      时,必须要理解这些抽象方法被调用的时机。在看不到源代码的情况下,想要编写出子类是非常困难的。
   3)、父类与子类的一致性
        在示例程序中,不论是 CharDisplay 实例还是 StringDisplay 的实例,都是先保存在 AbstractDisplay 类型的
       变量中,然后再来调用 display 的方法。
        使用父类类型的变量保存子类实例的优点是,即使没有用  instanceof 等指定子类的种类,程序也能正常工作。
       无论在父类类型的变量中保存哪个子类的实例,程序都可以正常工作,这种原则称为里氏替换原则(LSP) 。当然
        LSP  并非仅限于 Template Method 模式中,它是通用的继承原则。
5. 相关的设计模式

 1)、Factory Method 模式
         Factory Method 模式是将 Template Method 模式 用于生成实例的一个典型例子。
    2)、Stategy 模式
           在Template Method 模式中,可以使用继承改变程序的行为。这是因为Template Method 模式 在父类中定义程序
         行为的框架,在子类中决定具体的处理。
           与此相对的是Stategy 模式,它可以使用委托改变程序的行为。与 在Template Method 模式中改变程序行为不同
         的是,Stategy 模式用于替换整个算法。
6. 延伸阅读:类的层次与抽象类

1)、父类对子类的要求
          》》 我们在理解类的层次时,通常是站在子类的角度进行思考的。也就是说,很容易着眼于以下几点:
               *** 在子类中可以使用父类中定义的方法
               *** 可以通过在子类中增加方法以实现新的功能
               *** 在子类中重写父类的方法可以改变程序的行为
           》》让我们稍微改变一下立场,站在父类的角度进行思考。在父类中,我们声明了抽象方法,而将该方法的
               实现交给了子类。换言之,就程序而言,声明抽象方法是希望达到以下目的:
               *** 期待子类去实现抽象方法
               *** 要求子类去实现抽象方法

                也就是说,子类具有实现在父类中所声明的抽象方法的责任。因此,这种责任被称为“子类责任”。
   2)、抽象类的意义
            》》对于抽象类,我们是无法生成其实例的。
            》》由于在抽象方法中并没有编写具体的实现,所以我们无法知道在抽象方法中到底进行了什么样的处理。
               但是我们可以决定抽象方法的名字,然后通过调用使用了抽象方法的模板方法去编写处理。虽然具体
               的处理内容是由子类决定的,不过在抽象类阶段确定处理的流程非常重要。
   3)、父类与子类之间的协作
             》》 父类与子类的相互协作支撑起了整个程序。虽然将更多方法的实现放在父类中会让子类变得更轻松,但是
               同时也降低了子类的灵活性;反之,如果父类中实现的方法过少了,子类就会变得臃肿不堪,而且还是
               导致各子类间的代码出现重复。
              》》 在 Template Method 模式中,处理的流程被定义在父类中,而具体的处理则交给了子类。但是对于
                “如何划分处理的级别,哪些处理需要由父类完成,哪些处理需要交给子类负责”并没有定式,这些都
                 需要由负责程序设计的开发人员来决定。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小达人Fighting

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值