目录
在面向对象里面的封装
特性中,我们可以利用工厂模式封装对象创建
,利用命令模式封装方法调用(命令请求)
,用适配器模式和外观模式封装复杂接口和复杂子系统
。现在讨论用模板方法模式封装算法
。
1 模板方法模式
代模板方法模式
在一个方法中
定义一个算法的骨架
,而将算法的一些步骤延迟到子类去实现
。模板方法使得子类在不改变算法结构
的情况下,重新定义算法的某些步骤
。
- 这个模式
创建一个算法的模板
,而模板就是一个抽象类的一个方法
。 这个方法将算法定义成一组步骤
。其中的步骤可以是抽象的
,由具体子类来实现各个子类的该步骤的不同实现
;也可以是一个钩子
,由具体子类选择继承抽象类的该步骤
或者重新覆盖该步骤
。
2 模板方法模式的UML类图
AbstractClass
:抽象类。定义了模板方法
和执行算法的一组方法步骤
,该步骤可以是抽象的
,由子类来实现
;也可以是一个钩子方法
,由子类选择性地钩取抽象类该步骤的实现
或重新覆盖该钩子方法
。templateMethod
:模板方法。封装了执行算法的一种步骤,这些步骤必须在抽象类中有声明,当不一定实现。这样就使得模板方法本身和具体的步骤实现解耦
,因为具体的步骤实现在子类中完成
。ConcreteClass
:具体子类。实现了抽象类中定义的的抽象方法
,选择性地继承抽象类钩子的实现
或重新覆盖该钩子方法
。
3 模板方法模式的一个例子:要caffe还是tea
制作caffe和tea的步骤大同小异,都是先将水煮开,用热水泡caffe或者tea,然后将饮料倒进杯子,最后加一些适当的调料。只是第二步用热水泡caffe或者tea的方式可能不一样,第四步加的调料可能不同。
4 好莱坞准则和模板方法模式
4.1 好莱坞准则
别调用(打电话给)我们,我们会调用(打电话给)你。
- 防止“依赖腐败”:底层组件依赖高层组件,而高层组件又依赖底层组件。
- 在好莱坞准则下,底层组件将自己挂钩到系统上,高层组件决定什么时候调用底层组件。高层组件对底层组件的方式是“别调用我们,我们会调用了你”。这在计算机的分层框架中经常使用。
- 工厂方法模式,观察者模式都利用了好莱坞准则。
4.2 好莱坞准则与模板方法
5 模板方法模式的一个Python实现例子
5.1 例子解释
假如你要通过旅行社去旅游,旅行社提供了不同的套餐让你选择。不同的套餐中都定制出行方式,每天的行程安排。具体的出行方式和行程安排由具体套餐决定。
5.2 UML类图
5.3 代码实现
- 抽象类Trip
抽象类,为具体子类提供了一个接口
,可以定义了具体方法、抽象方法和钩子
抽象方法setTransport()、day1()、day2()、day3()、returnHome()由具体子类实现
模板方法itinerary()
封装的算法步骤依赖抽象方法setTransport()、day1()、day2()、day3()、returnHome()
from abc import ABCMeta, abstractmethod
class Trip(metaclass=ABCMeta):
'''
抽象类,即为具体子类提供了一个接口
'''
@abstractmethod
def setTransport(self):
'''抽象方法,由具体子类来实现,调用此方法设置出行的交通工具'''
pass
@abstractmethod
def day1(self):
'''抽象方法,由具体子类来实现,调用此方法设置出行第一天浏览地点'''
pass
@abstractmethod
def day2(self):
'''抽象方法,由具体子类来实现,调用此方法设置出行第二天浏览地点'''
pass
@abstractmethod
def day3(self):
'''抽象方法,由具体子类来实现,调用此方法设置出行第三天浏览地点'''
pass
@abstractmethod
def returnHome(self):
'''抽象方法,由具体子类来实现,调用此方法设置返程需要做的事'''
pass
def itinerary(self):
'''模板方法,此方法封装了完整的行程算法,具体的方法由子类实现'''
self.setTransport()
self.day1()
self.day2()
self.day3()
self.returnHome()
- 具体类VeniceTrip和MaldivesTrip
- 各个
具体类实现了抽象类中的抽象方法
,根据具体类的不同,采用不同的旅行套餐
- 各个
class VeniceTrip(Trip):
'''
具体子类,实现抽象类的抽象方法
'''
def setTransport(self):
print("Take a boat and find your way in the Grand Canal")
def day1(self):
print("Visit St Mark's Basilica in St Mark's Square")
def day2(self):
print("Appreciate Doge's Palace")
def day3(self):
print("Enjoy the food near the Rialto Bridge")
def returnHome(self):
print("Get souvenirs for friends and get back")
class MaldivesTrip(Trip):
'''
具体子类,实现抽象类的抽象方法
'''
def setTransport(self):
print("On foot, on any island, Wow!")
def day1(self):
print("Enjoy the marine life of Banana Reef")
def day2(self):
print("Go for the water sports and snorkelling")
def day3(self):
print("Relax on the beach and enjoy the sun")
def returnHome(self):
print("Dont feel like leaving the beach")
- 客户类TravelAgency
arrange_trip()方法
根据客户的输入选择历史旅行还是沙滩旅行,再根据客户的选择
,实例化相应的类
,然后调用相应类的模板方法itinerary()
class TravelAgency:
'''
Client类
'''
def arrange_trip(self):
'''此方法判断让客户选择历史旅行还是沙滩旅行,然后实例化一个具体子类对象,调用封装了算法的模板方法'''
choice = input("What kind of place you'd like to go historical or to a beach? ")
if choice == 'historical':
self.trip = VeniceTrip()
self.trip.itinerary()
elif choice == 'beach':
self.trip = MaldivesTrip()
self.trip.itinerary()
- 客户端
- 客户端
实例化一个客户对象
,然后调用客户对象的arrange_trip()方法
。 - 在arrange_trip()方法中根据客户的输入选择历史旅行还是沙滩旅行,再
根据客户的选择
,实例化相应的类
,然后调用相应类的模板方法itinerary()
- 客户端
if __name__ == '__main__':
travelAgency = TravelAgency()
travelAgency.arrange_trip()
5.4 输出结果
总结:
模板方法定义了算法步骤接口
,这些步骤的实现延迟到子类中
,将模板方法与具体子类的方法的实现解耦
,是一种最重要的代码复用技术。- 抽象类可以定义了
具体方法、抽象方法和钩子
钩子
方法在抽象类中不做事或者只做默认的事
,子类可以选择要不要覆盖它
- 为了
防止子类改变模板方法中的算法
,可以将模板方法声明为final
- 好莱坞准则:将决策权放在高级层模块中,在高级层模块中调用底层模块
-策略模式
使用组合
封装整个算法及各个组件框架,而模板方法模式
使用继承
封装算法步骤 - 工厂方法是一种特殊的模板方法,用于封装对象创建。