0%

Python 设计模式

努力学习,充实自己。

本文为阅读学习笔记,摘抄至

设计模式时构建大型软件系统最强大的方法之一。

《Python 设计模式》

常见的设计模式

  • 创建型模式
    • 运行机制基于对象的创建方式
    • 将对象创建的细节隔离
    • 代码与创建对象的类型无关
      单例模式是创建模式的一个例子
  • 结构型模式
    • 致力于设计出能够通过组合获得更强大功能的对象和类的结构
    • 重点是简化并识别类和对象之间的关系
    • 关注类的继承和组合
      适配器模式是结构型模式的一个例子
  • 行为型模式
    • 关注对象之间的家伙以及对象的响应性
    • 对象能够交互,同时保持松散耦合
      观察者模式是行为型模式的一个例子

创建型模式

结构型设计模式

了解:

  • 描述如何将对象和类组合成更大的结构。
  • 能够简化设计工作的模式,因为能够找出更贱大方法来认识或表示实体间的关系。
  • 类模式可以通过集成来描述抽象,从而提供更有用的程序接口,而对象模式则描述了如何将对象联系起来。结构型模式时类和对象模式的综合体。

例子:

  • 适配器模式: 将一个接口转换成客户希望的另外一个接口。试图根据客户端的需求来匹配不同类的接口
  • 桥接模式: 该模式将对象的接口与其实现进行解耦,使得两者可以独立工作
  • 装饰器模式: 该模式允许在运行时或以动态方式为对象添加职责。可以通过接口给对象添加某些属性

行为型设计模式


单例模式

  • 确保类有且只有一个对象被创建
  • 为对象提供一个访问点,以是程序可以全局访问该对象
  • 控制共享资源的并行访问

实现方法

1、构造函数私有化

  • 单例模式的一个简单方法是:使构造函数私有化,并创建一个静态方法来完成对象的初始化。

    • 只允许 Singleton 类生成一个实例
    • 如果已经有一个实例,会重复提供同一个对象
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    # 通过覆盖 __new__ 方法(python 用于实例化对象的特殊方法)来控制对象的创建,
    # 在创建对象之前,会检查对象是否存在
    class Singleton(object):
    def __new__(cls):
    if not hasattr(cls, 'instance'):
    cls.instance = super().__new__(cls)
    return cls.instance

    s = Singleton()
    s1 = Singleton()
    print(s, s1)

    <__main__.Singleton object at 0x000002769538A828>, <__main__.Singleton object at 0x000002769538A828>

2、懒汉式实例化

  • 能够确保在实际需要时才创建对象
    • 节约资源
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Singleton:
__instance = None
def __init__(self):
if not Singleton.__instance:
print("__init__ method called...")
else:
print("Instance already created;", self.get_instance())
@classmethod
def get_instance(cls):
if not cls.__instance:
cls.__instance = Singleton()
return cls.__instance

s = Singleton() # class initialized, but object not created
print("Object created", Singleton.get_instance()) # Object gets created here
s1 = Singleton() # instance already created

3、模块级别的单例模式

  • 默认情况下,所有的模块都是单例,这是由 Python 的导入行为所决定的
    • 检查一个 Python 模块是否已经导入
    • 如果已经导入,则返回改模块的对象。如果还没有导入,则导入该模块,并实例化
    • 因此,当模块被导入的时候,它就会被初始化。然而,当同一个模块被再次导入的时候, 它不会再次初始化,因为单例模式只能有一个对象,所以,他会返回同一个对象

