python结构型模式

复用代码使用组合的结构型模式汇总

Posted by QQL on January 19, 2019

结构型模式

适配器模式

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

两种实现方式

  • 类适配器:使用多继承
  • 对象适配器:使用组合
from abc import ABCMeta, abstractmethod


class Payment(metaclass=ABCMeta):
    # abstract class
    @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:  # cost和接口Payment不适配,但是已经有其他系统使用了BankPay的cost
    def cost(self, money):
        print("银联支付%d元." % money)


class ApplePay:
    def cost(self, money):
        print("苹果支付%d元." % money)


# # 类适配器,接口统一,适合单个接口的统一
# class NewBankPay(Payment, BankPay):  # 适配器多继承
#     def pay(self, money):  # 当调用pay时和调用cost是一样的
#         self.cost(money)



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

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


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

p2 = PaymentAdapter(ApplePay())
p2.pay(100)


#复用代码两种方法:继承和组合
# 组合
# 一个类里放另外一个类的对象
# class A:
#     pass
#
# class B:
#     def __init__(self):
#         self.a = A()

角色

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

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

桥模式

内容
 将一个事物的两个维度分离(例如一个图形,有形状和颜色两个维度),使其都可以独立地变化。

角色

  • 抽象(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)  # 传递self自身对象给组合的Color


# 添加新的形状只需根据接口书写即可
class Line(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)


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



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

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

应用场景
当事物有两个维度上的表现,两个维度都可能扩展时。
优点

  • 抽象和实现相分离
  • 优秀的扩展能力

组合模式

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

  • 抽象组件(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("------复合图形------")


# client
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)]
    ------复合图形------
    ------复合图形------
"""

适用场景

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

优点

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

外观模式

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

  • 外观(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("内存断电")


 # Facade外观
class Computer:
    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()

优点

  • 减少系统相互依赖
  • 提高了灵活性
  • 提高了安全性

代理模式

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

  • 远程代理:为远程的对象提供代理(如:ORM代理sql语句与Django进行交互)
  • 虚代理:根据需要创建很大的对象(如:手机流量不足,无图模式)
  • 保护代理:控制对原始对象的访问,用于对象有不同访问权限时

角色

  • 抽象实体(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")  # 读取真实文件,即使不get_content也会读取到所有内容占用内存
# subj.get_content()

# subj = VirtualProxy("test.txt")  # 拿到文件,并没有读取文件内容到内存中
# subj.get_content()  # 只有调用get_content才获取内容到内存中

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

优点

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