模板方法模式适用于以下场景:
1、当多个算法或类实现类似或相同逻辑的时候
2、在子类中实现算法有助于减少重复代码的时候
3、可以让子类利用覆盖实现行为来定义多个算法的时候
模板方法模式的主要意图:
1、使用基本操作定义算法的框架;
2、重新定义子类的某些操作,而无需修改算法的结构;
3、实现代码重用并避免重复工作
4、利用通用接口或实现
模板方法模式使用以下术语——AbstractClass、ConcreteClass、TemplateMethod和Client;
1、AbstractClass:声明一个定义算法步骤的接口
2、ConcreteClass:定义子类特定的步骤
3、template_method():通过调用步骤方法来定义算法
以下是一个代码示例,展示了该模式中所有参与者的关系:
from abc import ABCMeta, abstractmethod
class AbstractClass(metaclass=ABCMeta):
def __init__(self):
pass
@abstractmethod
def operation1(self):
pass
@abstractmethod
def operation2(self):
pass
def template_method(self):
print("定义算法,operation1与operation2")
self.operation2()
self.operation1()
class ConcreteClass(AbstractClass):
def operation1(self):
print("My Concrete Operation1")
def operation2(self):
print("Operation2 remains same")
class Client(object):
def main(self):
self.concreate = ConcreteClass()
self.concreate.template_method()
client = Client()
client.main()
现在假设我们为IOS设备开发自己的交叉编译器并运行程序。
首先开发一个抽象类(编译器),来定义编译器的算法。编译器执行的操作是收集由程序语言编写的源代码,然后编译成目标代码(二进制格式)。我们将这些步骤定义为collect_source()和compile_to_object()抽象方法,同时还定义了负责执行程序的run()方法。该算法是由compile_and_run()方法来定义的,它通过内部调用collect_source()、compile_to_object()和run()方法来定义编译器的算法。然后让具体类IOSCompiler实现抽象方法,在IOS设备上编译并运行Swift代码。
下面的Python代码用于实现模板方法设计模式:
from abc import ABCMeta, abstractmethod
class Compiler(metaclass=ABCMeta):
@abstractmethod
def collect_source(self):
pass
@abstractmethod
def compile_to_object(self):
pass
@abstractmethod
def run(self):
pass
def compile_and_run(self):
self.collect_source()
self.compile_to_object()
self.run()
class IOSCompiler(Compiler):
def collect_source(self):
print("收集源码")
def compile_to_object(self):
print("编译源码")
def run(self):
print("运行程序")
ios = IOSCompiler()
ios.compile_and_run()
想象一个旅行社的例子,它们定义了各种旅行线路,并提供度假套装行程。一个行程套餐本质上是你作为客户允诺的一次旅行。旅行还涉及一些详细信息,如游览的地点、交通方式和旅行有关的其他因素。当然,同样的行程可以根据客户的需求进行不同的定制。这种情况下,模板方法模式很适合;
设计注意事项:
1、对于上述场景,我们应该创建一个定义旅行的AbstractClass接口
2、旅行应包含多个抽象方法,定义所使用的交通方式,在第一天、第二天和第三天所游览的地点(假设这是一个为期三天的旅行),并定义回程
3、模板方法itinerary()将实际定义该旅行的行程
4、我们应该定义ConcreteClasses,以帮助我们根据客户的需要对旅行进行相应的定制
我们从抽象类开始,即Trip:
1、抽象对象由Trip类表示。他是一个接口,定义了不同日子使用的交通方式和参观的地点等细节
2、set_transport是一个抽象方法,它由ConcreteClass实现,作用是设置交通的方式;
3、day1()、day2()、day3()抽象方法定义了特定日期所参观的地点
4、itierary()模板方法创建完整的行程。旅行的序列为,首先定义交通模式,然后每天参观的地点,以及return_home
代码如下:
from abc import ABCMeta, abstractmethod
class Trip(metaclass=ABCMeta):
@abstractmethod
def set_transport(self):
pass
@abstractmethod
def day1(self):
pass
@abstractmethod
def day2(self):
pass
@abstractmethod
def day3(self):
pass
@abstractmethod
def return_home(self):
pass
def itinerary(self):
self.set_transport()
self.day1()
self.day2()
self.day3()
self.return_home()
我们还要开发了代表具体类的某些类:
1、在本例中,我们主要有两个实现Trip接口的具体类:VeniceTrip和MaldivesTrip;
2、这两个具体类代表游客根据他们的选择和兴趣所进行的两次不同的旅行
3、VeniceTrip和MaldivesTrip都实现了set_transport()、day1()、day2()、day3()和return_home()
class VeniceTrip(Trip):
def set_transport(self):
print("乘船从大运河走")
def day1(self):
print("叹息桥")
def day2(self):
print("圣马可广场")
def day3(self):
print("凤凰歌剧院")
def return_home(self):
print("威尼斯返回")
class MaldivesTrip(Trip):
def set_transport(self):
print("飞机")
def day1(self):
print("日光浴")
def day2(self):
print("潜水")
def day3(self):
print("欣赏海洋生物")
def return_home(self):
print("马尔代夫返回")
现在,让我们来考察一些旅行社和希望度过一个愉快的游客:
1、TravelAgency类代表该示例中的Client对象
2、它定义了arrange_trip()方法,让客户选择历史旅行或者海滩旅行
3、根据旅游者的选择,相应的类将被实例化
4、这个对象然后调用itinerary()模板方法,并根据客户的选择为游客安排相应的旅行
class TravelAgency(object):
def arrange_trip(self):
choice = input("historical or beach")
if choice == "historical":
self.trip = VeniceTrip()
self.trip.itinerary()
if choice == "beach":
self.trip = MaldivesTrip()
self.trip.itinerary()
TravelAgency().arrange_trip()
钩子是在抽象类中声明的方法,它通常被赋予一个默认实现。钩子背后的思想是为子类提供按需钩取算法的能力。但是它并不强制子类使用钩子,它可以很容易地忽略这一点
好莱坞原则是一种设计原则,即不要给我们打电话,我们会打给你。它来自好莱坞哲学,如果有适合演员的角色,影棚会给演员打电话。
在面向对象的世界中,我们允许低层组件使用好莱坞原则将自己挂入系统中,然而,高层组件确定底层系统的使用方式,以及何时需要它们。换句话说,高层组件对待底层组件的方式也是不要给我们打电话,我们会打电话给你。
这涉及模板方法模式,在这个意义上,它是高级抽象类,它安排定义算法的步骤。根据算法的工作方式,通过调用底层类来定义各个步骤的具体实现。
优点:
1、没有代码重复
2、由于模板方法模式使用继承而不是合成,因此能够对代码进行重用。所以,只有为数不多的几个方法需要重写
3、灵活性允许子类决定如何实现算法中的步骤
缺点:
1、调试和理解模板方法模式中的流程程序序列有时会令人困惑。你最终实现的方法可能是一个不应该实现的方法,或根本没有实现抽象的方法。文档和严格的错误处理必须由程序员完成
2、模板框架的维护可能是一个问题,因为任何层次的变更都可能对实现造成干扰。因此使用模板方法模式可能会使维护变的异常痛苦