4、Monostate(单态)单例模式

  • 所有对象共享相同状态

    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
    # python 使用 __dict__ 存储一个类所有对象的状态
    class Borg:
    __shared_state = {'1': '2'}
    def __init__(self):
    self.x = 1
    self.__dict__ = self.__shared_state
    b = Borg()
    b_1 = Borg()
    b.x = 4
    print("Borg Object 'b'", b) # b and b_1 are distinct objects
    print("Borg Object 'b_1'", b_1)
    print("Object state 'b'", b.__dict__) # b and b_1 share same state
    print("Object state 'b_1'", b_1.__dict__)

    Borg Object 'b' <__main__.Borg object at 0x0000012FBB4B8F28>
    Borg Object 'b_1' <__main__.Borg object at 0x0000012FBB4959B0>
    Object state 'b' {'1': '2', 'x': 4}
    Object state 'b_1' {'1': '2', 'x': 4}

    # 使用 __new__ 方法本身实现
    class Borg(object):
    __shared_state = {}
    def __new__(cls, *args, **kwargs):
    obj = super().__new__(cls, *args, **kwargs)
    obj.__dict__ = cls.__shared_state
    return obj
    b = Borg()
    b_1 = Borg()
    print("Borg object 'b'", b)
    print("Borg object 'b_1'", b_1)
    print("Object state 'b'", b.__dict__)
    b.x = 1
    print("Object state 'b'", b.__dict__)
    print("Object state 'b_1'", b_1.__dict__)

    Borg object 'b' <__main__.Borg object at 0x0000012FBA0AA8D0>
    Borg object 'b_1' <__main__.Borg object at 0x0000012FBA0AAA20>
    Object state 'b' {}
    Object state 'b' {'x': 1}
    Object state 'b_1' {'x': 1}

5、单例和元类

  • 元类是一个类的类,意味着 该类是它元类的实例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    class MyInt(type):
    def __call__(self, *args, **kwargs):
    print("**** Here's MY int *****", args)
    print("Now do whatever you want with these objects...")
    return type.__call__(cls, *args, **kwargs)
    class int(metaclass=MyInt):
    def __init__(self, x, y):
    self.x = x
    self.y = y

    i = int(2,3)

    **** Here's MY int ***** (2, 3)
    Now do whatever you want with these objects...

    对于已经存在的类,需要创建对象时,将调用 python 的特殊方法 __call__
    由于元类对类创建和对象实例化有更多的控制权,所以可以用于创建单例。
    为了控制类的创建和初始化,元类将覆盖 __new____init__ 方法

    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
    class Singleton(type):
    def __init__(self, *args, **kwargs):
    self.__instance = None
    super().__init__(*args, **kwargs)
    def __call__(self, *args, **kwargs):
    if self.__instance is None:
    self.__instance = super().__call__(*args, **kwargs)
    return self.__instance
    else:
    return self.__instance

    class Spam(metaclass=Singleton):
    def __init__(self):
    print("Creating Spam")

    a = Spam() # class initialized,and create object
    b = Spam() # class already initialized
    a is b
    c = Spam()
    a is c
    c
    b
    a

    Creating Spam
    True
    True
    <__main__.Spam object at 0x000002B0213E5898>
    <__main__.Spam object at 0x000002B0213E5898>
    <__main__.Spam object at 0x000002B0213E5898>

    其他参考:

使用案例

1、 数据库

  • 对数据库进行多种读取和写入操作

2、 为基础设施运行状况提供监控

  • 通过创建元类,并重写 __init____call__ 方法,同时使用新类中的继承使用方式,这使得元类的创建更加简洁

    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
    class MetaSingleton(type):
    def __init__(self, *args, **kwargs):
    self._instance = None
    super().__init__(*args, **kwargs)
    def __call__(self, *args, **kwargs):
    if self._instance is None:
    self._instance = super().__call__(*args, **kwargs)
    return self._instance
    class HealthCheck(metaclass=MetaSingleton):
    def __init__(self):
    self._servers = []
    def add_server(self):
    self._servers.append("Server 1")
    self._servers.append("Server 2")
    self._servers.append("Server 3")
    self._servers.append("Server 4")
    def change_server(self):
    self._servers.pop()
    self._servers.append("Server 5")

    hc1 = HealthCheck() # create object hc1
    hc2 = HealthCheck() # create object hc2
    print(hc1, hc2) # the object hc1 is hc2, because the object is same

    (<__main__.HealthCheck object at 0x000002AE60F94160>, <__main__.HealthCheck object at 0x000002AE60F94160>)

    hc1.add_server() # append some serve into servers
    print("Schedule health check for servers(1)...")

    Schedule health check for servers(1)...

    for i in range(4):
    print("Checking ", hc1._servers[i])

    Checking Server 1
    Checking Server 2
    Checking Server 3
    Checking Server 4

    hc2.change_server() # pop the servers
    print("Schdule health check for server (2)...")

    Schdule health check for server (2)...

    for i in range(4):
    print("Checking ", hc2._servers[i])

    Checking Server 1
    Checking Server 2
    Checking Server 3
    Checking Server 5

