软件编程之设计模式IV-行为型模式

类和对象如何交互,及划分责任和算法。

共分为以下几种:解释器模式、责任链模式、命令模式、迭代器模式、中介者模式、备忘录模式、观察者模式、状态模式、策略模式、访问者模式、模板方法模式。

解释器模式

责任链模式

使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系。讲这些对象连成一条链,并沿着这条链传递该请求,知道有一个对象处理它为止。

角色

  • 抽象处理者(Handler)
  • 具体处理者(ConcreteHandler)
  • 客户端(Client)

适用场景

  • 有多个对象可以处理一个请求,哪个对象处理由运行时决定
  • 在不明确接受者的情况下,向多个对象中的一个提交一个请求

优点

  • 降低耦合度:一个对象无需知道是其他哪一个对象处理的请求

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
from abc import ABCMeta, abstractmethod

# 抽象处理者(Handler)
class Handler(metaclass=ABCMeta):
@abstractmethod
def handler_leave(self, days):
pass

# 具体处理者(ConcreteHandler)
class GeneralManager(Handler):

def handler_leave(self, days):
if days <= 10:
print(f"总经理准假{days}天")
else:
print("请假天数过多,没有人可以批准")

# 具体处理者(ConcreteHandler)
class BranchManager(Handler):
def __init__(self):
self.next = GeneralManager()

def handler_leave(self, days):
if days <= 5:
print(f"部门经理准假{days}天")
else:
print("请假天数过多,部门经理没有批准权限")
self.next.handler_leave(days)

# 具体处理者(ConcreteHandler)
class ProductManager(Handler):

def __init__(self):
self.next = BranchManager()

def handler_leave(self, days):
if days <= 3:
print(f"产品经理准假{days}天")
else:
print("请假天数过多,产品经理没有批准权限")
self.next.handler_leave(days)

# 客户端(Client)
day = 9
ProductManager().handler_leave(day)

命令模式

迭代器模式

中介者模式

备忘录模式

观察者模式

定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新,观察者模式又称发布-订阅模式

角色

  • 抽象主题(Subject)
  • 具体主题(ConcreteSubject)-发布者
  • 抽象观察者(Observer)
  • 具体观察者(ConcreteObserver)-订阅者/观察者

适用场景

  • 当一个抽象模型有两方面,其中一个方面依赖于另一个方面。将这两者封装在独立对象中以使它们可以各自独立的改变和复用
  • 当对一个对象的改变需要同时改变其他对象,而不知道具体有多少对象有待改变时
  • 当一个对象必须通知其他对象,而它又不能假定其他对象是谁。换言之,你不希望这些对象是紧密耦合的时候

优点

  • 目标和观察者之间的抽象耦合最小

  • 支持广播通信

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
from abc import ABCMeta, abstractmethod


# 抽象观察者/订阅者
class Observer(metaclass=ABCMeta):
@abstractmethod
def update(self, notice):
# notice为Publisher的一个实例
pass


# 抽象发布者
class Publisher(metaclass=ABCMeta):
def __init__(self):
# 模拟订阅者列表
self.observers = []

# 订阅
def attach(self, observer_obj):
self.observers.append(observer_obj)

# 取消订阅
def detach(self, observer_obj):
self.observers.remove(observer_obj)

# 发布通知
def notify(self):
[observer_obj.update(self) for observer_obj in self.observers]

# 具体发布者
class CompanyPublisher(Publisher):
def __init__(self, info=None):
super().__init__()
self.__info = info

@property
def info(self):
return self.__info

@info.setter
def info(self, info):
self.__info = info
# 公告修改,通知所有订阅者
self.notify()

# 具体观察者/订阅者
class StaffObserver(Observer):
def __init__(self):
self.info = None

def update(self, notice):
# 将公司公告赋值给员工
self.info = notice.info


# 员工1
staff1 = StaffObserver()

# 员工2
staff2 = StaffObserver()

