python设计模式(三)

观察者模式

创建型模式的工作原理是基于对象的创建机制的。由于这些模式隔离了对象的创建细节,使得代码能够与要创建的对象的类型相互独立。

结构型模式用于设计对象和类的结构,从而使他们可以相互协作以获得更大的结构。

行为型模式,主要关注的是对象的责任,用来处理对象之间的交互,以实现更大的功能。

观察者设计模式是最简单的行为型模式之一。

主要目标:

  • 定义了对象之间的一对多的依赖关系,从而使一个对象中的任何改动都将通知给其他依赖对象。
  • 封装主题的核心组件

应用场景

  • 分布式系统中实现事件服务
  • 用作新闻机构框架
  • 股票市场也是观察者模式的一个大型场景
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
class Subject:

def __init__(self):
self.__observers = []

def register(self, observer):
self.__observers.append(observer)

def notifyAll(self, *args, **kwargs):
for observer in self.__observers:
observer.notify(self, *args, **kwargs)

class Observer:

def __init__(self):
subject.register(self)

def notify(self, subject, *args):
print(type(self).__name__, ":: Got", args, "From", subject)

class Observer1(Observer):

def __init__(self):
super(Observer1, self).__init__()

def notify(self, subject, *args):
super(Observer1, self).notify(subject, *args)

class Observer2(Observer):

def __init__(self):
super(Observer2, self).__init__()

def notify(self, subject, *args):
super(Observer2, self).notify(subject, *args)

subject = Subject()
observer1 = Observer1()
observer2 = Observer2()
subject.notifyAll("notification")

Subject作为一个主题,其实这跟装饰者模式有点类似

新闻机构的实现

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
from abc import ABCMeta, abstractmethod

class Subscriber(metaclass=ABCMeta):

@abstractmethod
def update(self):
pass

class SMSSuberscriber(Subscriber):
def __init__(self, publisher):
self.publisher = publisher
self.publisher.attach(self)

def update(self):
print(type(self).__name__, self.publisher.getNews())


class EmailSubscriber(Subscriber):
def __init__(self, publisher):
self.publisher = publisher
self.publisher.attach(self)

def update(self):
print(type(self).__name__, self.publisher.getNews())

class AnyOtherSubscriber(Subscriber):
def __init__(self, publisher):
self.publisher = publisher
self.publisher.attach(self)

def update(self):
print(type(self).__name__, self.publisher.getNews())


class NewsPublisher:

def __init__(self):
self.__subscribers = []
self.__latestNews = None

def attach(self, subscriber):
self.__subscribers.append(subscriber)

def detach(self):
return self.__subscribers.pop()

def subscribers(self):
return [type(x).__name__ for x in self.__subscribers]

def notifySubscribers(self):
for sub in self.__subscribers:
sub.update()

def addNews(self, news):
self.__latestNews = news

def getNews(self):
return "Got News:" , self.__latestNews

if __name__ == "__main__":
news_publisher = NewsPublisher()
for Subscribers in [SMSSuberscriber, EmailSubscriber, AnyOtherSubscriber]:
Subscribers(news_publisher)
print("\nSubscribers:", news_publisher.subscribers())

news_publisher.addNews("Hello world!")
news_publisher.notifySubscribers()

print("\nDetected:", type(news_publisher.detach()).__name__)
print("\nSubscribers:", news_publisher.subscribers())

news_publisher.addNews("My second news!")
news_publisher.notifySubscribers()

publisher实现了这个主题的职责,其余三个subscribe是观察者。

观察者模式的通知方式

拉模型

观察者扮演积极作用

  • 每当发生变化时,主题都会向所有已注册的观察者进行广播
  • 出现变化时,观察者负责获取相应的变化情况,或者从订户那里拉取数据
  • 拉模型的效率较低,因为它涉及两个步骤,1.主题通知观察者,2.观察者从主题处获取所需的数据

推模型

主题起主导作用的一方

  • 与拉模型不同,变化由主题推送到观察者。
  • 只从主题发送所需的数据,能提高性能

松耦合与观察者模式

松耦合主要目的是震区在彼此交互的对象之间实现松散耦合设计。耦合是指一个对象对于与其交互的其他对象的了解程度。

松耦合特性:

  • 它降低在一个元素内发生的更改可能对其他元素产生意外影响的风险
  • 是的测试、维护和故障排除工作更加简单
  • 系统可以轻松地分解为可定义的元素

在观察者模式上的解释:

  • 主题对观察者唯一的了解就是它实现了一个特定的接口,不需要了解具体观察者类
  • 可以随时添加任意的新观察者
  • 添加新的观察者时,不需更改主题
  • 观察者或主题没有绑定一起时,可以独立使用
  • 主题或观察者的变化不会相互影响,是独立松散耦合的

观察者模式的优缺点

优点:

  • 使得彼此交互的对象之间保持松弛耦合
  • 使得我们可以在无需对主题或观察者进行任何修改的情况下高效地发送数据到其他对象
  • 随时添加删除观察者

缺点:

  • 观察者接口必须有具体观察者实现,涉及继承,无法进行组合
  • 实现不当的话,观察者可能会增加复杂性,导致性能降低
  • 通知有时是不可靠的,并导致竞争条件或者不一致性

命令模式

命令模式是一种行为设计模式,其中对象用于封装在完成一项操作时或在触发一个时间时所需的全部信息:方法名称,拥有方法的对象,方法参数的值。

通常使用以下术语:Command、Receiver、Invoker和Client.

  • Command对象了解Receiver对象的情况,并能调用Receiver对象的方法。
  • 调用者方法的参数值存储在Command对象中
  • 调用者知道如何执行命令
  • 客户端用来创建Command对象并设置接收者

命令模式主要意图:

  • 将请求封装为对象
  • 可用不同的请求对客户进行参数化
  • 允许将请求保存在队列中
  • 提供面向对象的回调

常用场景:

  • 根据需要执行的操作对对象进行参数化
  • 将操作添加到队列并在不同地点执行请求
  • 创建一个结构来根据较小操作完成高级操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Wizard():

def __init__(self, src, rootdir):
self.choices = []
self.rootdir = rootdir
self.src = src

def preferences(self, command):
self.choices.append(command)

def execute(self):
for choice in self.choices:
if list(choice.values())[0]:
print("Copying binaries --", self.src, " to", self.rootdir)
else:
print("No Operation")

if __name__ == '__main__':
## Client code
wizard = Wizard("python", '/usr/bin')
wizard.preferences({"python":True})
wizard.preferences({"java":False})
wizard.execute()

preferences存储用户做出的选择,execute来执行

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
from abc import ABCMeta, abstractmethod

class Command(metaclass=ABCMeta):

def __init__(self, recv):
self.recv = recv

def execute(self):
pass

class ConcreteCommand(Command):

def __init__(self, recv):
self.recv = recv

def execute(self):
self.recv.action()

class Receiver:
def action(self):
print("Receiver Action")

class Invoker:
def command(self, cmd):
self.cmd = cmd

def execute(self):
self.cmd.execute()

if __name__ == '__main__':
recv = Receiver()
cmd = ConcreteCommand(recv)
invoker = Invoker()
invoker.command(cmd)
invoker.execute()

添加接收者和触发器

实现:

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
from abc import ABCMeta, abstractmethod

class Order(metaclass=ABCMeta):

@abstractmethod
def execute(self):
pass

class BuyStockOrder(Order):

def __init__(self, stock):
self.stock = stock

def execute(self):
self.stock.buy()

class SellStockOrder(Order):

def __init__(self, stock):
self.stock = stock

def execute(self):
self.stock.sell()

class StockTrade:

def buy(self):
print("You will buy stocks")

def sell(self):
print("You will sell stocks")

class Agent:

def __init__(self):
self.__orderQueue = []

def placeOrder(self, order):
self.__orderQueue.append(order)
order.execute()

if __name__ == '__main__':
# client
stock = StockTrade()
buyStock = BuyStockOrder(stock)
sellStock = SellStockOrder(stock)