单例模式的缺点

因为单例模式,具有全局访问的权限,因此可能会出现如下问题:

  • 全局变量可能在某处已经被误改,但是开发人员仍然认为它们没有发生变化,而该变量还在应用程序的其他位置被使用
  • 可能会对同一个对象创建多个引用。由于单例只创建一个对象,因此这种情况下会对同一个对象创建多个引用
  • 所有依赖于全局变量的类都会由于一个类的改变紧密耦合为全局数据,从而可能在无意中影响另一个类。

单例模式使用建议

  • 只需要创建一个对象,如线程池、缓存、对话框、注册表设置等。如果为每个应用程序创建多个实例,会导致资源的过度使用。单例模式在这种情况下工作得很好。
  • 单例是一种经过时间考验的成熟方法,能够在不带来太多缺陷的情况下提供全局访问点
  • 当使用全局变量或类的实例化非常耗费资源单最终却没有用到他们的情况下,单例的影响可以忽略不计。

工厂模式

“工厂”表示一个负责创建其他类型对象的类

优点:

  • 松耦合,即对象的创建可以独立于类的实现
  • 客户端无需了解创建对象的类,只需知道需要传递的接口、方法和参数,就能够创建所需类型的对象了。
  • 可以轻松在工厂中添加其他类来创建其他类型的对象
  • 工厂还可以重用现有对象。

Factory 模式有三种变体:

  • 简单工厂模式: 允许接口创建对象,但不会暴露对象的创建逻辑
  • 工厂方法模式: 允许接口创建对象,但使用哪个类来创建对象,则交由子类决定
  • 抽象工厂模式: 抽象工厂是一个能够创建一系列相关的对象而无需指定、公开其他具体类的接口。该模式能够提供其他工厂的对象,在其内部创建其他对象。

简单工厂模式

创建抽象类: 抽象的基类(ABCMeta 是 python 的特殊元类,用来生成类 Abstract)

  • 当元类 metaclassABCMeta 并且类方法中使用了装饰器 abstractmethod 后,此类为抽象类,无法实例化。

    1
    2
    3
    4
    5
    6
    from abc import ABCMeta, abstactmethod
    class Section(metaclass=ABCMeta):
    @abstactmethod
    def describe(self):
    print("Personal Section")
    # Can't instantiate abstract class Section with abstract methods describe

工厂方法模式

  • 定义了一个接口创建对象,工厂本身不负责创建对象,将任务交由子类来完成,即子类决定了要实例化哪些类
  • Factory 方法的创建通过集成而不是通过实例化来完成的
  • 工厂方法使设计更加具有可定制型。可以返回相同的实例或子类,而不是某种类型的对象

优点:

  • 具有更大的灵活性,使得代码更加通用,因为不是单纯的实例化某个类
  • 松耦合的,因为创建对象的代码与使用它的代码是分开的。客户端完全不需要关心要传递那些参数以及需要实例化哪些类。

抽象工厂模式

提供一个接口来创建一系列相关对象,而无需指定具体的类。

  • 确保客户端与对象的创建相互隔离,同时还确保客户端能够使用创建的对象。
  • 客户端只能通过接口访问对象。

工厂方法与抽象工厂方法比较

工厂方法 抽象工厂方法
项客户端开方了创建对象的方法 抽象工厂方法包含一个或多个工厂方法来创建一系列的相关对象
使用继承和子类来决定要创建的对象 使用组合将创建对象的任务委托给其他类
用于创建一个产品 用于创建相关产品的系列

Facade (门面)设计模式

