python设计模式

一、设计模式简介

参考链接:
https://github.com/BruceWW/python_design_pattern

设计模式本质上是对面向对象设计的实际运用,是对类的封装性,继承性和多态性以及类的关联关系和组合关系的充分理解,

  1. 提高思维、编程和设计能力
  2. 使程序设计更加标准化,代码编制更加工程化,使软件开发效率大大提高,从而缩短软件的开发周期
  3. 使设计的代码可重用性高,可读性强,可靠性高,灵活性好,可维护性强

1 面向对象设计原则

1)开放封闭原则

类或对象及其方法对于扩展来说,应该是开放的,对于修改来说,应该是封闭的。。
优点:有助于保持代码的向后兼容性

2)控制反转原则

高层级的模块不应该依赖于底层级的模块,依赖于抽象。该原则建议任何两个模块都不应该以紧密的方式相互依赖,基本模块和从属模块应该提供一个抽象层来耦合。
优点

  • 削弱了模块间的紧耦合,消除了系统中的复杂性/刚性
  • 依赖模块之间有一个明确的抽象层(由钩子或参数提供),因此便于通过更好的方式处理模块之间的依赖关系。

3)接口隔离原则

该原则规定,客户端不应该依赖他们不需要使用的接口。开发的方法要与特定的功能紧密联系。

存在与接口无关的方法,那么接口的类就必须实现他,实际上这是毫无必要的。

优点:

  • 强制开发人员写“瘦身型”接口,并使方法与接口紧密相关
  • 防止向接口中随意添加方法

4)单一职责原则

类的职责单一,引起类的变化的原因单一。
优点:

  • 每当一个功能发生变化时,特定的类需要改变之外,其他类无需变动。
  • 此外,如果一个类有很多功能,那么依赖它的类必定会修改由于多种原因多次修改,这是应该避免的。

5)替换原则

派生类必须能够完全取代基类

2 设计模式概念

1)设计模式的概念

设计模式就是解决特定问题的解决方案。设计模式在分析或设计阶段非常有用,并且如预期那样,在开发阶段也非常有用。

2)设计模式分类

不是每一种代码或每一种设计都可以叫做设计模式。解决一个问题的编程构造或数据结构就不能称为模式。

代码段:用某种语言编写的一段具有特定用途的代码。

设计:用来解决某个特定问题的优秀解决方案。

标准:这是一种解决某类问题的方法,它非常通用,并且适用于当前的情况。

模式:一个经过时间考验的、高效的、可扩展的解决方案。能解决一类已知问题。

根据目的来划分

a.创建型模式

用于描述“怎样创建对象”,它的主要特点是“将对象的创建与使用分离”。GoF 中提供了单例、原型、工厂方法、抽象工厂、建造者等 5 种创建型模式。

b.结构型模式

用于描述如何将类或对象按某种布局组成更大的结构,GoF 中提供了代理、适配器、桥接、装饰、外观、享元、组合等 7 种结构型模式。

c.行为模式

用于描述类或对象之间怎样相互协作共同完成单个对象都无法单独完成的任务,以及怎样分配职责。GoF 中提供了模板方法、策略、命令、职责链、状态、观察者、中介者、迭代器、访问者、备忘录、解释器等 11 种行为型模式。

根据作用范围来划分

根据模式主要用于类上还是主要用于对象来分,这种方式可分为类模式和对象模式两种

a.类模式

用于处理类与子类之间的关系,这些关系通过继承来建立,是静态的,在编译时刻便确定下来了。GoF中的工厂方法、(类)适配器、模板方法、解释器属于该模式。

b.对象模式

用于处理对象之间的关系,这些关系可以通过组合或聚合来实现,在运行时刻是可以变化的,更具动态性。GoF 中除了以上 4 种,其他的都是对象模式。

3)上下文——设计模式的适用性

上下文类型:

  • 参与者:在设计模式中用到的类。类可以在门面模式中扮演不同的角色,以完成多个目标。
  • 非功能需求:如内存优化、可用性和性能等需求,这类需求影响整个软件解决方案,很重要。
  • 权衡:并非所有设计模式都适合应用程序开发,需要权衡。
  • 结果:如果上下不合适,设计模式可能对代码的其他部分产生负面影响。

3 动态语言的设计模式

4 模式分类

1)创建型模型

  • 他们的运行机制基于对象的创建方式
  • 将对象创建的细节隔离开来
  • 代码与所创建的对象的类型无关

如:单例模式

2)结构型模式

性质:

  • 致力于设计出能够组合获得更强大功能的对象和类结构
  • 重点是简化结构并识别类和对象之间的关系
  • 主要关注类的继承和组合

适配器模式是结构型模式的一个例子。

3)行为型模式

性质:

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

如:观察者模式

二、创建型模式

