Python常用的设计模式(下)

简介: Python常用的设计模式(下)

正文


3.6. 创建型模式概述


抽象工厂模式和建造者模式相比于简单工厂模式和工厂方法模式而言更加灵活也更加复杂。通常情况下,软件设计以简单工厂模式或工厂方法模式开始,当发现设计需要更大的灵活性的时候,则向更加复杂的设计模式演化。


4. 结构型模式


4.1. 适配器模式


     将一个类的接口转换成客户希望的另外一个接口,适配器使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。实现适配器的两种方式,类适配器使用多继承,对象适配器使用组合组合就是一个类中放入另一类的对象。 先来看下组合:


class A:
  pass
class B:
  def __init__():
    self.a = A()

类适配器模式使用示例:

# 类适配器模式使用示例:
from abc import ABCMeta, abstractmethod
# 目标接口
class Payment(object, metaclass=ABCMeta):
    @abstractmethod
    def pay(self, money):
        pass
class Alipay(Payment):
    def pay(self, money):
        print('支付了%d' % money)
# 待适配的类
class BankPay():
    def cost(self, money):
        print('银联支付了%d' % money)
# 类适配器
class PaymentAdapter(Payment, BankPay):
    """
    把不兼容cost转换成pay
    """
    def pay(self, money):
        self.cost(money)
p = PaymentAdapter()
p.pay(100)
"""
银联支付了100
"""

对象适配器模式使用示例:

# 类适配器模式使用示例:
from abc import ABCMeta, abstractmethod
# 目标接口
class Payment(object, metaclass=ABCMeta):
    @abstractmethod
    def pay(self, money):
        pass
class Alipay(Payment):
    def pay(self, money):
        print('支付了%d' % money)
# 待适配的类
class BankPay():
    def cost(self, money):
        print('银联支付了%d' % money)
# 待适配的类
class ApplePay():
    def cost(self, money):
        print('苹果支付了%d' % money)
# 对象适配器
class PaymentAdapter(Payment):
    def __init__(self, payment):
        self.payment = payment
    def pay(self, money):
        self.payment.cost(money)
p = PaymentAdapter(ApplePay())
p.pay(100)
p = PaymentAdapter(BankPay())
p.pay(100)
"""
苹果支付了100
银联支付了100
"""


    适配器模式有三种角色,分别是目标接口、待适配的类和适配器。适用场景是:想使用一个已存在的类,而它的接口不符合你的要求。想使用一些已经存在的类,但是不可能对每一个都进行子类化以匹配它们的接口。对象适配器可以适配它的父类接口。


4.2. 桥模式


     桥模式是将一个事物的两个维度分离,使其都可以独立地变化。当事物有两个维度的表现,两个维度都可能扩展时使用。优点是:抽象和实现相分离,扩展能力强。如果不使用桥模式,在任何维度进行扩展,需要改好多代码,因为使用到了继承:

class Shape:
  pass
class Rectangle(Shape):
  pass
class Circle(Shape):
  pass
class RedRectangle(Rectangle):
  pass
class GreenRectangle(Rectangle):
  pass
class RedCircle(Circle):
  pass
class GreenCircle(Circle):
  pass


  以上代码形状和颜色两个维度是通过类的继承关系紧密结合在一起,是紧耦合。紧耦合是是不可取的,应用桥模式的思想,可以使用组合来实现(松耦合)。如果需要画直线,直接加上直线的类。需要新颜色,直接加上颜色的类。两个维度都可以自由扩展,不需要添加很多代码。这里的角色有抽象、细化抽象、实现者和具体实现者:

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 Red(Color):
    def paint(self, shape):
        print('画红色的%s' % shape.name)
# 如果要扩展颜色,只需要添加颜色类
class Green(Color):
    def paint(self, shape):
        print('画绿色的%s' % shape.name)
rectangle = Rectangle(Red())
rectangle.draw()
circle = Circle(Green())
circle.draw()
"""
画红色的长方形
画绿色的圆形
"""


