2.2.2 模板方法模式

    回顾上述代码,我们到底做了什么?父类中的方法celebrateSpringFestival()是我们的一个模板方法(也叫框架方法),它把回家过年分为三步,其中方法travel()是抽象部分,用于子类实现不同客户化逻辑,我们所使用的正是模板方法模式。GoF给出的模板方法模式的定义如下。

    Define the skeleton of an algorithm in an operation, deferring some steps to subclasses. Template Method lets subclasses redefine certain steps of an algorithm without changing the algorithm's structure.

    定义在一个操作中的一个算法框架,把一些步骤推迟到子类去实现。模板方法模式让子类不需要改变算法结构而重新定义特定的算法步骤。

    也就是说模板方法定义了一系列算法步骤,子类可以去实现/覆盖其中某些步骤,但不能改变这些步骤的执行顺序,模板方法有如下功能。

    能够解决代码冗余问题。在此例中,PassengerByAir、PassengerByTrain和PassengerByCoach等类没有必要去实现subscribeTicket()和celebrate()方法。

    把某些算法步骤延迟到子类,子类可以根据不同情况改变/实现这些方法,而子类的新方法不会引起既有父类的功能变化,例如上例中,我们加入PassengerByAir类,它没有影响HappyPeople类。

    易于扩展。我们通过创建新类,实现可定制化的方法就可以扩展功能。此例中,如果有人坐船回家,加入PassengerByBoat类实现父类的travel()抽象方法即可。

    父类提供了算法的框架,控制方法执行流程,而子类不能改变算法流程,子类方法的调用由父类模板方法决定。执行步骤的顺序有时候非常重要,我们在容器加载和初始化资源时,为避免子类执行错误的顺序,经常使用该模式限定子类方法的调用次序。比如此例中,你不能先回家再买票,也不能先庆祝再回家。

    父类可以把那些重要的、不允许改变的方法屏蔽掉,不让子类去覆写(Override/Overwrite)它们,比如在Java语言中,我们声明这些方法为final或者private即可。

    此例中,我们使用protected final关键字修饰celebrate()和subscribeTicket()方法,这样,子类不能覆写(Overwrite)它们;如果为了防止子类完全覆写celebrateSpringFestival()方法,也可以修饰此方法为final的。