[关闭]
@rfish 2015-07-22T12:54:56.000000Z 字数 6033 阅读 1676

行为型模式

实验楼


策略模式(Strategy)

策略模式将各种操作(算法)进行封装,并使它们之间可以互换,动态改变对象的操作方式(算法)

对比以下两种代码,方式。

普通:

  1. # -*- coding: utf-8 -*-
  2. class Question(object):
  3. """
  4. 问题对象,没有使用策略模式之前的作法
  5. """
  6. def __init__(self, admin=True):
  7. self._admin = admin
  8. def show(self):
  9. """
  10. 根据是否是管理员显示不同的信息
  11. """
  12. if self._admin is True:
  13. return "show page with admin"
  14. else:
  15. return "show page with user"
  16. if __name__ == '__main__':
  17. q = Question(admin=False)
  18. print(q.show())

上面方法的缺陷是我们在增加功能的时候,需要进入到这个类里面去修改,而当功能比较多的时候,修改起来势必将特别复杂。

使用策略模式:

  1. # -*- coding: utf-8 -*-
  2. import abc
  3. class AbsShow(object):
  4. """
  5. 抽象显示对象
  6. """
  7. __metaclass__ = abc.ABCMeta
  8. @abc.abstractmethod
  9. def show(self):
  10. pass
  11. class AdminShow(AbsShow):
  12. """
  13. 管理员的显示操作
  14. """
  15. def show(self):
  16. return "show with admin"
  17. class UserShow(AbsShow):
  18. """
  19. 普通用户的显示操作
  20. """
  21. def show(self):
  22. return "show with user"
  23. class Question(object):
  24. """
  25. 问题对象,使用策略模式之后的作法
  26. """
  27. def __init__(self, show_obj):
  28. self.show_obj = show_obj
  29. def show(self):
  30. return self.show_obj.show()
  31. if __name__ == '__main__':
  32. q = Question(show_obj=AdminShow())
  33. print(q.show())
  34. # 替换原来的显示对象,体现了策略模式的互换行为
  35. q.show_obj = UserShow()
  36. print(q.show())

如上代码,我们使操作使用这些操作的客户端完全分开了
操作:两个继承接口的子类,AdminShow UserShow
使用操作的客户端:Question
好处:当我们在添加方法的时候,只需要增加对应的操作,而不修改客户端Question的代码。因为我们在实例化Question的时候传入的操作名称,会直接去寻找我们现有的操作

观察者模式

观察者模式,就是当一个对象发生变化时,观察者能及时得到通知并更新。

  1. # -*- coding: utf-8 -*-
  2. import abc
  3. class Subject(object):
  4. """
  5. 被观察对象的基类
  6. """
  7. def __init__(self):
  8. self._observers = []
  9. def attach(self, observer):
  10. """
  11. 注册一个观察者
  12. """
  13. if observer not in self._observers:
  14. self._observers.append(observer)
  15. def detach(self, observer):
  16. """
  17. 注销一个观察者
  18. """
  19. try:
  20. self._observers.remove(observer)
  21. except ValueError:
  22. pass
  23. def notify(self):
  24. """
  25. 通知所有观察者,执行观察者的更新方法
  26. """
  27. for observer in self._observers:
  28. observer.update(self)
  29. class Course(Subject):
  30. """
  31. 课程对象,被观察的对象
  32. """
  33. def __init__(self):
  34. super(Course, self).__init__()
  35. self._message = None
  36. @property
  37. def message(self):
  38. """
  39. message 是一个属性
  40. """
  41. return self._message
  42. @message.setter
  43. def message(self, msg):
  44. """
  45. message 属性设置器
  46. """
  47. self._message = msg
  48. self.notify()
  49. class Observer(object):
  50. """
  51. 观察者抽象类
  52. """
  53. __metaclass__ = abc.ABCMeta
  54. @abc.abstractmethod
  55. def update(self, subject):
  56. pass
  57. class UserObserver(Observer):
  58. """
  59. 用户观察者
  60. """
  61. def update(self, subject):
  62. print("User observer: %s" % subject.message)
  63. class OrgObserver(Observer):
  64. """
  65. 机构观察者
  66. """
  67. def update(self, subject):
  68. print("Organization observer: %s" % subject.message)
  69. if __name__ == '__main__':
  70. # 初始化一个用户观察者
  71. user = UserObserver()
  72. # 初始化一个机构观察者
  73. org = OrgObserver()
  74. # 初始化一个课程
  75. course = Course()
  76. # 注册观察者
  77. course.attach(user)
  78. course.attach(org)
  79. # 设置course.message,这时观察者会收到通知
  80. course.message = "two observers"
  81. # 注销一个观察者
  82. course.detach(user)
  83. course.message = "single observer"


  • 在上面的示例代码中,先是写了一个被观察者的基类,把被观察者的共同特性写入基类中。然后再在继承于基类,写了真正需要被观察的类Course
  • 然后写了观察者的抽象类Observer提供一个update接口,在后面的UserObserverOrgObserver中实现了这个接口。
  • 在主程序中,实例化两个观察者,user和org和一个被观察者course,并在、course中注册这两个观察者。

    注册实际上是将,两个实力对象的名称放入一个列表。当我们每写入一个massage就是执行self.notify()也就是取出列表里的所有观察者,并执行他们的update方法。