# 公司发布者
c = CompanyPublisher("我是公司公告")

# 员工1绑定公司
c.attach(staff1)
# 员工2绑定公司
c.attach(staff2)
# 发布公告
c.info = "新人守则,请大家仔细阅读"
print(staff1.info)
print(staff2.info)

print("-------------------------------------")

# 取消员工2绑定公司
c.detach(staff2)
# 发布公告
c.info = "新年快乐,公司公告"
print(staff1.info)
print(staff2.info)

运行结果

1
2
3
4
5
新人守则,请大家仔细阅读
新人守则,请大家仔细阅读
-------------------------------------
新年快乐,公司公告
新人守则,请大家仔细阅读

状态模式

策略模式

定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换。本模式使得算法可独立于使用它的客户而变化。

角色

  • 抽象策略(Strategy)
  • 具体策略(ConcreteStrategy)
  • 上下文(Context)

优点

  • 定义了一系列可重用的算法和行为
  • 消除了一些条件语句
  • 可以提供相同行为的不同实现

缺点

客户必须了解不同的策略

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
from abc import ABCMeta, abstractmethod


class Strategy(metaclass=ABCMeta):
@abstractmethod
def execute(self, data):
pass


class FirstStrategy(Strategy):
def execute(self, data):
print(f"我是策略一【{data}】")


class SecondStrategy(Strategy):
def execute(self, data):
print(f"我是策略二【{data}】")


class Context:
def __init__(self, strategy, data):
self.strategy = strategy
self.data = data

def change_strategy(self, strategy):
self.strategy = strategy

def do_strategy(self):
self.strategy.execute(self.data)


# 策略1
first = FirstStrategy()
# 策略2
second = SecondStrategy()

data = "data……"

# 初始化策略1为默认策略
c = Context(first, data)
# 执行策略
c.do_strategy()

# 切换策略2
c.change_strategy(second)
# 执行策略
c.do_strategy()

执行结果

1
2
我是策略一【data……】
我是策略二【data……】

访问者模式

模板方法模式

定义一个操作中的算法的骨架,而将一些步骤延迟(具体实现)到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。

角色

  • 抽象类(AbstractClass):定义抽象的原子操作(狗子操作);实现一个模板方法作为算法的骨架。
  • 具体类(ConcreteClass):实现(具体代码)原子操作

适用场景

  • 一次性实现一个算法的不变的部分
  • 各个子类中的公共行为应该被提取出来并集中到一个公共父类中以避免代码重复
  • 控制子类扩展

示例

代码运行中已使用ctrl+c强制结束,所以窗口只绘制了4次

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
import random
import time
from abc import ABCMeta, abstractmethod


# 抽象类(AbstractClass)
class Window(metaclass=ABCMeta):
@abstractmethod
def start(self):
"""窗口开始后要执行的逻辑代码"""
pass

@abstractmethod
def repaint(self):
"""窗口变化绘制的逻辑代码"""
pass

@abstractmethod
def close(self):
"""窗口关闭要执行的逻辑代码"""
pass

def run(self):
"""窗口正常运行流程"""
self.start()
while True:
try:
self.repaint()
time.sleep(random.randint(1, 3))
except KeyboardInterrupt:
break
self.close()


# 具体类(ConcreteClass)
class MyWindow(Window):
def __init__(self, data):
self.data = data

def start(self):
print("窗口开始运行代码逻辑……")

def repaint(self):
print(f"窗口绘制内容为:{self.data}-{random.randint(10000000, 999999999)}")

def close(self):
print("窗口关闭运行代码逻辑")


w = MyWindow("Windows内容体")
w.run()

运行结果:

1
2
3
4
5
6
窗口开始运行代码逻辑……
窗口绘制内容为:Windows内容体-167964939
窗口绘制内容为:Windows内容体-23453276
窗口绘制内容为:Windows内容体-624587516
窗口绘制内容为:Windows内容体-79282804
窗口关闭运行代码逻辑