# invoker
agent = Agent()
agent.placeOrder(buyStock)
agent.placeOrder(sellStock)

优缺点:

优点:

  • 将调用操作的类与知道如何执行该操作的对象解耦
  • 提供队列系统后,可以创建一系列命令
  • 添加新命令更加容易,并且无需更改现有代码
  • 可以使用命令模式来定义回滚系统

缺点:

  • 为了实现目标,需要大量的类和对象进行协作。
  • 每一个单独的类都是一个ConcreteCommand类,从而增加了需要实现和维护的类的数量。

模板方法模式

模板方法模式是一种行为设计模式,通过一种称为模板方法的方式来定义程序框架或算法。

模板方法模式还通过将这些步骤中的一些实现推迟到子类来帮助重新定义为模板方法中算法。

模板方法适用场景

  • 当多个算法或类实现类似或逻辑相同的时候
  • 在子类中实现算法有助于减少重复代码的时候
  • 可以让子类利用覆盖实现行为来定义多个算法的时候

一个简单的例子:编译器

主要意图:

  • 使用基本操作定义算法的框架
  • 重新定义子类的某些操作,而无需修改算法的结构
  • 实现代码重用并避免重复工作
  • 利用通用接口或实现

术语: AbstratClass, ConcreteClass, TemplateMethod和Client

  • AbstractClass: 声明一个定义算法步骤的接口
  • ConcreteClass: 定义子类特定的步骤
  • templateMethod: 通过调用步骤来定义算法
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
from abc import ABCMeta, abstractmethod

class Complier(metaclass=ABCMeta):

@abstractmethod
def collectSource(self):
pass

@abstractmethod
def compileToObject(self):
pass

@abstractmethod
def run(self):
pass

def compileAndRun(self):
self.collectSource()
self.compileToObject()
self.run()

class IosComplier(Complier):

def collectSource(self):
print("Collecting Swift Source Code")

def compileToObject(self):
print("Compiling Swift code to LLVM bitcode")

def run(self):
print("Program running on runtime environment")

ios = IosComplier()
ios.compileAndRun()

一个编译器,compileAndRun()就是一个模板的核心。

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
from abc import ABCMeta, abstractmethod

class AbstractClass(metaclass=ABCMeta):

def __init__(self):
pass

@abstractmethod
def operation1(self):
pass

@abstractmethod
def operation2(self):
pass

def template_method(self):
print("Defining the Algorithm. Operation1 follows Operation2")
self.operation2()
self.operation1()

class ConcreteClass(AbstractClass):

def operation1(self):
print("My Concrete Operation1")

def operation2(self):
print("Operation2 remains same")

class Client:

def main(self):
self.concrete = ConcreteClass()
self.concrete.template_method()

client = Client()
client.main()

实现:

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
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()

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("Don't feel like leaving the beach..")

class TravelAgency:

def arrangeTrip(self):
choice = input("What kind of place you'd like to go historical or to a beach?\n")
if choice == "historical":
self.trip = VeniceTrip()
self.trip.itinerary()

if choice == "beach":
self.trip = MaldivesTrip()
self.trip.itinerary()

TravelAgency().arrangeTrip()

一个旅游社的安排

优缺点:

优点:

  • 没有代码重复
  • 使用的是继承不是合成,可以对代码进行重用,只有为数不多的几个方法需要重写
  • 灵活性允许子类决定如何实现算法中的步骤

缺点:

  • 调试和理解模板方法模式中的流程序列会使人困惑,文档和养个的错误处理由程序员完成
  • 模板框架的维护可能是一个问题
文章目录
  1. 1. 观察者模式
    1. 1.1. 新闻机构的实现
    2. 1.2. 观察者模式的通知方式
    3. 1.3. 松耦合与观察者模式
    4. 1.4. 观察者模式的优缺点
  2. 2. 命令模式
    1. 2.1. 实现:
    2. 2.2. 优缺点:
  3. 3. 模板方法模式
    1. 3.1. 实现:
    2. 3.2. 优缺点:
|