添加消息时,程序的执行顺序:

Created with Raphaël 2.1.2Startcourse.message="..."self.notify()取出注册列表中的所有实例名称,执行update方法End

好处:
该模式的好处,观察者和被观察者在程序逻辑上分开,当需要观察某个类时,只需要,将这个观察者实例化后注册到(添加到)被观察者的观察者列表中,即可。将会提供灵活方便的代码运用。

命令模式(Command)

命令模式就是对命令的封装。所谓封装命令,就是将一系列操作封装到命令类中,并且命令类只需要对外公开一个执行方法execute,调用此命令的对象只需要执行命令的execute方法就可以完成所有的操作。

  1. # -*- coding: utf-8 -*-
  2. import abc
  3. class VmReceiver(object):
  4. """
  5. 命令接收者,真正执行命令的地方
  6. """
  7. def start(self):
  8. print("Virtual machine start")
  9. def stop(self):
  10. print("Virtual machine stop")
  11. class Command(object):
  12. """
  13. 命令抽象类
  14. """
  15. __metaclass__ = abc.ABCMeta
  16. @abc.abstractmethod
  17. def execute(self):
  18. """
  19. 命令对象对外只提供 execute 方法
  20. """
  21. pass
  22. class StartVmCommand(Command):
  23. """
  24. 开启虚拟机的命令
  25. """
  26. def __init__(self, recevier):
  27. """
  28. 使用一个命令接收者初始化
  29. """
  30. self.recevier = recevier
  31. def execute(self):
  32. """
  33. 真正执行命令的时候命令接收者
  34. """
  35. self.recevier.start()
  36. class StopVmCommand(Command):
  37. """
  38. 停止虚拟机的命令
  39. """
  40. def __init__(self, recevier):
  41. """
  42. 使用一个命令接收者初始化
  43. """
  44. self.recevier = recevier
  45. def execute(self):
  46. """
  47. 真正执行命令的时候命令接收者
  48. """
  49. self.recevier.stop()
  50. class ClientInvoker(object):
  51. """
  52. 命令调用者
  53. """
  54. def __init__(self, command):
  55. self.command = command
  56. def do(self):
  57. self.command.execute()
  58. if __name__ == '__main__':
  59. recevier = VmReceiver()
  60. start_command = StartVmCommand(recevier)
  61. # 命令调用者同时也是客户端,通过命令实例也执行真正的操作
  62. client = ClientInvoker(start_command)
  63. client.do()
  64. # 能改变命令接收者,执行不同的操作
  65. stop_command = StopVmCommand(recevier)
  66. client.command = stop_command
  67. client.do()
  • 声明一个命令执行者的类VmReceiver,里面包含所有的命令.
  • 再实例两个命令类,用来传递命令(传入执行者类)
  • 实例化命令调用者ClientInvoker时传入相应的操作(such as StopVmCommand)名称。
    然后会找到该名称的类StopVmCommand,执行该类,该类就会去执行者VmReceiver中找对应的方法start执行。