门面在隐藏内部系统复杂性的同时,为客户端提供了一个接口,使其可以非常轻松地访问系统

  • 为子系统中的一组接口提供一个统一的接口,并定义一个高级接口来帮助客户端通过更加简单的方式使用子系统
  • 解决的问题
    用单个接口对象来表示复杂的子系统。实际上,并不是封装子系统,而是对底层子系统进行组合
  • 促进了实现多个客户端的解耦

最少知识原则

减少对象之间的交互

  • 对于创建的每个对象,都应该考察与之交互的类的数量,以及交互的方式
  • 避免创建许多彼此紧密耦合的类

代理模式-控制对象的访问

代理通常就是一个介于寻求方和提供方之间的中介系统。代理服务器可以封装请求、保护隐私,并且非常适合在分布式架构中运行。

  • 主要目的为其他对象提供一个代理者或占位符,从而控制对实际对象访问

适用场景:

  • 以更简单的方式表示一个复杂的系统。

  • 提高现有的实际对象的安全性。

  • 为不同服务器上的远程对象提供本地接口。

  • 为消耗大量内存的对象提供了一个轻量级的句柄。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    class Actor(object):
    def __init__(self):
    self.is_busy = False
    def occupied(self):
    self.is_busy = True
    print(type(self).__name__, "is occupied with current movie")
    def available(self):
    print(type(self).__name__, "is free for the movie")
    def get_status(self):
    return self.is_busy
    class Agent(object):
    def __init__(self):
    self.principal = None
    def work(self):
    self.actor = Actor()
    if self.actor.get_status():
    self.actor.occupied()
    else:
    self.actor.available()
    r = Agent()
    r.work()

    Actor is free for the movie
    • 为其他对象提供了一个代理,从而实现了原始对象的访问控制
    • 可以用作一个层或接口,以支持分布式访问
    • 通过增加代理,保护真正的组件不受意外的影响

不同类型的代理

  • 虚拟代理
    • 对象实例化后会占用大量内存的话,可以先用占位符来表示,这就是所谓的虚拟代理。即仅在请求或访问对象时,才会创建实际对象。
  • 远程代理
    • 给位于远程服务器或不同地址空间上的实际对象提供了一个本地表示
  • 保护代理
    • 能够控制 **RealSubject(实际主题)**的敏感对象的访问
  • 智能代理
    • 在访问对象时插入其他操作

代理模式的优点

  • 可以通过缓存笨重的对象或频繁访问的对象来提高应用程序的性能
  • 提供对于真实主题的访问授权。因此,只有提供合适权限的情况下,这个模式才会接收委派
  • 远程代理便于与可用作网络连接和数据库连接的远程服务器进行交互,并可用于监视系统
代理模式 门面模式
为其他对象提供了代理或占位符,以控制对原始对象的控制 为类的大型子系统提供了一个接口
代理对象具有与其目标对象相同的接口,并保存有目标对象的引用 实现了子系统之间的通信和依赖性的最小化
充当客户端和被封装的对象之间的中介 门面对象提供了单一的简单接口

装饰器与代理模式的区别

  • 在运行时装饰器向被装饰的对象添加行为,代理是控制访问对象。代理和真实主题之间的关联是在编译时完成的,而不是动态的。

观察者模式

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

  • 应用中存在一个许多其他服务所依赖的核心服务,那么该核心服务将会变成观察者必须观察/监视其变化的主题。