1、单例模式

确保类有且只有一个特定的对象,并提供全局访问点。

适用情形:日志记录或数据库操作、打印机后台处理程序、读取配置文,以及其他程序——该程序运行过程中只能生成一个实例,避免对同一资源产生相互冲突的请求。计算机系统中,还有 Windows 的回收站、操作系统中的文件系统、多线程中的线程池、显卡的驱动程序对象、打印机的后台处理服务、应用程序的日志对象、数据库的连接池、网站的计数器、Web 应用的配置对象、应用程序中的对话框、系统中的缓存等常常被设计成单例。

只需要创建一个对象,如线程池、缓存、对话框、注册表、设置等

应用场景:

  • 需要频繁创建的一些类,使用单例可以降低系统的内存压力,减少 GC。
  • 某类只要求生成一个对象的时候,如一个班中的班长、每个人的身份证号等。
  • 某些类创建实例时占用资源较多,或实例化耗时较长,且经常使用。
  • 某类需要频繁实例化,而创建的对象又频繁被销毁的时候,如多线程的线程池、网络连接池等。
  • 频繁访问数据库或文件的对象。
  • 对于一些控制硬件级别的操作,或者从系统上来讲应当是单一控制逻辑的操作,如果有多个实例,则系统会完全乱套。
  • 当对象需要被共享的场合。由于单例模式只允许创建一个对象,共享该对象可以节省内存,并加快对象访问速度。如 Web 中的配置对象、数据库的连接池等。

当使用全局变量或类的实例化非常耗费资源但是最终却没有用到它们的情况下,单例的影响可以忽略不计。

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

简单的实现方法:使构造函数私有化,并创建一个静态方法来完成对象的初始化。对象将在第一次调用的时创建,此后这个类将返回同一个对象。

知识点:

  • 一个对象实例化过程先执行类__new__方法,没写,默认是object的__new__方法,返回一个实例化对象,然后调用__init__()方法,对这个对象进行初始化,在一个类的__new__方法中先判断是不是存在实例,存在直接返回。
# 使用类装饰器实现
class Singleton(object):
    def __init__(self, cls):
        self._cls = cls
        self._instance = {}
    def __call__(self):
        if self._cls not in self._instance:
            self._instance[self._cls] = self._cls()
        return self._instance[self._cls]

@Singleton
class Cls2(object):
    def __init__(self):
        pass

cls1 = Cls2()
cls2 = Cls2()
print(id(cls1) == id(cls2))
# 使用元类的方式
class Singleton(type):
    _instances = {}
    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances:
            cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
        return cls._instances[cls]

class Cls4(metaclass=Singleton):
    pass

cls1 = Cls4()
cls2 = Cls4()

__new__方法实现单例,python3中可以使用super().new(cls)代替super(A, cls).new(cls)

class A:
    def __new__(cls, *args, **kwargs):
        if not hasattr(cls, 'instance'):
            # cls.instance = super(A, cls).__new__(cls)
            cls.instance = super().__new__(cls)
        return cls.instance

单例模式中的懒汉式实例化

懒汉式实例化就能确保在实际需要的时候才创建对象,可以解决资源。

# 懒汉式单例模式,在b = B()时没有新的对象被创建,实际对象创建发生在调用B.getInstance()的时候
class B:
    __instance = None

    def __init__(self):
        if not B.__instance:
            print("__init__ method called..")
        else:
            print("Instance already created:", self.getInstance())

    @classmethod
    def getInstance(cls):
        if not cls.__instance:
            cls.__instance = B()
        return cls.__instance

b = B()
print(B.getInstance())
b1 = B()

# __init__ method called..
# __init__ method called..
# <__main__.B object at 0x00000260B6A95630>
# Instance already created: <__main__.B object at 0x00000260B6A95630>

模块级别的单例模式

默认情况下,所有的模块都是单例,这是python导入行为决定的。
当模块被导入的时候,它就会被初始化

Monostate单例模式

GoF的单例模式是指一个类有且只有一个对象,基于所有对象共享相同状态,因此它也被称为Monostate(单态)模式。

Monostate模式可以生成两个实例,但是对象状态,b.__dict__和b1.__dict__却是相同的。将__sh设置为空就可以。

# Monostate模式
class Borg:
    __sh = {"name": "Borg"}

    def __init__(self):
        self.x = 1
        self.__dict__ = self.__sh

b = Borg()
b1 = Borg()
b.x = 4

print(b == b1)
print(b.__dict__)
print(b1.__dict__)

通过__new__方法实现Borg模式,

# 通过修改__new__方法实现Borg模式
class Borg:
    _sh = {}

    def __new__(cls, *args, **kwargs):
        obj = super(Borg, cls).__new__(cls, *args, **kwargs)
        obj.__dict__ = cls._sh
        return obj