4.3. 组合模式


     将对象组合成树形结构以表示“部分-整体”的层次结构(特别是结构是递归的),组合模式使得用户对单个对象和组合对象的使用具有一致性。优点是定义了包含基本对象和组合对象的层次结构;简化客户端代码,客户端可以一致地使用组合对象和单个对象;更加容易增加新类型的组件。

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(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(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):
        for g in self.children:
            g.draw()
# 简单图形
print('------简单图形------')
p = Point(1, 2)
l1 = Line(Point(1, 2), Point(3, 4))
l2 = Line(Point(5, 6), Point(7, 8))
print(p)
print(l1)
print(l2)
print('------复合图形(p,l1,l2)------')
# 复合图形
pic = Picture([p, l1, l2])
pic.draw()


4.4. 外观模式


     外观模式为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层的接口,这个接口使得这一子系统更加容易使用。外观模式下的角色有外观和子系统类,优点是:减少系统相互依赖,提高灵活性,提高了安全性。下面看一个例子:

# 子系统类
class CPU:
    def run(self):
        print('CPU start to run...')
    def stop(self):
        print('CPU stop to run...')
# 子系统类
class Disk:
    def run(self):
        print('Disk start to run...')
    def stop(self):
        print('Disk stop to run...')
# 子系统类
class Memory:
    def run(self):
        print('Memory start to run...')
    def stop(self):
        print('Memory stop to run...')
# 外观
class Computer():
    def __init__(self):
        self.CPU = CPU()
        self.Disc = Disk()
        self.Member = Memory()
    def run(self):
        self.CPU.run()
        self.Disc.run()
        self.Member.run()
    def stop(self):
        self.CPU.stop()
        self.Disc.stop()
        self.Member.stop()
# 客户端,高层代码
c = Computer()
c.run()
c.stop()


4.5. 代理模式


     为其它对象提供一种代理以控制对这个对象的访问。角色有抽象实体、实体和代理。应用场景有远程代理:为远程的对象提供代理(通过ORM向数据库写值,不用关注数据库是在远程);虚代理:根据需要创建很大的对象(需要的时候创建对象);保护代理:控制对原始对象的访问,用于对象有不同的访问权限。下面是不使用虚代理的例子:

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
        print('读取文件内容!')
        with open(self.filename, 'r', encoding='utf-8') as f:
            self.content = f.read()
    def get_content(self):
        return self.content
    def set_content(self, content):
        with open(self.filename, 'w', encoding='utf-8') as f:
            f.write(content)
subj = RealSubject('test.txt')
"""
读取文件内容!
"""


使用虚代理的例子:

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
        print('读取文件内容!')
        with open(self.filename, 'r', encoding='utf-8') as f:
            self.content = f.read()
    def get_content(self):
        return self.content
    def set_content(self, content):
        with open(self.filename, 'w', encoding='utf-8') as f:
            f.write(content)
class ProtectedSubject(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 = ProtectedSubject('test.txt')
print(subj.get_content())
subj.set_content('abc')
"""
读取文件内容!
test file!
Traceback (most recent call last):
  File "/home/thanlon/projects/PycharmProjects/untitled/代理模式.py", line 42, in <module>
    subj.set_content('abc')
  File "/home/thanlon/projects/PycharmProjects/untitled/代理模式.py", line 37, in set_content
    raise PermissionError('无写入权限!')
PermissionError: 无写入权限!
"""



     不使用虚代理,只要是实例化 RealSubject 类,就会读取这个文件占用内存。使用虚代理后,可以和根据需要创建对象,用户不调用是不会创建 RealSubject 对象的,节省了内存的开销。如果需要只有读的权限而没有写的权限,可以使用保护代理:


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
        print('读取文件内容!')
        with open(self.filename, 'r', encoding='utf-8') as f:
            self.content = f.read()
    def get_content(self):
        return self.content
    def set_content(self, content):
        with open(self.filename, 'w', encoding='utf-8') as f:
            f.write(content)
class ProtectedSubject(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 = ProtectedSubject('test.txt')
print(subj.get_content())
subj.set_content('abc')
"""
读取文件内容!
test file!
Traceback (most recent call last):
  File "/home/thanlon/projects/PycharmProjects/untitled/代理模式.py", line 42, in <module>
    subj.set_content('abc')
  File "/home/thanlon/projects/PycharmProjects/untitled/代理模式.py", line 37, in set_content
    raise PermissionError('无写入权限!')
PermissionError: 无写入权限!
"""


5. 行为型模式


5.1. 责任链模式


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

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 <= 30:
            print('总经理准假%d' % day)
        else:
            print('可以辞职了!')
# 具体的处理者
class DepartmentManager(Handler):
    def __init__(self):
        self.next = GeneralManager()
    def handle_leave(self, day):
        if day <= 7:
            print('项目主管准假%d' % 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)
day = 20
p = ProjectDirector()
p.handle_leave(day)
"""
项目主管职权不足
部门经理职权不足
总经理准假20
"""


使用场景:有多个对象可以处理一个请求,哪个对象处理由运行时决定;在不明确接收者的情况下,向多个对象中的一个提交一个请求。优点是降低耦合度,一个对象无需知道是其它哪一个对象处理其请求。


5.2. 观察者模式


观察者模式应用比较广泛,又被称为“发布-订阅”模式。它用来定义对象间一种一对多的依赖关系,当一个对象的状态发生变化时,所有依赖它的对象都得到通知并被自动更新。观察者模式的角色有:抽象主题、具体主题(发布者)、抽象观察者和具体观察者(订阅者)。

from abc import ABCMeta, abstractmethod
# 抽象的订阅者
class Observer(metaclass=ABCMeta):
    @abstractmethod
    def update(self, notice):
        """
        :param notice: Notice类的对象
        :return:
        """
        pass
# 抽象的发布者:可以是接口,子类不需要实现,所以不需要定义抽象方法!
class Notice:
    def __init__(self):
        self.observers = []
    def attach(self, obs):
        self.observers.append(obs)
    def detach(self, obs):
        self.observers.remove(obs)
    def notify(self):
        """
        推送
        :return:
        """
        for obs in self.observers:
            obs.update(self)
# 具体的发布者
class StaffNotice(Notice):
    def __init__(self, company_info):
        super().__init__()  # 调用父类对象声明observers属性
        self.__company_info = company_info
    @property
    def company_info(self):
        return self.__company_info
    @company_info.setter
    def company_info(self, info):
        self.__company_info = info
        self.notify()
# 具体的订阅者
class Staff(Observer):
    def __init__(self):
        self.company_info = None
    def update(self, notice):
        self.company_info = notice.company_info
staff_notice = StaffNotice('初始化公司信息')
staff1 = Staff()
staff2 = Staff()
staff_notice.attach(staff1)
staff_notice.attach(staff2)
# print(staff1.company_info) None
# print(staff2.company_info) None
staff_notice.company_info = '假期放假通知!'
print(staff1.company_info)
print(staff2.company_info)
staff_notice.detach(staff2)
staff_notice.company_info = '明天开会!'
print(staff1.company_info)
print(staff2.company_info)
"""
假期放假通知!
假期放假通知!
明天开会!
假期放假通知!
"""

使用场景:当一个抽象模型有两个方面,其中一个方面依赖另一个方面。将这两者封装在独立对象中以使它们可以各自独立地改变和复用;当对一个对象的改变需要同时改变其它对象,而不知道具体有多少对象待改变;当一个对象必须通知其它对象,而它又不能假定其它对象是谁。换言之,你不希望这些对象是紧耦合的。优点:目标和观察者之间的抽象耦合最小;支持广播通信。


5.3. 策略模式


定义一个个算法,把它们封装起来,并且使它们可以相互替换。本模式使得算法可独立于使用它的客户而变化。角色有:抽象策略、具体策略和上下文。

from abc import abstractmethod, ABCMeta
from datetime import datetime
# 抽象策略
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
        # 可以定义用户不知道的东西
        self.date = datetime.now()
    def set_strategy(self, strategy):
        self.strategy = strategy
    def do_strategy(self):
        self.strategy.execute(self.data)
data = "Hello!"
# 使用较快的策略处理
fast_strategy = FastStrategy()
context = Context(fast_strategy, data)
context.do_strategy()
# 使用较慢的策略处理
slow_strategy = SlowStrategy()
context = Context(slow_strategy, data)
context.do_strategy()
"""
使用较快的策略处理Hello!
使用较慢的策略处理Hello!
"""

优点:定义了一些列可重用的算法和行为;消除了一些条件语句;可以提供相同行为的不同实现;缺点:客户必须了解不同的策略。


5.4. 模板方法模式


内容:定义一个操作中的算法骨架,将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。使用模板方法,需要用到两种角色,分别是抽象类和具体类。抽象类的作用是是定义抽象类(钩子操作),实现一个模板方法作为算法的骨架。具体类的作用实现原子操作。

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):
        """
        模板方法(具体方法),这个大逻辑就不需要自己写了
        :return:
        """
        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()

模板方法适用的场景:一次性实现一个算法的不变部分,各个子类中的公共行为应该被提取出来并集中到一个公共父类中以避免代码重复;控制子类扩展。

相关文章
|
16天前
|
设计模式 开发者 Python
Python编程中的设计模式:工厂方法模式###
本文深入浅出地探讨了Python编程中的一种重要设计模式——工厂方法模式。通过具体案例和代码示例,我们将了解工厂方法模式的定义、应用场景、实现步骤以及其优势与潜在缺点。无论你是Python新手还是有经验的开发者,都能从本文中获得关于如何在实际项目中有效应用工厂方法模式的启发。 ###
|
4天前
|
设计模式 算法 搜索推荐
Python编程中的设计模式:优雅解决复杂问题的钥匙####
本文将探讨Python编程中几种核心设计模式的应用实例与优势,不涉及具体代码示例,而是聚焦于每种模式背后的设计理念、适用场景及其如何促进代码的可维护性和扩展性。通过理解这些设计模式,开发者可以更加高效地构建软件系统,实现代码复用,提升项目质量。 ####
|
18天前
|
设计模式 监控 数据库连接
Python编程中的设计模式之美:提升代码质量与可维护性####
【10月更文挑战第21天】 一段简短而富有启发性的开头,引出文章的核心价值所在。 在编程的世界里,设计模式如同建筑师手中的蓝图,为软件的设计和实现提供了一套经过验证的解决方案。本文将深入浅出地探讨Python编程中几种常见的设计模式,通过实例展示它们如何帮助我们构建更加灵活、可扩展且易于维护的代码。 ####
|
26天前
|
设计模式 开发者 Python
Python编程中的设计模式:从入门到精通####
【10月更文挑战第14天】 本文旨在为Python开发者提供一个关于设计模式的全面指南,通过深入浅出的方式解析常见的设计模式,帮助读者在实际项目中灵活运用这些模式以提升代码质量和可维护性。文章首先概述了设计模式的基本概念和重要性,接着逐一介绍了几种常用的设计模式,并通过具体的Python代码示例展示了它们的实际应用。无论您是Python初学者还是经验丰富的开发者,都能从本文中获得有价值的见解和实用的技巧。 ####
|
23天前
|
设计模式 开发者 Python
Python编程中的设计模式应用与实践###
【10月更文挑战第18天】 本文深入探讨了Python编程中设计模式的应用与实践,通过简洁明了的语言和生动的实例,揭示了设计模式在提升代码可维护性、可扩展性和重用性方面的关键作用。文章首先概述了设计模式的基本概念和重要性,随后详细解析了几种常用的设计模式,如单例模式、工厂模式、观察者模式等,在Python中的具体实现方式,并通过对比分析,展示了设计模式如何优化代码结构,增强系统的灵活性和健壮性。此外,文章还提供了实用的建议和最佳实践,帮助读者在实际项目中有效运用设计模式。 ###
13 0
|
26天前
|
设计模式 存储 数据库连接
Python编程中的设计模式之美:单例模式的妙用与实现###
本文将深入浅出地探讨Python编程中的一种重要设计模式——单例模式。通过生动的比喻、清晰的逻辑和实用的代码示例,让读者轻松理解单例模式的核心概念、应用场景及如何在Python中高效实现。无论是初学者还是有经验的开发者,都能从中获得启发,提升对设计模式的理解和应用能力。 ###
|
2月前
|
设计模式
python24种设计模式
python24种设计模式
|
3月前
|
设计模式 XML 数据格式
python之工厂设计模式
python之工厂设计模式
python之工厂设计模式
|
4月前
|
设计模式 机器学习/深度学习 测试技术
设计模式转型:从传统同步到Python协程异步编程的实践与思考
【7月更文挑战第15天】探索从同步到Python协程异步编程的转变,异步处理I/O密集型任务提升效率。async/await关键词定义异步函数,asyncio库管理事件循环。面对挑战,如思维转变、错误处理和调试,可通过逐步迁移、学习资源、编写测试和使用辅助库来适应。通过实践和学习,开发者能有效优化性能和响应速度。
51 3
|
3月前
|
设计模式 存储 数据库连接
Python设计模式:巧用元类创建单例模式!
Python设计模式:巧用元类创建单例模式!
48 0