本文转载自实验楼课程:
策略模式
现在模拟一个简单的功能:如果是管理员用户,以管理员方式显示某页面,如果不是,那么一普通用户显示页面
使用策略模式之前:
class Question(object):
"""
问题对象,没有使用策略模式之前的作法
"""
def __init__(self, admin=True):
self._admin = admin
def show(self):
"""
根据是否是管理员显示不同的信息
"""
if self._admin is True:
return "show page with admin"
else:
return "show page with user"
if __name__ == '__main__':
q = Question(admin=False)
print(q.show())
缺点
这样实现虽然好理解,然而如果需求增加显示逻辑,比如经理方式显示页面,开发方式显示页面,测试方式显示页面等等,那么就需要修改show()代码,增加if分支。
使用策略模式之后
主要思路是将操作和操作对象解耦。
import abc
class AbsShow(metaclass=abc.ABCMeta):
@abc.abstractmethod
def show(self):
pass
class AdminShow(AbsShow):
def show(self):
return "show page with admin"
class UserShow(AbsShow):
def show(self):
return "show page with user"
class Question():
def __init__(self,show_obj):
self.obj=show_obj
def show(self):
return self.obj.show()
if __name__ == "__main__":
p=Question(AdminShow())
print(p.show())
p.obj=UserShow()
print(p.show())
我们将原来的 Question.show 抽象成了 AbsShow ,这个操作类负责显示信息。然后我们分别基于该抽象类实现管理员显示类 AdminShow 和用户显示类 UserShow ,这样一来我们就使操作和使用这些操作的客户端完全分开了。在最后我们重新实现了 Question ,并且 Question.show 方法直接调用显示对象的显示方法。这样一来我们将 Question 对象和显示方法进行了解耦,增加新的显示方法时,只需要增加新的显示对象就可以了
观察者模式
这里模拟一个关注通知的功能。
import abc
class Subject():
def __init__(self):
self._observers=[]
def attach(self,observer):
if observer not in self._observers:
self._observers.append(observer)
def detach(self,observer):
try:
self._observers.remove(observer)
except ValueError:
pass
def notify(self):
for observer in self._observers:
observer.update(self)
class Course(Subject):
def __init__(self):
super().__init__()
self._message=None
@property
def message(self):
return self._message
@message.setter
def message(self,msg):
self._message=msg
self.notify()
class observer(metaclass=abc.ABCMeta):
@abc.abstractmethod
def update(self,sub):
pass
class UserObserver(observer):
def update(self,sub):
print('userObserver:{}'.format(sub.message))
class OrgObserver(observer):
def update(self,sub):
print('OrgObserver:{}'.format(sub.message))
if __name__ == "__main__":
user=UserObserver()
org=OrgObserver()
course=Course()
course.attach(user)
course.attach(org)
course.message='课程开始时间为11.11'
course.detach(org)
course.message='课程开始时间为12.12'
输出结果:
分析
创建了一个被观察的对象course,并未之添加了两个关注者org和user,当couse的message发生变化时,会调用被观察者course的notify()方法,从而调用观察者的update方法,打印修改的最新信息。整个过程需要建以下对象:
- 被观察对象的基类Subject:拥有添加(attach)、移出(dettch)、通知(notify)的方法,其中通知会调用观察者的update方法并传入course对象
- 被观察对象Course继承Subject,为其添加方法message并加上@property属性注解,让message可以当做属性调用,通过@message.setter注解,调用自身继承的notify方法通知所有关注者。
- 观察对象的基类Observer,利用abc模块定义为抽象类,并且添加一个抽象方法即可:update
- 具体观察对象的实现,继承Observer。实现update方法,这个update方法是传入了被观察对象的,所以可以直接通过传入对象获取新修改的值。
命令模式
import abc
class Recevier():
''' 可以封装很多基础命令 '''
def start(self):
print('vm start...')
def stop(self):
print('vm stop...')
class Commond(metaclass=abc.ABCMeta):
@abc.abstractmethod
def excute(self):
pass
class StartCommond(Commond):
''' 开启虚拟机,这里可以封装多个基础方法,但是实例只有一个 '''
def __init__(self,recevier):
self._recevice=recevier
def excute(self):
self._recevice.start()
class StopCommond(Commond):
''' 关闭虚拟机,这里可以封装多个基础方法,但是实例只有一个 '''
def __init__(self,recevier):
self._recevice=recevier
def excute(self):
self._recevice.stop()
class ClinetInvoker():
def __init__(self,commond):
self._commond=commond
def do(self):
self._commond.excute()
if __name__ == "__main__":
recevier=Recevier()
#开启虚拟机
commond=StartCommond(recevier)
invok=ClinetInvoker(commond)
invok.do()
#关闭虚拟机
invok._commond=StopCommond(recevier)
invok.do()
分析
以上代码中,我们通过启动和停止 Linux 虚拟机的例子实现了命令模式。通过命令模式,使命令调用者ClinetInvoker和命令接收者Recevier之间解耦,前者不必知道后者具体是怎么操作虚拟机的,只需要通过ClientInvoker.do()方法调用Command.execute()方法能完成虚拟机的相关操作。
总的来说,命令模式的封装性很好:每个命令都被封装起来,对于客户端来说,需要什么功能就去调用相应的命令,而无需知道命令具体是怎么执行的。同时命令模式的扩展性很好,在命令模式中,在接收者类中一般会对操作进行最基本的封装,命令类则通过对这些基本的操作进行二次封装,当增加新命令的时候,对命令类的编写一般不是从零开始的,有大量的接收者类可供调用,也有大量的命令类可供调用,代码的复用性很好。
模板方法
模板方法比较简单,就是父类定义好抽象步骤,由子类去实现就好了,不同子类实现步骤不同。但主要流程都是在父类中定义好了的。这里是以钓鱼为例,定义好了三步:
- 准备鱼饵
- 去到目的地
- 选择钓点
import abc
class Fishing(object):
"""
钓鱼模板基类
"""
__metaclass__ = abc.ABCMeta
def finishing(self):
"""
钓鱼方法中,确定了要执行哪些操作才能钓鱼
"""
self.prepare_bait()
self.go_to_riverbank()
self.find_location()
print("start fishing")
@abc.abstractmethod
def prepare_bait(self):
pass
@abc.abstractmethod
def go_to_riverbank(self):
pass
@abc.abstractmethod
def find_location(self):
pass
class JohnFishing(Fishing):
"""
John 也想去钓鱼,它必须实现钓鱼三步骤
"""
def prepare_bait(self):
"""
从淘宝购买鱼饵
"""
print("John: buy bait from Taobao")
def go_to_riverbank(self):
"""
开车去钓鱼
"""
print("John: to river by driving")
def find_location(self):
"""
在岛上选择钓点
"""
print("John: select location on the island")
class SimonFishing(Fishing):
"""
Simon 也想去钓鱼,它也必须实现钓鱼三步骤
"""
def prepare_bait(self):
"""
从京东购买鱼饵
"""
print("Simon: buy bait from JD")
def go_to_riverbank(self):
"""
骑自行车去钓鱼
"""
print("Simon: to river by biking")
def find_location(self):
"""
在河边选择钓点
"""
print("Simon: select location on the riverbank")
if __name__ == '__main__':
# John 去钓鱼
f = JohnFishing()
f.finishing()
# Simon 去钓鱼
f = SimonFishing()
f.finishing()
输出结果: