行为型模式

行为型模式,顾名思义,它主要关注的是对象的责任。

  • 它们关注对象之间的交互以及对象的响应性
  • 对象应该能够交互,同时仍然保持松散耦合

观察者模式

在观察者设计模式中,对象(主题)维护了一个依赖(观察者)列表,以便主题可以使用观察者定义的任何方法通知所有观察者它所发生的变化。

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

UML图

image-20210225223249145

  • 主题(Subject):类Subject需要了解Observe。Subject类具有许多方法,诸如register()和deregister()等,Observer可以通过这些方法注册到Subject类中。因此,一个Subject可以处理多个Observe。
  • 观察者(Observe):它为关注主题的对象定义了一个接口。它定义了Observe需要实现的各个方法,以便在主题发生变化时能够获得相应的通知。
  • 具体观察者(ConcreteObserver):它用来保存应该与Subject的状态保持一致的状态。它实现了Observe接口以保持其状态与主题中的变化相一致。

代码案例

 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
#!/usr/bin/env python
# -*- coding: utf-8 -*-

class Subject(object):
    def __init__(self):
        self.__observer = []

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

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

class Observer1(object):
    def __init__(self, subject):
        subject.register(self)

    def notify(self, subject, *args):
        print(f"{type(self).__name__}:: Got, {args} from {subject}")

class Observer2(object):

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

    def notify(self, subject, *args):
        print(f"{type(self).__name__}:: Got, {args} from {subject}")

if __name__ == '__main__':
    subject = Subject()
    observer1 = Observer1(subject)
    observer2 = Observer2(subject)
    subject.notifyAll("notify~~~")

观察者模式的优点和缺点

优点:

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

缺点:

  • 观察者接口必须由具体观察者实现,这涉及继承。无法进行组合,因为观察者接口可以实例化
  • 如果实现不当的话,观察者可能会增加复杂性,并导致性能降低
  • 在软件应用程序中,通知有时可能是不可靠的,并导致竞争条件或不一致性

现实世界的观察者模式

  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
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
#!/usr/bin/env python
# -*- coding: utf-8 -*-

from abc import ABCMeta
import abc

class NewsPublisher(object):
    """主题,新闻发布机构."""

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

    def attach(self, subscriber):
        """订阅.

        :param subscriber:
        :return: list
        """
        self.__subscribers.append(subscriber)

    def detach(self):
        """取消订阅.

        :param subscriber:
        :return: list
        """
        return self.__subscribers.pop()

    def subscribers(self):
        """获取注册的订阅者.

        :return: list
        """
        return [type(x).__name__ for x in self.__subscribers]

    def notifySubscribers(self):
        """通知订阅者.

        :return: obj
        """
        for sub in self.__subscribers:
            sub.update()

    def addNews(self, news):
        """添加新闻.

        :param news:
        :return:
        """
        self.__latestNews = news

    def getNews(self):
        """返回最新信息.

        :return:
        """
        return f"Got News: {self.__latestNews}"

class Subscriber(metaclass=ABCMeta):
    """观察者抽象接口."""

    @abc.abstractmethod
    def update(self):
        """抽象方法.

        :return:
        """
        pass

class EmailSubscriber(Subscriber):
    """观察者1."""

    def __init__(self, publisher):
        self.publisher = publisher
        self.publisher.attach(self)

    def update(self):
        """更新新闻."""
        print(type(self).__name__, self.publisher.getNews())

class SMSSubscriber(Subscriber):
    """观察者2."""

    def __init__(self, publisher):
        self.publisher = publisher
        self.publisher.attach(self)

    def update(self):
        """更新新闻."""
        print(type(self).__name__, self.publisher.getNews())

class AnyOtherSubscriber(Subscriber):
    """观察者3."""

    def __init__(self, publisher):
        self.publisher = publisher
        self.publisher.attach(self)

    def update(self):
        """更新新闻."""
        print(type(self).__name__, self.publisher.getNews())

if __name__ == '__main__':
    new_publisher = NewsPublisher()
    for Subscribers in [SMSSubscriber, EmailSubscriber, AnyOtherSubscriber]:
        Subscribers(new_publisher)
    print(f"\\n Subscriber:{new_publisher.subscribers()}")

    new_publisher.addNews("hello")
    new_publisher.notifySubscribers()

    print(f"\\n Detach:{type(new_publisher.detach()).__name__}")
    print(f"\\n Subscribers:{new_publisher.subscribers()}")

    new_publisher.addNews("hi")
    new_publisher.notifySubscribers()

命令模式——封装调用

命令模式也是一种行为设计模式,其中对象用于封装在完成以项操作时或在触发一个事件时所需的全部信息。

这些信息包含如下

  • 方法名称
  • 拥有方法的对象
  • 方法参数的值

命令模式常用术语Command、Receiver、Invoker 和 Client

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

命令模式的主要意图如下

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

命令模式的应用场景

  • 根据需要执行的操作对对象进行参数化
  • 将操作添加到队列并在不同地点执行请求
  • 创建一个结构来根据较小操作完成高级操作

UML图

image-20210225223627772

  • Command:声明执行操作的接口。
  • ConcreteCommand:将一个Receiver对象和一个操作绑定在一起。
  • Client:创建ConcreteCommand对象并设定其接受者。
  • Invoker:要求改ConcreteCommand执行这个请求。
  • Receiver:知道如何实施与执行一个请求相关的操作。

代码实现

 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
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from abc import ABCMeta
import abc

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(object):
    def action(self):
        print("Receiver Action")

class Invoker(object):
    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()

outReceiver Action

现实世界的命令模式(一个股票交易例子)

 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
#!/usr/bin/env python
# -*- coding: utf-8 -*-

from abc import ABCMeta
import abc

class Order(metaclass=ABCMeta):
    """订单抽象接口类."""

    @abc.abstractmethod
    def execute(self):
        pass

class BuyStockOrder(Order):
    """Buy 接口实现类."""
    
    def __init__(self, stock):
        self.stock = stock

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

class SellStockOrder(Order):
    """Sell 接口实现类."""
    
    def __init__(self, stock):
        self.stock = stock

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

class StockTrade(object):
    """接收者."""
    
    def buy(self):
        """调用交易中所卖入的股票."""
        
        print("You will buy stocks")

    def sell(self):
        """调用交易中所买入的股票."""
        
        print("You will sell stocks")

class Agent(object):
    """代理."""
    
    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类,从而增加了需要实现和维护的类的数量。

模板方法设计模式

通过一种称为模板方法的方式来定义程序框架或算法

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

模板方法模式常用术语

  • AbstractClass:声明一个定义算法步骤的接口。
  • ConcreteClass:定义子类特定的步骤。
  • template_method():通过调用步骤方法来定义算法。

代码实现(案例1)

 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
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from abc import ABCMeta
import abc

class Compiler(metaclass=ABCMeta):
    @abc.abstractmethod
    def collectSource(self):
        pass

    @abc.abstractmethod
    def compileToObject(self):
        pass

    @abc.abstractmethod
    def run(self):
        pass

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

class IOSCompiler(Compiler):
    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")

if __name__ == '__main__':
    iOS = IOSCompiler()
    iOS.complileAndRun()

UML图

image-20210225223752017

  • AbstractClass:在抽象方法的帮助下定义算法的操作或步骤。这些步骤将被具体子类覆盖
  • template_method():定义算法的框架。在模板方法中调用抽象方法定义的多个步骤来定义序列或算法本身。
  • ConcreteClass:实现(由抽象方法定义的)步骤,来执行算法子类的特定步骤。

代码案例二

 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
#!/usr/bin/env python
# -*- coding: utf-8 -*-
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("My Concrete Operation2")

class Client(object):
    def main(self):
        self.concreate = ConcreteClass()
        self.concreate.template_method()

if __name__ == '__main__':
    client = Client()
    client.main()

out
My Concrete Operation2
My Concrete Operation1

代码案例三(旅行社例子)

 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
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from abc import abstractmethod, ABCMeta

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):
    """Venice 旅游实现类."""
    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):
    """Maldives 旅游实现类."""
    def setTransport(self):
        print("On foot, on any island, wow!")

    def day1(self):
        print("Enjoy the marine life of Banana Reed")

    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 TraveAgency(object):
    """代理类."""
    def arrange_trip(self):
        choice = input("What kind of place you`d like togo historical or to a beach? \\n")
        if choice == 'historical':
            self.trip = VeniceTrip()
            self.trip.itinerary()
        if choice == 'beach':
            self.trip = MaldivesTrip()
            self.trip.itinerary()

TraveAgency().arrange_trip()

模板方法模式优缺点

优点

  • 没有重复代码
  • 由于模板方法模式使用继承而不是合成,因此能够对代码进行重用。所以只有为数不多的几个方法需要重写;
  • 灵活性允许子类决定如何实现算法中的步骤

缺点

  • 调试和理解模板方法模式中的流程序列有时会令人困惑。你最终实现的方法可能是一个不应该实现的方法,或根本没有实现抽象方法。文档和严格的错误处理必须由程序员完成;
  • 模板框架的维护可能是一个问题,因为任何层次(低层或高层)的变更都可能对实现造成干扰。因此,使用模板方法模式可能会使维护变得异常痛苦。

状态设计模式

一个对象可以基于其内部状态封装多个行为。状态模式也可以看作是在运行时改变对象行为的一种方式。

状态设计模式常用名词

  • State:这被认为是封装对象行为的接口。这个行为与对象的状态相关联。
  • ConcreteState:这是实现State接口的子类。ConcreteState实现与对象的特定状态相关联的实际行为。
  • Context:这定义了用户感兴趣的接口。Context还维护一个ConcreteState子类的实例,该子类在内部定义了对象的特定状态的实现。

代码示例1

 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
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from abc import ABCMeta, abstractmethod

class State(metaclass=ABCMeta):
    """状态抽象类接口."""

    @abstractmethod
    def Handle(self):
        pass

class ConcreteStateB(State):
    """接口子类A."""

    def Handle(self):
        print("ConcreteStateB")

class ConcreteStateA(State):
    """接口子类B."""

    def Handle(self):
        print("ConcreteStateA")

class Context(State):
    """用户操作接口."""

    def __init__(self):
        self.state = None

    def getState(self):
        return self.state

    def setState(self, state):
        self.state = state

    def Handle(self):
        self.state.Handle()

if __name__ == '__main__':
    context = Context()
    stateA = ConcreteStateA()
    stateB = ConcreteStateB()

    context.setState(stateB)
    context.Handle()

out:
ConcreteStateB

UML图

image-20210225223938505

  • Handle()方法,根据状态的变化定义要采取的实际行动
  • Context:这是一个接受客户端请求的类。它维护着对象的当前状态的引用。这样,就可以根据相应的请求,来调用具体的行为。

代码示例2(TV播放)

 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
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from abc import abstractmethod, ABCMeta

class State(metaclass=ABCMeta):

    @abstractmethod
    def doThis(self):
        pass

class StartState(State):
    def doThis(self):
        print("Tv Switching ON ..")

class SweightState(State):
    def doThis(self):
        print("Tv Switching OFF..")

class TVContext(State):
    def __init__(self):
        self.state = None

    def getState(self):
        return self.state

    def setState(self, state):
        self.state = state

    def doThis(self):
        self.state.doThis()

if __name__ == '__main__':
    context = TVContext()
    context.getState()

    start = StartState()
    sweight = SweightState()

    context.setState(sweight)
    context.doThis()

out
Tv Switching OFF..

一个应用案例(pythonic 电脑状态)

 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
#!/usr/bin/env python
# -*- coding: utf-8 -*-

class ComputerState(object):
    name = "state"
    allowed = []

    def switch(self, state):
        if state.name in self.allowed:
            print(f"Current: {self} => switched to new state {state.name}.")
            state.__class__ = state

        else:
            print(f"Current: {self} => switching to {state.name} not possible.")

    def __str__(self):
        return self.name

class Off(ComputerState):
    name = "off"
    allowed = ["on"]

class On(ComputerState):
    name = "on"
    allowed = ["off", "suspend", "hibernate"]

class Suspend(ComputerState):
    name = "suspend"
    allowed = ["on"]

class Hibernate(ComputerState):
    name = "hibernate"
    allowed = ["on"]

class Computer(object):
    def __init__(self, model="HP"):
        self.model = model
        self.state = Off()

    def change(self, state):
        self.state.switch(state)

if __name__ == '__main__':
    comp = Computer()
    # Switch On
    comp.change(On)
    # Switch Off
    comp.change(Off)

    # Switch on again
    comp.change(On)
    # Suspend
    comp.change(Suspend)
    # Try to hibernate --> can`t
    comp.change(Hibernate)
    # switch on back
    comp.change(On)
    # Finally off
    comp.change(Off)

状态模式优缺点

优点

  • 在状态设计模式中,对象的行为是其状态的函数结果,并且行为在运行时根据状态而改变。这消除了对iese或 switch/case条件逻辑的依赖。例如,在电视远程遥控的场景中,我们还可以通过简单地写一个类和方法来实现相应的行为,但是该类和方法将用到参数,并使用ieei句块来执行具体操作(打开/关闭电视)。
  • 使用状态模式,实现多态行为的好处是显而易见的,并且更易于添加状态来支持额外的行为。
  • 状态设计模式还提高了聚合性,因为特定于状态的行为被聚合到 Concretestate类中,并且放置在代码中的同一个地方。
  • 使用状态设计模式,通过只添加一个 Concretestate类来添加行为是非常容易的。因此,状态模式不仅改善了扩展应用程序行为时的灵活性,而且全面提高了代码的可维护性。

不足

  • 类爆炸:由于每个状态都需要在 Concretestate的帮助下定义,因此我们可能导致创建了太多功能较为单一的类。我们不妨考虑有限状态机的情况——如果有许多状态,但每个状态与另一个状态没有太大不同,我们仍然需要将它们写成单独的 Concretestate类。这既增加了代码量,又使得状态机的结构更加难以审查
  • 随着每个新行为的引入(即使添加行为只是添加一个 Concretestate), Context类都需要进行相应的更新以处理每个行为。这使得上下文行为更容易受到每个新的行为的影响。