好处:
代码更加灵活,增加代码复用。

模板方法模式(Template Method)

先定义一个类模板,在这个类中,我们定义了各种操作的顺序(轮毂或者说是骨架),但是并不实现这些操作,这些操作由子类来操作。

  1. # -*- coding: utf-8 -*-
  2. import abc
  3. class Fishing(object):
  4. """
  5. 钓鱼模板基类
  6. """
  7. __metaclass__ = abc.ABCMeta
  8. def finishing(self):
  9. """
  10. 钓鱼方法中,确定了要执行哪些操作才能钓鱼
  11. """
  12. self.prepare_bait()
  13. self.go_to_riverbank()
  14. self.find_location()
  15. print("start fishing")
  16. @abc.abstractmethod
  17. def prepare_bait(self):
  18. pass
  19. @abc.abstractmethod
  20. def go_to_riverbank(self):
  21. pass
  22. @abc.abstractmethod
  23. def find_location(self):
  24. pass
  25. class JohnFishing(Fishing):
  26. """
  27. John 也想去钓鱼,它必须实现钓鱼三步骤
  28. """
  29. def prepare_bait(self):
  30. """
  31. 从淘宝购买鱼饵
  32. """
  33. print("John: buy bait from Taobao")
  34. def go_to_riverbank(self):
  35. """
  36. 开车去钓鱼
  37. """
  38. print("John: to river by driving")
  39. def find_location(self):
  40. """
  41. 在岛上选择钓点
  42. """
  43. print("John: select location on the island")
  44. class SimonFishing(Fishing):
  45. """
  46. Simon 也想去钓鱼,它也必须实现钓鱼三步骤
  47. """
  48. def prepare_bait(self):
  49. """
  50. 从京东购买鱼饵
  51. """
  52. print("John: buy bait from JD")
  53. def go_to_riverbank(self):
  54. """
  55. 骑自行车去钓鱼
  56. """
  57. print("John: to river by biking")
  58. def find_location(self):
  59. """
  60. 在河边选择钓点
  61. """
  62. print("John: select location on the riverbank")
  63. if __name__ == '__main__':
  64. # John 去钓鱼
  65. f = JohnFishing()
  66. f.finishing()
  67. # Simon 去钓鱼
  68. f = SimonFishing()
  69. f.finishing()

将应该执行的步骤用抽象方法放在父类中,并写一个不是抽象方法的方法按顺序执行前面的步骤。(如果是抽象方法在写子类的时候就会复写掉了)。也就是我调用这个方法,即可按顺序执行完所有步骤。

作业


问题1:
请描述策略模式命令模式的不同之处

答:
我觉得,策略模式和命令模式,在前面都是一样的,在策略模式中,把选择操作,和实际操作分开了。而在命令模式,也将选择操作和实际操作分开了。但是有个中间者,它将命令的执行传递给了真正执行的那个类中。

下面是策略模式:

  1. +-----------+
  2. +---> |AdminShow()|
  3. | +-----------+
  4. +----------+ |
  5. |Question()++
  6. +----------+ |
  7. | +-----------+
  8. +---> |UserShow() |
  9. +-----------+
  10. 选择 执行

下面是命令模式:

  1. +----------------+
  2. +-> |StartVmCommand()+---+
  3. | +----------------+ |
  4. +---------------+ | | +------------+
  5. |ClientInvoker()+-+ +--> |VmReceiver()|
  6. +---------------+ | | +------------+
  7. | +----------------+ |
  8. +-> |StopVmCommand() +---+
  9. +----------------+
  10. 选择 传递 执行

问题2:
在学习命令模式的时候有没有想到任务队列呢?我们将需要执行的命令对象放入队列中,工作线程从队列中获取命令对象,并调用命令的execute方法以完成任务。这不就是命令模式吗?那么问题来了,请使用 Python 线程和队列实现命令模式,在这个例子中,你需要创建命令接收者,命令抽象类,命令具体类,一个用于将命令对象放入队列的线程类和一个从队列中获取命令对象并执行的线程类。提示:使用 Python threading 和 Queue 模块。

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注