Python常见设计模式2: 结构型模式

文章目录


适配器模式

  • 将一个类的接口转换成客户希望的另一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
  • 两种实现方式:
    • 类适配器:使用多继承
    • 对象适配器:使用组合
python 复制代码
# 适配器模式
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)

桥接模式

  • 将一个事物的两个维度分离,使其都可以独立地变化。
python 复制代码
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):
        # TODO:矩形逻辑
        self.color.paint(self)


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

    def draw(self):
        # TODO:原型逻辑
        self.color.paint(self)


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


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


shape = Rectangle(Red())
shape.draw()

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

组合模式

  • 将对象组合成树形结构以表示"部分-整体"的层次结构。组合模式是用户对单个对象和组合对象的使用都具有一致性。
  • 角色:
    • 抽象组件(Component)
    • 叶子组件(Leaf)
    • 复合组件(Composite)
    • 客户端(Client)
python 复制代码
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("*"* 100)
        print("----复合图形 start----")
        for g in self.children:
            g.draw()
        print("----复合图形 end----")


# 线段
l = Line(Point(1, 1), Point(2, 2))
print(l)

# 点跟线组合
p1 = Point(2, 3)
l1 = Line(Point(2, 4), Point(6, 7))
l2 = Line(Point(1, 5), Point(2, 8))
pic1 = Picture([p1, l1, l2])
# pic1.draw()

# pic1跟 pic2再次组合
l3 = Line(Point(2, 4), Point(6, 7))
l4 = Line(Point(1, 5), Point(2, 8))
pic2 = Picture([l3, l4])
pic3 = Picture([pic1, pic2])
pic3.draw()
# 此处csdn运行代码可能出问题,理论上应该有三次**********

外观模式

  • 为子系统的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
  • 角色:
    • 外观(Facade)
    • 子系统类(Sub system classes)
python 复制代码
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.memory.stop()
        self.disk.stop()
        self.cpu.stop()


computer = Computer()
computer.run()
print(f"计算机正在运行。。。")
computer.stop()

代理模式

  • 为其他对象提供一种代理以控制对这个对象的访问
  • 应用场景:
    • 远程代理:为远程的对象提供代理
    • 虚代理:可在需要时才创建对象,减少内存消耗
    • 保护代理:控制对原始对象的访问,用于对象有不同访问权限时

建议在py源文件同级目录下创建一个test.txt文件

txt 复制代码
Initial Context.
python 复制代码
"""
角色:
    抽象实体(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
        # 尝试读取文件,如果文件不存在则初始化为空内容
        try:
            with open(filename, 'r') as f:
                self.content = f.read()
        except FileNotFoundError:
            self.content = ""

    def get_content(self):
        return self.content

    def set_content(self, content, new_filename=None):
        # 如果提供了新文件名,则更新当前实例的文件名
        if new_filename:
            self.filename = new_filename
        
        # 写入内容(如果文件不存在会自动创建)
        with open(self.filename, 'w') as f:
            f.write(content)
            self.content = content


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 self.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.set_content("Testing Context.")
print(subj.get_content())

print("---- VirtualProxy")
v_subj = VirtualProxy("test.txt")
print(v_subj.get_content())

print("---- ProtectedProxy")
p_subj = ProtectedProxy("test.txt")
print(p_subj.get_content())
# p_subj.set_content("aaa")
# 会输出PermissionError