单例和元类

元类是一个类的类。也就是说该类是它元类的一个实例。类的定义由它的元类决定,python通过A=type(name, base, dict)创建它。
name:类的名称
base:这是基类
dict:这是属性变量

# 以下表明存在一个元类,因为int是type类型的类,
a = 5
print(type(a))
print(type(int))
# <class 'int'>
# <class 'type'>
# 元类的实现
class MyInt(type):
    def __call__(cls, *args, **kwargs):
        print(args)
        print("###########")
        return type.__call__(cls, *args, **kwargs)


class int(metaclass=MyInt):
    def __init__(self, x, y):
        self.x = x
        self.y = y


int(1, 2)

对于已经存在的类来说,需要创建对象时,将调用python的特殊方法__call__。实例化int类的时候元类的__call__方法将会被调用,元类控制着对象的实例化。

注意:为了控制类的创建和初始化,元类将覆盖__new____init__方法。

# 基于元类的单例模式实现
class MetaSingleton(type):
    _instances = {}

    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances:
            cls._instances[cls] = super().__call__(*args, **kwargs)
        return cls._instances[cls]


class Logger(metaclass=MetaSingleton):
    pass


log1 = Logger()
log2 = Logger()
print(log1, log2)

单例模式1

数据库操作的一致性,即一个操作不应与其他操作冲突。

优化数据库操作,提高内存和CPU的利用率。

数据库

单例模式2

单例模式的缺点

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

  • 全局变量可能在某处已经被修改了,但是开发人员认为还没有被修改
  • 可能会对同一对象创建多个引用,由于单例只有一个对象,因此这种情况下会对同一个对象创建多个引用。
  • 所有依赖于全局变量的类都会由于一个类的改变而紧密耦合为全局数据,从而可能在无意中影响另一个类。

2.工厂模式

凡是需要生成复杂对象的地方都可以尝试使用工厂模式来代替。

定义:定义一个创建产品对象的工厂接口,将产品对象的实际创建推迟到具体子工厂类中,满足创建于与使用分离的特点。

简单工厂模式

统一使用一个类作为对外的接口,根据参数的不同去实例化不同的类

"""
两个产品(两种类型的书)
"""


class TechnicalBooks(object):
    """技术书籍"""

    def publish(self):
        return "Python-Book"


class LiteraryBooks(object):
    """文学书籍"""

    def publish(self):
        return "Black Hole Book"


# 现在我们有两种类型的书,分别是TechnicalBooks和LiteraryBooks的书
# 按照我们平常的方法来实例化的话,此时创建对象时是会对客户端暴露真正创建的类
it_books = TechnicalBooks()
ly_books = LiteraryBooks()


# 这时我们就可以构造一个"简单工厂"把所有实例化的过程封装在里面,把真正实例的类隐藏起来
class SimpleFactory(object):
    """简单工厂"""

    @staticmethod
    def publish_book(name):
        if name == 'technical':
            return TechnicalBooks()
        elif name == 'literary':
            return LiteraryBooks()


it_books2 = SimpleFactory.publish_book('technical')
ly_books2 = SimpleFactory.publish_book('literary')

但是简单工厂也有一个缺点,那就是违背了solid的 "开闭原则",假如我们还需要增加一种书籍,那就必须要对简单工厂SimpleFactory进行源码的修改,

简单工厂使用场景:

  • 已经确定有多少具体的类,不会再增加的情况下使用。
    例如:某个系统,已经明确就只会有MySQL、Redis、MongoDB三个数据库的情况下,可以直接使用简单工厂模式。

工厂方法模式

在简单工厂的基础上把SimpleFactory抽象成不同的工厂,每个工厂对应生成自己的产品,这就是工厂方法。

"""
两个产品(两种类型的书)
"""
import abc


# 真正进行实例化的类
class TechnicalBooks(object):
    """技术书籍"""

    def publish(self):
        return "Python-Book"


class LiteraryBooks(object):
    """文学书籍"""

    def publish(self):
        return "Black Hole Book"


# 抽象工厂:先定义抽象类,然后每种类型的书籍都有自己对于的工厂
class AbstractFactory(metaclass=abc.ABCMeta):
    """抽象工厂"""

    @abc.abstractmethod
    def publish_book(self):
        pass


class TechnicalFactory(AbstractFactory):
    """技术书籍工厂"""

    def publish_book(self):
        return TechnicalBooks()


class LiteraryFactory(AbstractFactory):
    """文学书籍工厂"""

    def publish_book(self):
        return LiteraryBooks()


it_books2 = TechnicalFactory().publish_book()
ly_books2 = LiteraryFactory().publish_book()

这样每个工厂就只负责生产自己的产品,避免了在新增产品时需要修改工厂的代码,遵循了"开闭原则",如果需要新增产品时,只需要增加相应的工厂即可。