主要目标:

  • 定义了对象间的一对多的依赖关系,从而使得一个对象中的任何更改都将自动通知给其他依赖对象

  • 封装了主题的核心组件

    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
    class Subject(object):
    def __init__(self):
    self.__observers = []
    def register(self, observer):
    self.__observers.append(observer)
    def notify_all(self, *args, **kwargs):
    for observer in self.__observers:
    observer.notify(self, *args, **kwargs)
    class Observer1(object):
    def __init__(self, subject):
    subject.register(self)
    def notify(self, subject, *args):
    print(type(self).__name__, "::Got",args, 'From', subject)
    class Observer2(object):
    def __init__(self, subject):
    subject.register(self)
    def notify(self, subject, *args):
    print(type(self).__name__, "::Got", args, 'From', subject)

    subject = Subject()
    observer1 = Observer1(subject)
    observer2 = Observer2(subject)
    subject.notify_all('notification')

    Observer1 ::Got ('notification',) From <__main__.Subject object at 0x000002784EFD0CF8>
    Observer2 ::Got ('notification',) From <__main__.Subject object at 0x000002784EFD0CF8>

    具体观察者(ConcreteObserver<observer1、observer2>)通过实现观察者(Observer<Observer1、Observer2>)提供的接口向主题注册自己。
    每当状态发生变化时,该主题(Subject)都会使用观察者提供的通知方法(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
    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    from abc import ABCMeta, abstractmethod


    class NewsPublisher(object):
    def __init__(self):
    self.__subscribers = []
    self.__latest_news = 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 notify_subscribers(self):
    for sub in self.__subscribers:
    sub.update()

    def add_news(self, news):
    self.__latest_news = news

    def get_news(self):
    return "Got News", self.__latest_news


    class MetaSubscriber(metaclass=ABCMeta):
    @abstractmethod
    def update(self):
    pass


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

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


    class SMSSubscriber(Subscriber):
    pass


    class AnyOtherSubscriber(Subscriber):
    pass


    class EmailSubscriber(Subscriber):
    pass


    if __name__ == '__main__':
    news_publisher = NewsPublisher()
    for subscriber in [SMSSubscriber, EmailSubscriber, AnyOtherSubscriber]:
    subscriber(news_publisher) # attach to publisher
    print("\nSubscribers:", news_publisher.subscribers()) # print all subscribers
    news_publisher.add_news('Hello world!') # add news to publisher
    news_publisher.notify_subscribers() # publish the news to subscriber
    print("\nDetached:", type(news_publisher.detach()).__name__)
    print("\nSubscribers:", news_publisher.subscribers())
    news_publisher.add_news("My second news!")
    news_publisher.notify_subscribers()

观察者模式的通知方式

通知观察者在主题中发生的变化

  • 推模型
  • 拉模型

拉模型

观察者扮演积极的角色

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

推模型

主题是主导作用的一方 * 变化由主题推送到观察者 * 在拉模型中,主题可以向观察者发送详细的信息(即使可能不需要)。当主题发送大量观察者用不到的数据时,会使响应时间过长。 * 只从主题发送所需的数据,能提高性能

松耦合与观察者模式

松耦合架构

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

观察者模式提供了实现主题和观察者松耦合的对象设计模式

  • 主题对观察者唯一的了解就是它实现一个特定的接口。同时,也不需要了解具体观察者类
  • 可以随时添加任意的新观察者
  • 添加新的观察者时,不需要修改主题
  • 观察者或主题没有绑定在一起,所以可以彼此独立使用。如果需要的话,观察者可以在任何地方重复使用
  • 主题或观察者中的变化不会相互影响

命令模式

对象用于封装在完成一项操作时或在触发一个事件时所需的全部信息。

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

术语:

  • 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
    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
    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    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(object):
    @staticmethod
    def buy():
    print("You will buy stocks")

    @staticmethod
    def sell():
    print("You will sell stocks")


    class Agent(object):
    def __init__(self):
    self.__order_queue = []

    def place_order(self, order):
    self.__order_queue.append(order)
    order.execute()


    if __name__ == '__main__':
    # Client
    stock = StockTrade()
    buy_stock = BuyStockOrder(stock)
    sell_stock = SellStockOrder(stock)
    # invoker
    agent = Agent()
    agent.place_order(buy_stock)
    agent.place_order(sell_stock)

应用命令模式的方式:

  • 重做或回滚操作
    • 在文件系统或内存中创建快照,当被要求回滚时,恢复到该快照
    • 使用命令模式时,可以存储命令序列,并且要求进行重做时,重新运行相同的一组操作即可
  • 异步任务执行
    • 分布式系统中,要求设备具备异步执行任务的功能,以便核心服务在大量请求时不会发生阻塞
    • 命令模式中,Invoker 对象可以维护一个请求列表,并将这些任务发送到 Receiver 对象,以便可以独立于主应用程序线程来完成相应的操作。

命令模式的优缺点

优点:

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

缺点:

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

模板方法模式–封装算法

适用场景:

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

模板方法设计模式

意图:

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

术语:

  • AbstractClass:声明一个定义算法步骤的接口

  • ConcreteClass:定义子类特定的步骤

  • template_method:通过调用步骤方法来定义算法

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


    class Compile(metaclass=ABCMeta):
    @abstractmethod
    def collect_source(self):
    pass

    @abstractmethod
    def compile_to_object(self):
    pass

    @abstractmethod
    def run(self):
    pass

    def compile_and_run(self):
    # template method,defines algorithm
    self.collect_source()
    self.compile_to_object()
    self.run()


    class IOSCompile(Compile):
    def collect_source(self):
    print("Collecting swift source cdoe")

    def compile_to_object(self):
    print("Compiling swift code to LLVM bitcode")

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


    if __name__ == '__main__':
    IOS = IOSCompile()
    IOS.compile_and_run()

示例:

  • 旅行社-行程套餐

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


    class Trip(metaclass=ABCMeta):
    """
    Abstract class for trip
    """
    @abstractmethod
    def set_transport(self):
    pass

    @abstractmethod
    def day_1(self):
    pass

    @abstractmethod
    def day_2(self):
    pass

    @abstractmethod
    def day_3(self):
    pass

    @abstractmethod
    def return_home(self):
    pass

    def itinerary(self):
    """
    the template method for trip
    :return:
    """
    self.set_transport()
    self.day_1()
    self.day_2()
    self.day_3()
    self.return_home()


    class VeniceTrip(Trip):
    """
    Concrete class for trip
    """
    def set_transport(self):
    print("Take a boat and find your way in the Grand Canal")

    def day_1(self):
    print("Visit St Mark's Basilica in St Mark's Square")

    def day_2(self):
    print("Appreciate Doge's Palace")

    def day_3(self):
    print("Enjoy the food near the Rialto Bridge")

    def return_home(self):
    print("Get souvenirs for friends and get back")


    class MaldivesTrip(Trip):
    def set_transport(self):
    print("On foot, on any island, Wow!")

    def day_1(self):
    print("Enjoy the marine life of Banana Reef")

    def day_2(self):
    print("Go for the water sports and snorkelling")

    def day_3(self):
    print("Relax on the beach and enjoy the sun")

    def return_home(self):
    print("Don't fell like leaving the beach...")


    class TravelAgency(object):
    def __init__(self):
    self.trip = None

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


    if __name__ == '__main__':
    TravelAgency().arrange_trip()

模板方法模式–钩子

钩子是在抽象类中声明的方法,通常被赋予一个默认实现。钩子背后的思想是为子类提供按需钩取算法的能力。
当子类必须提供实现时,使用抽象方法;当实现不是强制的时候,使用钩子。

模板方法模式的优缺点

优点:

  • 没有代码重复
  • 使用继承,能够对代码进行复用
  • 灵活性允许子类决定如何实现算法中的步骤

缺点:

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

模式-视图-控制器(MVC)–复合模式

模型提供数据和业务逻辑(如何存储和查询信息),视图负责数据的展示(如何呈现),而控制器是两者之间的粘合剂,根据用户要求的呈现方式来协调模型和视图。

术语:

  • 模型: 声明一个存储和操作数据的类
  • 视图: 声明一个类来创建用户界面和显示数据
  • 控制器: 声明一个连接模型和视图的类
  • 客户端: 声明一个类,根据某些操作来获得某些结果

意图:

  • 将数据和数据的展示隔离
  • 使类的维护和实现更加简单
  • 灵活的改变数据的存储和显示方法

适用场景:

  • 当需要更改展示方式而不更改业务逻辑
  • 多个控制器可用于使用多个视图来更改用户界面上的展示
  • 再次重申,当模型改变时,视图无需改动

模型–了解应用程序的情况

模型是应用程序的基石,因为它独立于视图和控制器,而视图和控制器则依赖于模型。

视图–外观

视图用来将数据展示在接口上,供客户查看。

控制器–胶水

控制用户在界面上的交互。

案例:

  • 消息通知服务

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


    class Model(object):
    services = {
    'email': {'number': 1000, 'price': 2},
    'sms': {'number': 1000, 'price': 10},
    'voice': {'number': 1000, 'price': 15},
    }


    class View(object):
    @staticmethod
    def list_services(services):
    for service in services:
    print('service: ', service, '')

    @staticmethod
    def list_pricing(services):
    for service in services:
    print("For", Model.services[service]['number'],
    service, "message you pay $", Model.services[service]['price'])


    class Controller(object):
    def __init__(self):
    self.models = Model()
    self.views = View()

    def get_services(self):
    services = self.models.services.keys()
    return self.views.list_services(services)

    def get_pricing(self):
    services = self.models.services.keys()
    return self.views.list_pricing(services)


    class Client(object):
    @staticmethod
    def main():
    controller = Controller()
    print("Services Provided")
    controller.get_services()
    print("Pricing for Services")
    controller.get_pricing()


    if __name__ == '__main__':
    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
from abc import ABCMeta, abstractmethod


class State(metaclass=ABCMeta):
@abstractmethod
def handle(self):
pass


class ConcreteStateB(State):
def handle(self):
print(type(self).__name__)


class ConcreteStateA(State):
def handle(self):
print(type(self).__name__)


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

def get_state(self):
return self.state

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

def handle(self):
self.state.handle()


if __name__ == '__main__':
context = Context()
state_a = ConcreteStateA()
state_b = ConcreteStateB()
context.set_state(state_a)
context.handle()

状态设计模式-简单示例

  • 电视遥控器

    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
    class ComputerState(object):
    name = "State"
    allowed = []

    def switch(self, state):
    if state.name in self.allowed:
    print("Current: ", self, '=> switching to new state', state.name)
    self.__class__ = state # change the current class
    else:
    print("Current: ", self, '=> switching to', state.name, 'not possible.')

    def __str__(self):
    return self.name


    class On(ComputerState):
    name = 'On'
    allowed = ['Off', 'Suspend', 'hibernate']


    class Off(ComputerState):
    name = 'Off'
    allowed = ['On']


    class Suspend(ComputerState):
    name = 'Suspend'
    allowed = ['On']


    class Hibernate(ComputerState):
    name = 'Hibernate'
    allowed = ['On']


    class Computer(object):
    def __init__(self):
    self.state = Off() # instantiate the Off to object

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


    if __name__ == '__main__':
    computer = Computer()
    # switch on
    computer.change(On)
    # switch of
    computer.change(Off)
    # switch on again
    computer.change(On)
    # suspend
    computer.change(Suspend)
    # try to hibernate - cannot!
    computer.change(Hibernate)
    # switch on back
    computer.change(On)
    # finally off
    computer.change(Off)

    类在未重写 __str__ 前,实例化后, self 的值是 类对象的引向,重写 __str__ 后,self 的值为其返回的信息 __class__ 是每个类 的内部属性,是对类的引用。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    class TmpTest(object):
    def get_self_info(self):
    print(self)

    t = TmpTest()
    t.get_self_info()
    <__main__.TmpTest object at 0x0000014A7A845780>

    class TmpTest(object):
    def get_self_info(self):
    print(self)
    def __str__(self):
    return 'test'

    a = TmpTest()
    a.get_self_info()
    test

状态模式的优缺点

优点:

  • 对象的行为是其状态的函数结果,行为在运行时根据状态而改变。消除了对 if/else 条件逻辑的依赖
  • 实现多态,易于添加状态来支持额外的行为
  • 提高了聚合性
  • 改善了扩展应用程序行为时的灵活性,全面提高了代码的可维护性

缺点:

  • 类爆炸: 需要创建许多功能单一的类,增加了代码量,使结构难以审查
  • 上下文行为容易受到每个新的行为的影响

反模式

不良设计的4个方面:

  • 不动性: 开发的应用程序非常难以重用
  • 刚性: 任何小的修改都会导致软件的太多部分必须进行相应的改动
  • 脆弱性: 任何更改都会导致现有系统变得非常容易崩溃
  • 粘滞性: 修改必须由开发人员在代码或环境本身中进行。