设计模式(笔记)
设计模式:对软件中普遍存在(反复出现)的各种问题,所提出的解决方案。每一个设计模式系统的命名、解释和评价了面向对象系统中一个重要和重复设计。
面向对象
面向对象三大特性
- 封装
- 继承
- 多态
接口
接口:若干抽象方法的集合。
作用:限制接口的类必须按照接口给定的调用方式实现这些方法,对高层模块隐藏了类的内部实现。
from abc import ABCMeta, abstractmethod
class Pryment(metaclass=ABCMeta):
@abstractmethod
def pay(self, money: int):
...
class Alipay(Pryment):
def pay(self, money: int):
print(money)
def flush_play(pay: Pryment, money: int):
pay.pay(money)
在团队开发时公司总会要求看此类接口文档然后来实现此方法。结构主要提高多人开发时容错率。
面向对象设计SOLID原则
- 开放封闭原则:一个软件实体类、模块和函数应该对扩展开放,对修改关闭。即软件实体应尽量不修改原有代码的情况下扩展。
- 里氏替换原则:所有引用父类的地方必须能透明地使用其子类的对象。
- 依赖倒置原则:高层模块不应该依赖底层模块,二者都应该依赖其抽象:抽象不应该依赖细节;细节应该依赖抽象。换言之,要针对接口编程而不是针对现实编程。
- 接口隔离原则:使用多个专门的接口,而不使用单一的总接口,即客户端不应该依赖那些它不需要的接口。
- 单一职责原则:不要存在多余一个导致类变更的原因。通俗说一个类只负责一项职责。
设计模式分类
- 创建型模式(5种):工厂方法模式、抽象工厂模式、创建者模式、原型模式、单例模式。
- 结构型模式(7种):适配器模式、桥模式、组合模式、修饰模式、外观模式、享元模式、代理模式。
- 行为型模式(11种):解释器模式、责任链模式、命令模式、迭代器模式、中介者模式、备忘录模式、观察者模式、策略模式、访问者模式、模板方法模式。
创建型模式
简单工厂模式
不直接向客户端暴露对象创建的实现细节,而是通过一个工厂类来负责创建产品实例。
角色
- 工厂角色(Creator)
- 抽象产品角色(Product)
- 具体产品角色(Concrete Product)
优点
- 隐藏了对象创建的实现细节
- 客户端不需要修改代码
缺点
- 违反了单一原则,将创建逻辑几种到一个工厂类里
- 当添加新产品时,需要修改工厂类代码,违反了开闭原则。
工厂方法模式
定义一个用于创建对象的接口(工厂接口)让子类决定实例化哪一个产品类。
角色
- 抽象工厂角色(Creator)
- 具体工厂角色(Concrete Creator)
- 抽象产品角色(Product)
- 具体产品角色(Concrete Product)
优点
- 每个具体产品都对应一个具体工厂类,不需要修改工厂类代码。
- 隐藏了对象创建的实现细节
缺点
- 每增加一个具体产品类,就必须增加一个相应的具体工厂类
抽象工厂模式
定义一个工厂类接口,让工厂子类创建一系列相关或相互依赖的对象。
例:生产一部手机,需要手机壳、CPU、操作系统三类对象进行组装,其中每类对象都有不同总类。每个具体工厂,分别都产生一部手机所需要的三个对象。
相比工厂方法模式,抽象工厂模式中的每个具体工厂都生产一套产品。
角色
- 抽象工厂角色(Creator)
- 具体工厂角色(Concrete Creator)
- 抽象产品角色(Product)
- 具体产品角色(Concrete Product)
- 客户端(Client)
优点
- 将客户端与类的具体实现分离
- 每个工厂创建了一个完整的产品系列,使得易于交换产品系列
- 有利于产品的一致性(即产品之间的约束关系)
缺点
- 难以支持新总类的(抽象)产品
建造者模式
将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同表示。
建造者模式与抽象工厂模式相似,也用来创建复杂对象。主要区别是建造者模式着重一步步构造一个复杂对象,而抽象工厂模式着重与多个系列的产品对象
角色
- 抽象建造者(Builder)
- 具体建造者(Concrete Builder)
- 指挥者(Director)
- 产品(Product)
优点
- 隐藏了一个产品的内部结构和装配过程
- 将构造代码与表示代码分开
- 可以对构造过程进行更精细的控制
单例模式
保证类只有一个实例,并提供一个访问它的全局访问点。
角色
- 单例(Singleton)
优点
- 对唯一实例的受控访问
- 单例相当于全局变量,但防止了命名空间被污染
例子
class Singleton:
def __new__(cls, *args, **kwargs):
if not hasattr(cls, "_instance"):
cls._instance = super(Singleton, cls).__new__(cls)
return cls._instance
class MyClass(Singleton):
def __init__(self, a):
self.a = a
m = MyClass(10)
b = MyClass(20)
print(m.a)
print(b.a)
# 20 20
print(id(a) == id(b))
# True
创建型模式小结
抽象工厂模式和建造者模式相比于简单工厂模式和工厂方法模式而言更灵活也更复杂
通常情况下,设计以简单工厂模式或者工厂方法模式开始,当你发现设计需要更大的灵活性时,则向更复杂的设计模式演化
结构型模式
适配器模式
将一个类的接口转换成客户希望的另一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
两种实现方式
- 类适配器:使用多继承
- 对象适配器:使用组合
例
from abc import ABCMeta, abstractmethod
class Payment(metaclass=ABCMeta):
@abstractmethod
def pay(self, money):
...
class BankPay:
def cost(self, money):
print(money)
# 类适配器
class NewBankPay(Payment, BankPay):
def pay(self, money):
self.cost(money)
# 对象适配器
class PaymentAdapter(Payment):
def __init__(self, payment: BankPay):
self.payment = payment
def pay(self, money):
self.payment.cost(money)
角色
- 目标接口(Target)
- 待适配的类(Adaptee)
- 适配器(Adapter)
适用场景
- 想使用一个已经存在的类,而它的接口不符合你的需求
- (对象适配器)想使用一些已经存在的子类,但不能对每一个都进行子类化以匹配他们的接口。对象适配器可以适配它。
桥模式
将一个事物两个维度分离,使其都可以独立化
角色
- 抽象(Abstraction)
- 细化抽象(RefinedAbstraction)
- 实现者(Implementor)
- 具体实现者(ConcreteImplementor)
例
from abc import ABCMeta, abstractmethod
# 图形类,抽象
class Shape(metaclass=ABCMeta):
name = "图形"
@abstractmethod
def draw(self):
...
def __init__(self, color: "Color"):
self.color = color
# 颜色类,实现
class Color(metaclass=ABCMeta):
@abstractmethod
def paint(self, shape: Shape):
...
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: Shape):
print("红色的", shape.name)
class Green(Color):
def paint(self, shape: Shape):
print("绿色的", shape.name)
class Blue(Color):
def paint(self, shape: Shape):
print("蓝色的", shape.name)
line = Line(Red())
circle = Circle(Blue())
line.draw()
circle.draw()
应用场景
- 当事物有两个维度上的表现,两个维度都可能扩展时
优点
- 抽象和实现相分离
- 优秀的扩展能力
组合模式
将对象组成树形结构表示”部分-整体“的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。
角色
- 抽象组件(Component)
- 叶子组件(Leaf)
- 复合组件(Composite)
- 客户端(Client)
from abc import ABCMeta, abstractmethod
from typing import List
class Graphic(metaclass=ABCMeta):
@abstractmethod
def draw(self):
...
class Point(Graphic):
def __init__(self, x: int, y: int):
self.x = x
self.y = y
def __str__(self):
return str((self.x, self.y))
def draw(self):
print(str(self))
class Line(Graphic):
def __init__(self, *points: Point):
self.points = points
def draw(self):
print(f"{self.__class__.__name__}{[str(p) for p in self.points]}".replace("'", ""))
class Picture(Graphic):
def __init__(self):
self.children: List[Graphic] = []
self.add = self.children.append
def draw(self):
for graphic in self.children:
graphic.draw()
p1 = Point(10, 20)
l1 = Line(Point(20, 30), Point(20, 40))
l2 = Line(Point(30, 30), Point(30, 40))
picture = Picture()
picture.add(p1)
picture.add(l1)
picture.add(l2)
picture.draw()
适用场景
- 表示对象”部分-整体“层次结构(特别结构是递归的)
- 希望用户忽略组合对象于单个对象的不同,用户统一的使用组个结构中的所有对象
优点
- 定义了包含基本对象和组合对象的类层次结构
- 简化客户端代码,即客户端可以一致的使用组个对象和单个对象
- 更容易增加新类型的组件
外观模式
为子系统中的一组结构提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
角色
- 外观(facade)
- 子系统类(subsystem classes)
例
class CPU:
def run(self):
...
def stop(self):
...
class Memory:
def run(self):
...
def stop(self):
...
class Computer:
def __init__(self):
self.cpu = CPU()
self.memory = Memory()
def run(self):
self.cpu.run()
self.memory.run()
def stop(self):
self.cpu.stop()
self.memory.stop()
优点
- 减少系统相互依赖
- 提高了灵活性
- 提高了安全性
代理模式
为其它对象提供一种代理以控制对这个对象的访问
角色
- 抽象实体(Subject)
- 实体(RealSubject)
- 代理(Proxy)
from abc import ABCMeta, abstractmethod
from typing import Optional
class Subject(metaclass=ABCMeta):
@abstractmethod
def get_content(self):
...
@abstractmethod
def set_content(self, content):
...
class RealSubject(Subject):
def __init__(self, filename: str):
self.filename = filename
with open(self.filename, 'r', encoding="utf-8") as file:
self.content = file.read()
def get_content(self):
return self.content
def set_content(self, content):
with open(self.filename, 'w', encoding="utf-8") as file:
file.write(content)
class VirtualProxy(Subject):
"""虚代理,在使用这个类的时候先不获取文本内容,以免浪费内存"""
def __init__(self, filename: str):
self.filename = filename
self.subject: Optional[Subject] = None
def get_content(self):
if not self.subject:
self.subject = RealSubject(self.filename)
return self.subject.get_content()
def set_content(self, content):
if not self.subject:
self.subject = RealSubject(self.filename)
self.subject.set_content(content)
class ProtectedProxy(Subject):
"""保护代理,设置读写权限"""
def __init__(self, filename):
self.subject = RealSubject(filename)
def get_content(self):
return self.subject.get_content()
def set_content(self, content):
raise PermissionError("无权限")
应用场景
- 远程代理:为远程的对象提供代理
- 虚代理:更具需要创建很大的对象
- 保护代理:控制对原始对象的访问,用于对象有不同访问权限时
优点
- 远程代理:可以隐藏对象位于远程地址的空间事实
- 虚代理:可以优化,例如根据要求创建对象
- 保护代理:允许在访问一个对象时有一些附加的内务处理
行为型模式
责任链模式
内容使用对各对象都有机会处理请求,从而避免请求的发送者接收者之间耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。
角色
- 抽象处理者(Handler)
- 具体处理者(ConcreteHandler)
- 客户端(Client)
例
from abc import ABCMeta, abstractmethod
class Handler(metaclass=ABCMeta):
@abstractmethod
def handle_leava(self, day: int):
...
class GeneralManager(Handler):
def handle_leava(self, day):
if day <= 14:
print("pass")
else:
print("out")
class DepartmentManager(Handler):
def __init__(self):
self.next = GeneralManager()
def handle_leava(self, day):
if day <= 7:
print("pass")
else:
print("next >>", self.next.__class__.__name__)
self.next.handle_leava(day)
class ProjectDirector(Handler):
def __init__(self):
self.next = DepartmentManager()
def handle_leava(self, day):
if day <= 3:
print("pass")
else:
print("next >>", self.next.__class__.__name__)
self.next.handle_leava(day)
ProjectDirector().handle_leava(15)
适用场景
- 有多个对象可以处理一个请求,哪个对象处理由允许时决定
- 在不明确接收者情况下,向多个对象中的一个提交一个请求
优点
- 降低耦合度:一个对象无需知道是其它哪一个对象处理其请求
观察者模式
定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。观察者模式又称“发布-订阅”模式
角色
- 抽象主题(Subject)
- 具体主题(ConreteSubject)- 发布者
- 抽象观察者(Observer)
- 具体观察者(ConcreteObserver) - 订阅者
from abc import ABCMeta, abstractmethod
from typing import List
class Observer(metaclass=ABCMeta): # 抽象订阅者
@abstractmethod
def update(self, notice: "Notice"):
...
class Notice: # 发布者
def __init__(self, company_info: str = ""):
self.observers: List[Observer] = []
self.attach = self.observers.append
self.detach = self.observers.remove
self.__company_info: str = company_info
def notify(self):
"""推送给所有订阅者"""
[x.update(self) for x in self.observers]
@property
def company_info(self) -> str:
return self.__company_info
@company_info.setter
def company_info(self, value: str):
self.__company_info = value
self.notify()
class Staff(Observer):
def __init__(self):
self.company_info = ""
def update(self, notice):
self.company_info = notice.company_info
s1 = Staff()
s2 = Staff()
notice = Notice("new")
notice.attach(s1)
notice.attach(s2)
notice.company_info = "hello"
print(s1.company_info)
print(s2.company_info)
适用场景
- 当一个抽象模型有两个方面,其中一个方面依赖于另一个方面。将这两者封装在独立对象中以使它们可以各自独立地改变和复用。
- 当对一个对象的改变需要同时改变其他对象,而不知道具体有多少对象有待改变。
- 当一个对象必须通知其他对象,而它又不能假定其他对象是谁,换而言之你不喜欢这些对象是紧密耦合的。
优点
- 目标和观察者之间的抽象耦合最小
- 支持广播通信
策略模式
定义一系列算法,把他们一个个封装起来,并且使他们可以相互替换。本模式使得算法可以独立使用它的客户而变化。
角色
- 抽象策略(Strategy)
- 具体策略(ConcreteStrategy)
- 上下文(Context)
from abc import ABCMeta, abstractmethod
class Strategy(metaclass=ABCMeta):
@abstractmethod
def execute(self, data):
...
class FastStrategy(Strategy):
"""快处理效果好但是在高峰期容易严重阻塞"""
def execute(self, data):
print("fast", data)
class SlowStrategy(Strategy):
"""慢处理可以缓解高峰期的阻塞但是效果差"""
def execute(self, data):
print("slow", data)
class Context:
def __init__(self, strategy: Strategy, data):
self.strategy = strategy
self.data = data
def set_strategy(self, strategy):
self.strategy = strategy
def do_strategy(self):
self.strategy.execute(self.data)
fast = FastStrategy()
slow = SlowStrategy()
context = Context(fast, "") # 优先选择fast
context.set_strategy(slow) # 遇到高峰期时替换成slow
context.do_strategy()
优点
- 定义了一系列可重复的算法和行为
- 消除了一些条件语句
- 可以提供相同行为的不同实现
缺点
- 用户必须了解不同策略
模板方法模式
定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重新定义该算法的某些特定步骤。
角色
- 抽象类(Abstractclass)
- 定义抽象的原子操作(钩子操作)
- 具体类(Concreteclass)
- 实现原子操作
from abc import ABCMeta, abstractmethod
class BetaWindow(metaclass=ABCMeta):
@abstractmethod
def start(self):
...
@abstractmethod
def stop(self):
...
@abstractmethod
def repaint(self) -> bool:
...
def run(self):
self.start()
while self.repaint():
...
self.stop()
class Window(BetaWindow):
def __init__(self, message: str):
self.message = message
def start(self):
print("start...")
def stop(self):
print("stop...")
def repaint(self):
print(self.message)
适用场景
- 一次性实现一个算法的不变部分
- 各个子类中的公共行为应该被提取出来并集中到一个公共父类中以避免代码重复
- 控制子类扩展