比如要新增一种小说类型的书籍,只需新增一个NovelBooks类和NovelFactory类。

工厂方法的使用场景:

当系统中拥有的子类很多,并且以后可能还需要不断拓展增加不同的子类时。
当设计系统时,还不能明确具体有哪些类时。
在工厂方法中,使用者不需要知道具体的产品类名,只需要知道其对应的工厂即可。

抽象工厂模式

工厂方法解决了"开闭原则"的问题,但是我们出版书籍之前肯定还会有其他的步骤,比如印刷。
如果每一个步骤我们就要写一个对应的工厂类,那我们就会需要创建很多很多类了。
因此为了解决这个问题,我们就要需要抽象工厂类,让一个工厂可以生产同一类的多个产品或多个动作(步骤),这就是抽象工厂。

"""
两个产品(两种类型的书)
"""
import abc


# 印刷书籍
class PrintingTechnicalBooks(object):
    """印刷技术书籍"""

    def printing(self):
        return "Print-Python-Book"


class PrintingLiteraryBooks(object):
    """印刷文学书籍"""

    def printing(self):
        return "Print Black Hole Book"


# 出版书籍
class TechnicalBooks(object):
    """出版技术书籍"""

    def publish(self):
        return "Python-Book"


class LiteraryBooks(object):
    """出版文学书籍"""

    def publish(self):
        return "Black Hole Book"


# 抽象工厂:先定义抽象类,然后每种类型的书籍都有自己对于的工厂
class AbstractFactory(metaclass=abc.ABCMeta):
    """抽象工厂"""

    @abc.abstractmethod
    def print_book(self):
        pass

    @abc.abstractmethod
    def publish_book(self):
        pass


class TechnicalFactory(AbstractFactory):
    """技术书籍工厂"""

    def print_book(self):
        return PrintingTechnicalBooks()

    def publish_book(self):
        return TechnicalBooks()


class LiteraryFactory(AbstractFactory):
    """文学书籍工厂"""

    def print_book(self):
        return PrintingLiteraryBooks()

    def publish_book(self):
        return LiteraryBooks()


# 实例化工厂对象
it = TechnicalFactory()
ly = LiteraryFactory()

# 印刷书籍
it_print = it.print_book()
ly_print = ly.print_book()
# 出版书籍
it_publish = it.publish_book()
ly_publish = ly.publish_book()

抽象工厂模式与工厂方法模式的区别:
抽象工厂中的一个工厂对象可以负责多个不同产品对象的创建

抽象工厂的使用场景:当多个产品(步骤)集合在一起,组成产品族时。对于一个产品族,如果只想显示接口而不是实现时。

3.生成器(建造者)模式

将一个复杂的对象的构建与表示分离,调用者只需要调用类中的方法即可一步步组装并在最后返回生成的对象。使用生成器使调用者不需要了解复杂对象的组装过程,生成器与抽象工厂非常类似,但是生成器着重于一步步组成,最后才返回生成的对象。

应用场景:

  • 使用生成器模式可避免 “重叠构造函数 (telescopic constructor)” 的出现。
  • 当希望使用代码创建不同形式的产品 (例如石头或木头房屋) 时, 可使用生成器模式。
  • 使用生成器构造组合树或其他复杂对象。
  • 调用方不需要知道这些对象之间怎么组合

优缺点:

  • 可以动态的改变一个对象的内部表示
  • 使得构造代码与实现代码分离
  • 由于每产生一种新的对象,都要生成一个新的类,导致子类过多

使用流程:

  • 1.创建公共手机类,表示你最终要创建的对象
  • 2.创建抽象类PhoneBuilder,定义了各种组装和使用的方法
  • 3.定义各种具体生成类,用来组装属性
  • 4.定义工厂类,装配各个组件
from abc import ABCMeta, abstractmethod


class Phone(object):
    """
    公共的手机类
    作为各种创建模式的创建目标
    存储了手机的os、cpu以及名称等属性
    为了简化模型,所有属性都采用字符串进行表示
    """

    def __init__(self):
        self._os = None
        self._cpu = None
        self._name = None

    @property
    def os(self) -> str:
        return self._os

    @os.setter
    def os(self, os: str) -> None:
        self._os = os

    @property
    def cpu(self) -> str:
        return self._cpu

    @cpu.setter
    def cpu(self, cpu: str) -> None:
        self._cpu = cpu

    @property
    def name(self) -> str:
        return self._name

    @name.setter
    def name(self, name: str) -> None:
        self._name = name

    def information(self) -> str:
        """
        返回手机信息
        :return:
        """
        return f"this is {self._name}, it's cpu is {self._cpu} and it's os is {self._os}"


class PhoneBuilder(metaclass=ABCMeta):
    def __init__(self):
        self._phone = Phone()

    @abstractmethod
    def add_cpu(self):
        pass

    @abstractmethod
    def add_os(self):
        pass

    @abstractmethod
    def add_name(self):
        pass

    def get_phone(self):
        return self._phone


class iPhoneBuilder(PhoneBuilder):
    """
    具体的iphone生成器
    """

    def add_os(self) -> None:
        """
        生成os,并装配到手机上
        :return:
        """
        self._phone.os = 'ios'

    def add_cpu(self) -> None:
        """
        生成cpu,并装配到手机上
        :return:
        """
        self._phone.cpu = 'a13'

    def add_name(self) -> None:
        """
        生成手机信息,并配置到手机上
        :return:
        """
        self._phone.name = 'iphone 11'


class SamsungS20Builder(PhoneBuilder):
    def add_os(self) -> None:
        self._phone.os = 'android'

    def add_cpu(self) -> None:
        self._phone.cpu = 'adreno650'

    def add_name(self) -> None:
        self._phone.name = 'samsung s20'


class HuaweiMate30Builder(PhoneBuilder):
    def add_os(self) -> None:
        self._phone.os = 'android'

    def add_cpu(self) -> None:
        self._phone.cpu = 'kirin 990'

    def add_name(self) -> None:
        self._phone.name = 'huawei 990'


class Foxconn(object):
    """
    以富士康为例,创建一个富士康类
    """

    @staticmethod
    def produce(phone_builder: PhoneBuilder.__subclasses__()):
        """
        静态方法,用于实例化生成器,并调用生成器的方法进行组装
        :param phone_builder: 手机的生成器类
        :return:
        """
        # 实例化一个手机生成器
        builder = phone_builder()
        # 装配os
        builder.add_os()
        # 装配cpu
        builder.add_cpu()
        # 配置手机信息
        builder.add_name()
        print(f'a phone was produced and the phone builder is {builder.__class__.__name__}')
        print(builder.get_phone().information())
        print()


if __name__ == '__main__':
    # 这里可以不进行实例化,直接调用静态方法
    foxconn_line = Foxconn()
    foxconn_line.produce(iPhoneBuilder)
    foxconn_line.produce(SamsungS20Builder)
    foxconn_line.produce(HuaweiMate30Builder)

4.原型模式

基于原型实例,通过拷贝进行对象的创建
适用:当一个对象初始化时,需要消耗大量资源或步骤比较繁杂
场景:python中的copy函数

优缺点:

  • 1.快速创建一个新的实例
  • 2.如果需要维护老类,则比较困难
from abc import ABCMeta, abstractmethod
from copy import deepcopy


class Phone(object):
    """
    公共的手机类
    作为各种创建模式的创建目标
    存储了手机的os、cpu以及名称等属性
    为了简化模型,所有属性都采用字符串进行表示
    """

    def __init__(self):
        self._os = None
        self._cpu = None
        self._name = None

    @property
    def os(self) -> str:
        return self._os

    @os.setter
    def os(self, os: str) -> None:
        self._os = os

    @property
    def cpu(self) -> str:
        return self._cpu

    @cpu.setter
    def cpu(self, cpu: str) -> None:
        self._cpu = cpu

    @property
    def name(self) -> str:
        return self._name

    @name.setter
    def name(self, name: str) -> None:
        self._name = name

    def information(self) -> str:
        """
        返回手机信息
        :return:
        """
        return f"this is {self._name}, it's cpu is {self._cpu} and it's os is {self._os}"


class Prototype(metaclass=ABCMeta):
    """
    原型对象使用的基类
    """

    def __init__(self, info: str):
        self.info = info

    @abstractmethod
    def clone(self):
        """
        原型对象使用的copy方法,需要用户进行实现
        :return:
        """
        pass


class Logo(Prototype):
    """
    logo类,继承了原型原型基类
    """

    def __init__(self, info):
        super(Logo, self).__init__(info)

    def clone(self):
        """
        实现clone方法
        :return:
        """
        # logo = Logo(self._info)
        # return logo
        return deepcopy(self)


class OS(Prototype):
    """
    os类,实现克隆基类
    """

    def __init__(self, info):
        super(OS, self).__init__(info)

    def clone(self):
        """
        实现clone方法
        :return:
        """
        # 返回一个os对象,被返回的对象与原始对象所有属性都一样,但是是两个不同的实例
        # os = OS(self._info)
        # return os
        # 建议使用python的copy方法
        return deepcopy(self)


class Foxconn(object):
    """
    以富士康为例,创建一个富士康类
    """

    @staticmethod
    def package(phone: Phone, os: OS, logo: Logo):
        """
        静态方法,用于给一个已经生产完的手机添加操作系统和logo
        :param phone:
        :param os:
        :param logo:
        :return:
        """
        # 为手机添加一份克隆的os,类似cv
        phone.os = os.clone()
        # 为手机添加一个克隆的logo贴纸,类似复印
        # 偷懒使用了猴子补丁给phone对象添加属性
        setattr(phone, 'logo', logo.clone())
        print('os and logo had been add to the phone')
        print(f"it's logo is {phone.logo.info}")
        print(f"it's os is {phone.os.info}")


def compare(obj1, obj2):
    print(f"the info of original obj is '{obj1.info}', and the info of obj on phone is '{obj2.info}'")
    print(f"the id of original obj is '{id(obj1)}', and the id of obj on phone is '{id(obj2)}'")


if __name__ == '__main__':
    foxconn = Foxconn()
    # 实例化一部手机
    phone1 = Phone()
    # 实例化一个iOS的系统
    os1 = OS('ios')
    # 实例化一个苹果的图标
    logo1 = Logo('apple')
    # 实例化另一部手机
    phone2 = Phone()
    # 实例化一个安卓系统
    os2 = OS('andriod')
    # 实例化一个三星的图标
    logo2 = Logo('samsung')
    # 在工厂中,给手机1装上os,并贴上图标
    foxconn.package(phone1, os1, logo1)
    # 比较原来的系统与被装上的系统之间的差别
    compare(os1, phone1.os)
    compare(logo1, phone1.logo)
    foxconn.package(phone2, os2, logo2, )
    compare(os2, phone2.os)
    compare(logo2, phone2.logo)

三、结构性模式

代理、适配器、桥接、装饰、外观、享元、组合

1.代理模式

内容:为其他对象提供一种代理以控制对这个对象的访问。
应用场景:

  • 远程代理:为远程的对象提供代理
  • 虚代理:根据需要创建很大的对象
  • 保护代理:控制对原始对象的访问,用于对象有不同访问权限时

角色:

  • 抽象实体(Subject)
  • 实体(RealSubject)
  • 代理(Proxy)

优点:

  • 远程代理:可以隐藏对象位于远程地址空间的事实
  • 虚代理:可以进行优化,例如根据要求创建对象
  • 保护代理:允许在访问一个对象时有一些附加的内务处理

缺点:

  • 造成了系统中类的数量增加
  • 增加了代理对象,会导致请求处理速度变慢
  • 增加了系统的复杂度
from abc import ABCMeta, abstractmethod


class Subject(metaclass=ABCMeta):
    @abstractmethod
    def get_content(self):
        pass

    @abstractmethod
    def set_content(self, content):
        pass


class RealSubject(Subject):
    def __init__(self, filename):
        self.filename = filename
        f = open(filename, 'r', encoding='utf-8')
        print("读取文件内容")
        self.content = f.read()
        f.close()

    def get_content(self):
        return self.content

    def set_content(self, content):
        f = open(self.filename, 'w', encoding='utf-8')
        f.write(content)
        f.close()


class VirtualProxy(Subject):
    """虚代理"""
    def __init__(self, filename):
        self.filename = filename
        self.subj = None

    def get_content(self):
        if not self.subj:
            self.subj = RealSubject(self.filename)
        return self.subj.get_content()

    def set_content(self, content):
        if not subj:
            self.subj = RealSubject(self.filename)
        return self.subj.set_content(content)


class ProtectedProxy(Subject):
    def __init__(self, filename):
        self.subj = RealSubject(filename)

    def get_content(self):
        return self.subj.get_content()

    def set_content(self, content):
        raise PermissionError("无写入权限")


# subj = RealSubject("test.txt")
# subj.get_content()

subj = ProtectedProxy("test.txt")
print(subj.get_content())
subj.set_content("abc")

2.适配器模式

内容:将一个类的接口转换成客户希望的另一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
两种实现方式:

  • 类适配器:使用多继承
  • 对象适配器:使用组合

角色:

  • 目标接口(Target)
  • 待适配的类(Adaptee)
  • 适配器(Adapter)

适用场景:

  • 想使用一个已经存在的类,而它的接口不符合你的要求
  • (对象适配器)想使用一些已经存在的子类,但不可能对每一个都进行子类化以匹配它们的接口。对象适配器可以适配它的父类接口。
from abc import ABCMeta, abstractmethod


class Payment(metaclass=ABCMeta):

    @abstractmethod
    def pay(self, money):
        pass


class Alipay(Payment):
    def pay(self, money):
        print("支付宝支付%d元" % money)


class WechatPay(Payment):
    def pay(self, money):
        print("微信支付%d元" % money)


class BankPay:
    def cost(self, money):
        print("银联支付%d元" % money)


# 类适配器
class NewBankPay(Payment, BankPay):
    def pay(self, money):
        self.cost(money)


