概述
什么是模板
模板的原意是指带有镂空文字的薄薄的塑料板。只要用笔在模板的镂空处进行临摹,即使是手写也能写出整齐的文字。虽然只要看到这些镂空的洞,我们就可以知道能写出哪些文字,但是具体能写出的问题是什么感觉则依赖于所用的笔。如果使用签字笔来临摹,则可以写出签字似的文字;如果使用铅笔来临摹,则可以写出签字笔;而如果是用彩色笔临摹,则可以写出彩色的字。但是无论使用什么笔,文字的形状都会与模板上镂空处的形状一致。
什么是模板模式
模板模式是带有模板功能的模式,组成模板的方法被定义在父类中。由于这些方法是抽象方法,所以只查看父类的代码是无法知道这些方法最终会进行何种具体处理的,唯一能知道的就是父类是如何调用这些方法的。
实现上述这些抽象方法的是子类。在子类中实现了抽象方法也就决定了具体的处理。也就是说,只要在不同的子类中实现不同的具体处理,当父类的模板方法被调用时程序行为也会不同。但是,不论子类中的具体实现如何,处理的流程都会按照父类中所定义的那样进行。
像这样在父类中处理流程的框架,在子类中实现具体处理的模式就被称为模板模式。
在程序开发过程中,经常会遇到这种情况:某个方法要实现算法需要多个步骤,但其中有一些步骤是固定不变的,而另一些步骤则是不固定的。为了提高代码的可扩展性和可维护性,模板方法模式在这种场景下就派上了用场。
在模板方法中,一个算法可以分为多个步骤,这些步骤的执行次序在一个被称为模板方法中定义,而算法的每个步骤都对应着一个方法,这些方法被称为基本方法。模板方法按照它定义的顺序依次调用多个基本方法,从而实现整个算法流程。在模板方法模式中,会将模板方法的实现以及那些固定不变的基本方法的实现放到父类中,而那些不固定的基本方法在父类中只是抽象方法,其真正的实现代码会被延迟到子类中完成。
结构
下面来看模板方法模式的结构,如下图,其中template()方法是模板方法,operation3()是固定不变的基本方法,而operation1、operation2、operation4都是固定不变的基本方法,所以在AbstractClass中都定义为抽象方法,而ConcreteClass1和ConcreteClass2这两个子类需要实现这些方法。
优点
通过上面的描述可知,模板方法模式可以将模板方法以及固定不变的基本方法统一封装到父类中,而将变化的部分封装到子类中实现,这样就由父类控制着整个算法的流程,而子类实现算法的某些细节,实现了这两方面的解耦。当需要修改算法的行为时,开发人员可以通过添加子类的方式实现,这符合开放-封闭原则。
模板方法模式不仅可以复用已有的代码,还可以充分利用了面向对象的多态性,系统可以在运行时选择一种具体子类实现完整的算法,这就提高了系统的灵活性和可扩展性。
模板方法模式与其他设计模式一样,都会增加系统的抽象程度。另外,模板方法模式在修改算法实现细节时,会增加类的个数,也会增加系统的复杂性。
扩展思路的要点
可以使逻辑处理通用化
使用模板模式究竟能带来什么好处呢?这里,它的优点是由于在父类的模板方法中编写了算法,因此无须再每个子类中再编写算法。
类的层次与抽象类
父类对子类的要求
我们在理解类的层次时,通常是站在子类的角度进行思考的。也就是说,很容易着眼于以下几点。
- 在子类中可以使用父类中定义的方法。
- 可以通过在子类中增加方法以实现新的功能。
- 在子类中重写父类的方法可以改变程序的性能。
现在,我们稍微改变一下立场,站在父类的角度进行思考。在父类中,我们声明了抽象方法,而将该方法的实现交给了子类。换言之,就程序而言,声明抽象方法是希望达到以下目的。
- 期待子类去实现抽象方法
- 要求子类去实现抽象方法
也就是说,子类具有实现在父类中所声明的抽象方法的责任。因此,这种责任被称为子类责任。
抽象类的意义
对于抽象类,我们是无法生成其实例的。在抽象方法中并没有编写具体的实现,所以我们无法知道在抽象方法中到底进行了什么样的处理。但是我们可以决定抽象方法的名字,然后通过调用使用了抽象方法的模板方法去编写处理。虽然具体的处理内容是由子类决定的,不过在抽象类阶段确定处理的流程非常重要。