@rfish
2015-07-22T12:54:56.000000Z
字数 6033
阅读 1850
实验楼
策略模式将各种操作(算法)进行封装,并使它们之间可以互换,动态改变对象的操作方式(算法)
对比以下两种代码,方式。
普通:
# -*- coding: utf-8 -*-class Question(object):"""问题对象,没有使用策略模式之前的作法"""def __init__(self, admin=True):self._admin = admindef 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())
上面方法的缺陷是我们在增加功能的时候,需要进入到这个类里面去修改,而当功能比较多的时候,修改起来势必将特别复杂。
使用策略模式:
# -*- coding: utf-8 -*-import abcclass AbsShow(object):"""抽象显示对象"""__metaclass__ = abc.ABCMeta@abc.abstractmethoddef show(self):passclass AdminShow(AbsShow):"""管理员的显示操作"""def show(self):return "show with admin"class UserShow(AbsShow):"""普通用户的显示操作"""def show(self):return "show with user"class Question(object):"""问题对象,使用策略模式之后的作法"""def __init__(self, show_obj):self.show_obj = show_objdef show(self):return self.show_obj.show()if __name__ == '__main__':q = Question(show_obj=AdminShow())print(q.show())# 替换原来的显示对象,体现了策略模式的互换行为q.show_obj = UserShow()print(q.show())
如上代码,我们使
操作和使用这些操作的客户端完全分开了
操作:两个继承接口的子类,AdminShowUserShow
使用操作的客户端:Question
好处:当我们在添加方法的时候,只需要增加对应的操作,而不修改客户端Question的代码。因为我们在实例化Question的时候传入的操作名称,会直接去寻找我们现有的操作。
观察者模式,就是当一个对象发生变化时,观察者能及时得到通知并更新。
# -*- coding: utf-8 -*-import abcclass Subject(object):"""被观察对象的基类"""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:passdef notify(self):"""通知所有观察者,执行观察者的更新方法"""for observer in self._observers:observer.update(self)class Course(Subject):"""课程对象,被观察的对象"""def __init__(self):super(Course, self).__init__()self._message = None@propertydef message(self):"""message 是一个属性"""return self._message@message.setterdef message(self, msg):"""message 属性设置器"""self._message = msgself.notify()class Observer(object):"""观察者抽象类"""__metaclass__ = abc.ABCMeta@abc.abstractmethoddef update(self, subject):passclass UserObserver(Observer):"""用户观察者"""def update(self, subject):print("User observer: %s" % subject.message)class OrgObserver(Observer):"""机构观察者"""def update(self, subject):print("Organization observer: %s" % subject.message)if __name__ == '__main__':# 初始化一个用户观察者user = UserObserver()# 初始化一个机构观察者org = OrgObserver()# 初始化一个课程course = Course()# 注册观察者course.attach(user)course.attach(org)# 设置course.message,这时观察者会收到通知course.message = "two observers"# 注销一个观察者course.detach(user)course.message = "single observer"
- 在上面的示例代码中,先是写了一个
被观察者的基类,把被观察者的共同特性写入基类中。然后再在继承于基类,写了真正需要被观察的类Course。- 然后写了观察者的抽象类
Observer提供一个update接口,在后面的UserObserver和OrgObserver中实现了这个接口。- 在主程序中,实例化两个观察者,user和org和一个被观察者course,并在、course中注册这两个观察者。
注册实际上是将,两个实力对象的名称放入一个列表。当我们每写入一个massage就是执行
self.notify()也就是取出列表里的所有观察者,并执行他们的update方法。
添加消息时,程序的执行顺序:
好处:
该模式的好处,观察者和被观察者在程序逻辑上分开,当需要观察某个类时,只需要,将这个观察者实例化后注册到(添加到)被观察者的观察者列表中,即可。将会提供灵活方便的代码运用。
命令模式就是对命令的
封装。所谓封装命令,就是将一系列操作封装到命令类中,并且命令类只需要对外公开一个执行方法execute,调用此命令的对象只需要执行命令的execute方法就可以完成所有的操作。
# -*- coding: utf-8 -*-import abcclass VmReceiver(object):"""命令接收者,真正执行命令的地方"""def start(self):print("Virtual machine start")def stop(self):print("Virtual machine stop")class Command(object):"""命令抽象类"""__metaclass__ = abc.ABCMeta@abc.abstractmethoddef execute(self):"""命令对象对外只提供 execute 方法"""passclass StartVmCommand(Command):"""开启虚拟机的命令"""def __init__(self, recevier):"""使用一个命令接收者初始化"""self.recevier = recevierdef execute(self):"""真正执行命令的时候命令接收者"""self.recevier.start()class StopVmCommand(Command):"""停止虚拟机的命令"""def __init__(self, recevier):"""使用一个命令接收者初始化"""self.recevier = recevierdef execute(self):"""真正执行命令的时候命令接收者"""self.recevier.stop()class ClientInvoker(object):"""命令调用者"""def __init__(self, command):self.command = commanddef do(self):self.command.execute()if __name__ == '__main__':recevier = VmReceiver()start_command = StartVmCommand(recevier)# 命令调用者同时也是客户端,通过命令实例也执行真正的操作client = ClientInvoker(start_command)client.do()# 能改变命令接收者,执行不同的操作stop_command = StopVmCommand(recevier)client.command = stop_commandclient.do()
- 声明一个命令执行者的类
VmReceiver,里面包含所有的命令.- 再实例两个命令类,用来传递命令(传入执行者类)
- 再
实例化命令调用者ClientInvoker时传入相应的操作(such asStopVmCommand)名称。
然后会找到该名称的类StopVmCommand,执行该类,该类就会去执行者VmReceiver中找对应的方法start执行。好处:
代码更加灵活,增加代码复用。
先定义一个类模板,在这个类中,我们定义了各种操作的顺序(轮毂或者说是骨架),但是并不实现这些操作,这些操作由子类来操作。
# -*- coding: utf-8 -*-import abcclass Fishing(object):"""钓鱼模板基类"""__metaclass__ = abc.ABCMetadef finishing(self):"""钓鱼方法中,确定了要执行哪些操作才能钓鱼"""self.prepare_bait()self.go_to_riverbank()self.find_location()print("start fishing")@abc.abstractmethoddef prepare_bait(self):pass@abc.abstractmethoddef go_to_riverbank(self):pass@abc.abstractmethoddef find_location(self):passclass 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("John: buy bait from JD")def go_to_riverbank(self):"""骑自行车去钓鱼"""print("John: to river by biking")def find_location(self):"""在河边选择钓点"""print("John: select location on the riverbank")if __name__ == '__main__':# John 去钓鱼f = JohnFishing()f.finishing()# Simon 去钓鱼f = SimonFishing()f.finishing()
将应该执行的步骤用
抽象方法放在父类中,并写一个不是抽象方法的方法按顺序执行前面的步骤。(如果是抽象方法在写子类的时候就会复写掉了)。也就是我调用这个方法,即可按顺序执行完所有步骤。
问题1:
请描述策略模式命令模式的不同之处
答:
我觉得,策略模式和命令模式,在前面都是一样的,在策略模式中,把选择操作,和实际操作分开了。而在命令模式,也将选择操作和实际操作分开了。但是有个中间者,它将命令的执行传递给了真正执行的那个类中。
下面是策略模式:
+-----------++---> |AdminShow()|| +-----------++----------+ ||Question()+++----------+ || +-----------++---> |UserShow() |+-----------+选择 执行
下面是命令模式:
+----------------++-> |StartVmCommand()+---+| +----------------+ |+---------------+ | | +------------+|ClientInvoker()+-+ +--> |VmReceiver()|+---------------+ | | +------------+| +----------------+ |+-> |StopVmCommand() +---++----------------+选择 传递 执行
问题2:
在学习命令模式的时候有没有想到任务队列呢?我们将需要执行的命令对象放入队列中,工作线程从队列中获取命令对象,并调用命令的execute方法以完成任务。这不就是命令模式吗?那么问题来了,请使用 Python 线程和队列实现命令模式,在这个例子中,你需要创建命令接收者,命令抽象类,命令具体类,一个用于将命令对象放入队列的线程类和一个从队列中获取命令对象并执行的线程类。提示:使用 Python threading 和 Queue 模块。