# 对象适配器
class PaymentAdapter(Payment):
    def __init__(self, payment):
        self.payment = payment

    def pay(self, money):
        self.payment.cost(money)


p = PaymentAdapter(BankPay())
p.pay(100)

3.桥接模式

内容:将一个事物的两个维度分离,使其都可以独立地变化

角色:

  • 抽象(Abstraction)
  • 细化抽象(RefinedAbstraction)
  • 实现者(Implementor)
  • 具体实现者(ConcreteImplementor

应用场景:

  • 当事物有两个维度上的表现,两个维度都可能扩展时。

优点:

  • 抽象和实现相分离
  • 优秀的扩展能力
from abc import ABCMeta, abstractmethod


class Shape(metaclass=ABCMeta):
    def __init__(self, color):
        self.color = color

    @abstractmethod
    def draw(self):
        pass


class Color(metaclass=ABCMeta):
    @abstractmethod
    def paint(self, shape):
        pass


class Rectangle(Shape):
    name = "长方形"

    def draw(self):
        # 长方形逻辑
        self.color.paint(self)


class Circle(Shape):
    name = "圆形"

    def draw(self):
        # 圆形逻辑
        self.color.paint(self)


class Line(Shape):
    name = "直线"

    def draw(self):
        # 直线逻辑
        self.color.paint(self)


class Red(Color):
    def paint(self, shape):
        print("红色的%s" % shape)


class Green(Color):
    def paint(self, shape):
        print("绿色的%s" % shape.name)


class Blue(Color):
    def paint(self, shape):
        print("蓝色的%s" % shape.name)


shape = Line(Blue())
shape.draw()


shape2 = Circle(Green())
shape2.draw()

4.装饰模式

5.外观模式

内容:为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
角色:

  • 外观(facade)
  • 子系统类(subsystem classes)
class CPU:
    def run(self):
        print("CPU开始运行")

    def stop(self):
        print("CPU停止运行")


class Disk:
    def run(self):
        print("硬盘开始工作")

    def stop(self):
        print("硬盘停止工作")


class Memory:
    def run(self):
        print("内存通电")

    def stop(self):
        print("内存断电")


class Computer: # Facade
    def __init__(self):
        self.cpu = CPU()
        self.disk = Disk()
        self.memory = Memory()

    def run(self):
        self.cpu.run()
        self.disk.run()
        self.memory.run()

    def stop(self):
        self.cpu.stop()
        self.disk.stop()
        self.memory.stop()


# Client

computer = Computer()
computer.run()
computer.stop()

6.享元模式

7.组合模式

内容:将对象组合成树形结构以表示“部分-整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。
角色:

  • 抽象组件(Component)
  • 叶子组件(Leaf)
  • 复合组件(Composite)
  • 客户端(Client)

适用场景:

  • 表示对象的“部分-整体”层次结构(特别是结构是递归的)
  • 希望用户忽略组合对象与单个对象的不同,用户统一地使用组合结构中的所有对象

优点:

  • 定义了包含基本对象和组合对象的类层次结构
  • 简化客户端代码,即客户端可以一致地使用组合对象和单个对象
  • 更容易增加新类型的组件
from abc import ABCMeta, abstractmethod


# 抽象组件
class Graphic(metaclass=ABCMeta):
    @abstractmethod
    def draw(self):
        pass


# 叶子组件
class Point(Graphic):
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __str__(self):
        return "点(%s, %s)" % (self.x, self.y)

    def draw(self):
        print(str(self))


# 叶子组件
class Line(Graphic):
    def __init__(self, p1, p2):
        self.p1 = p1
        self.p2 = p2

    def __str__(self):
        return "线段[%s, %s]" % (self.p1, self.p2)

    def draw(self):
        print(str(self))


# 复合组件
class Picture(Graphic):
    def __init__(self, iterable):
        self.children = []
        for g in iterable:
            self.add(g)

    def add(self, graphic):
        self.children.append(graphic)

    def draw(self):
        print("----复合图形----")
        for g in self.children:
            g.draw()
        print("----复合图形----")


p1 = Point(2, 3)
l1 = Line(Point(3, 4), Point(6, 7))
l2 = Line(Point(1, 5), Point(2, 8))

pic1 = Picture([p1, l1, l2])

p2 = Point(4, 4)
l3 = Line(Point(1, 1), Point(0, 0))
pic2 = Picture([p2, l3])


pic = Picture([pic1, pic2])
pic.draw()
"""
三层复合图形
----复合图形----
----复合图形----
点(2, 3)
线段[点(3, 4), 点(6, 7)]
线段[点(1, 5), 点(2, 8)]
----复合图形----
----复合图形----
点(4, 4)
线段[点(1, 1), 点(0, 0)]
----复合图形----
----复合图形----
"""

四、行为型模式

1.责任链模式

内容:使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。
角色:

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

适用场景:

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

优点:

  • 降低耦合度:一个对象无需知道是其他哪一个对象处理其请求
from abc import ABCMeta, abstractmethod


class Handler(metaclass=ABCMeta):
    @abstractmethod
    def handle_leave(self, day):
        pass


class GeneralManager(Handler):
    def handle_leave(self, day):
        if day <= 10:
            print("总经理准假%d天" % day)
        else:
            print("你还是辞职吧")


class DepartmentManager(Handler):
    def __init__(self):
        self.next = GeneralManager()

    def handle_leave(self, day):
        if day <= 5:
            print("部门经理准假%s天" % day)
        else:
            print("部门经理职权不足")
            self.next.handle_leave(day)


class ProjectDirector(Handler):
    def __init__(self):
        self.next = DepartmentManager()

    def handle_leave(self, day):
        if day <= 3:
            print("项目主管准假%d天" % day)
        else:
            print("项目主管职权不足")
            self.next.handle_leave(day)


# Client
day = 12
h = ProjectDirector()
h.handle_leave(day)

2.观察者模式

观察者模式也叫发布订阅模式,定义了对象之间一对多依赖,当一个对象改变状态时,这个对象的所有依赖者都会收到通知并按照自己的方式进行更新。

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

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

两个示例:

class Subject(object):
    def __init__(self):
        self.obj_server = []
        self.__topic = ''

    def add_obj(self, obj):
        if obj not in self.obj_server:
            self.obj_server.append(obj)

    def remove(self, obj):
        self.obj_server.remove(obj)

    @property
    def topic(self):
        return self.__topic

    @topic.setter
    def topic(self, topic):
        self.__topic = topic
        self.notify()

    def notify(self):
        for obj in self.obj_server:
            obj.update(self.topic)


class Ob(object):
    def __init__(self, name):
        self.name = name
        self.topic = None

    def update(self, topic):
        self.topic = topic
        print('{0}更新: {1}'.format(self.name, self.topic))


sub = Subject()

a = Ob('A')
a2 = Ob('A2')
a3 = Ob('A3')

sub.add_obj(a)
sub.add_obj(a2)
sub.add_obj(a3)
sub.topic = 'topic 1'
sub.remove(a2)
sub.topic = 'topic 2'
class Watcher:
    def __init__(self, id, name):
        self.id = id
        self.name = name

    def send(self, msg):
        print(str(self.name) + "-" + str(self.id) + "recive the message is: " + msg)


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

    def add_queue(self, sub):
        self.queues.append(sub)
        return self.queues

    def remove_queue(self, sub):
        self.queues.remove(sub)

    def notice(self, msg):
        for queue in self.queues:
            queue.send(msg)


tom = Watcher(1001, "tom")
tony = Watcher(1002, "tony")
jack = Watcher(1003, "jack")

weather = Subject()
weather.add_queue(tom)
weather.add_queue(tony)

military = Subject()
military.add_queue(tony)
military.add_queue(jack)

weather.notice("it's rain")
military.notice("it's peace")

#将tony从weather and military主题中取消订阅
weather.remove_queue(tony)
military.remove_queue(tony)

#取消订阅后给剩下的订阅者发布消息
weather.notice("it's windy")
military.notice("it's war")

3.策略模式

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

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

优点:

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

缺点:

  • 客户必须了解不同的策略
from abc import ABCMeta,abstractmethod

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


class FastStrategy(Strategy):
    def execute(self, data):
        print("用较快的策略处理%s" % data)


class SlowStrategy(Strategy):
    def execute(self, data):
        print("用较慢的策略处理%s" % data)


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

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

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


# Client

data = "[...]"
s1 = FastStrategy()
s2 = SlowStrategy()
context = Context(s1, data)
context.do_strategy()
context.set_strategy(s2)
context.do_strategy()

4.模板方法模式

内容:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
角色:

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

适用场景:

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


class Window(metaclass=ABCMeta):
    @abstractmethod
    def start(self):
        pass

    @abstractmethod
    def repaint(self):
        pass

    @abstractmethod
    def stop(self): # 原子操作/钩子操作
        pass

    def run(self):  # 模板方法
        self.start()
        while True:
            try:
                self.repaint()
                sleep(1)
            except KeyboardInterrupt:
                break
        self.stop()


class MyWindow(Window):
    def __init__(self, msg):
        self.msg = msg

    def start(self):
        print("窗口开始运行")

    def stop(self):
        print("窗口结束运行")

    def repaint(self):
        print(self.msg)


MyWindow("Hello...").run()
posted @ 2022-11-21 21:59  pandaZzz  阅读(22)  评论(0)    收藏  举报
返回顶部