0 设计模式
对软件中普遍存在(反复出现)的各种问题,所提出的解决方案。每一个设计模式系统地命名、解释和评价了面向对象系统中一个重要的和反复出现的设计。
-
面向对象设计的SOLID原则
-
开放封闭原则:一个软件实体,如类、模块和函数应该对扩展开放,对修改关闭。即,软件实体应尽量在不修改原有代码的情况下进行扩展
-
里氏替换原则:引用所有父类的地方必须能透明地使用其子类的对象。
-
依赖倒置原则
要针对接口编程,而不是针对实现编程
- 高层模块不应该依赖底层模块,二者都应该依赖其抽象;
- 抽象不应该依赖细节;
- 细节应该依赖抽象。
-
接口隔离原则:使用多个专门的接口,而不使用单一的总接口,即客户端不应该依赖那些它不需要的接口
-
单一职责原则:不要存在多余一个导致类变更的原因。通俗地说,即一个类只负责一项职责
-
-
设计模式分类
- 创建型模式(5种):工厂方法模式、抽象工厂模式、创建者模式、原型模式、单例模式
- 结构型模式(7种):适配器模式、桥模式、组合模式、装饰模式、外观模式、享元模式、代理模式
- 行为型模式(11种):解释器模式、责任链模式、命令模式、迭代器模式、中介者模式、备忘录模式、观察者模式、状态模式、策略模式、访问者模式、模板方法模式
1 创建型模式
1.1 简单工厂模式
- 内容:不直接向客户端暴露对象创建的实现细节,而是通过一个工厂类来负责创建产品类的实例。
- 角色
- 工厂角色(Creator)
- 抽象产品角色(Product)
- 具体产品角色(Concrete Product)
- 优点
- 隐藏了对象创建的实现细节
- 客户端不需要修改代码
- 缺点
- 违反了单一职责原则,将创建逻辑集中到一个工厂类里
- 当添加新产品时,需要修改工厂代码,违反了开闭原则
1 | from abc import ABCMeta, abstractmethod |
1.2 工厂方法模式
- 内容:定义一个用于创建对象的接口(工厂接口),让子类决定实例化哪一个产品类
- 角色
- 抽象工厂角色(Creator)
- 具体工厂角色(Concrete Creator)
- 抽象产品角色(Product)
- 具体产品角色(Concrete Product)
- 优点
- 每个具体产品都对应一个具体工厂类,不需要修改工厂类代码
- 隐藏了对象创建的实现细节
- 缺点
- 每增加一个具体产品类,就必须增加一个相应的具体工厂类
1 | from abc import ABCMeta, abstractmethod |
1.3 抽象工厂模式
-
内容:定义一个工厂类接口,让工厂子类来创建一系列相关或相互依赖的对象
相比工厂方法模式,抽象工厂模式种的每个具体工厂都生产一套产品
-
角色
- 抽象工厂角色(Creator)
- 具体工厂角色(Concrete Creator)
- 抽象产品角色(Product)
- 具体产品角色(Concrete Product)
- 客户端(Client)
-
优点
- 将客户端与类的具体实现相分离
- 每个工厂创建了一个完整的产品系列,使得易于交换产品系列
- 有利于产品的一致性(即产品之间的约束关系)
-
缺点
- 难以支持新种类的(抽象)产品
1 | from abc import ABCMeta, abstractmethod |
1.4 建造者模式
-
内容:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示
建造者模式与抽象工厂模式相似,也用来创建复杂对象。主要区别是建造者模式着重步步构造一个复杂对象,而抽象工厂模式着重于多个系列的产品对象。
-
角色
- 抽象建造者(Builder)
- 具体建造者(Concrete Builder)
- 指挥者(Director)
- 产品(Product)
-
优点
- 隐藏了一个产品的内部结构和装配过程
- 将构造代码与表示代码分开
- 可以对构造过程进行更精细的控制
1 | from abc import ABCMeta, abstractmethod |
1.5 单例模式
- 内容:保证一个类只有一个实例,并提供一个访问它的全局访问点
- 角色:单例(Singleton)
- 优点
- 对唯一实例的受控访问
- 单例相当于全局变量,但防止了命名空间被污染
1 | class Singleton: |
2 结构型模式
2.1 适配器模式
-
内容:将一个类的接口转换成客户希望的另一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作
-
两种实现方式
- 类适配器:使用多继承
- 对象适配器:使用组合
-
角色
- 目标接口(Target)
- 待适配的类(Adaptee)
- 适配器(Adapter)
-
使用场景
- 想使用一个已经存在的类,而它的接口不符合你的要求
- (对象适配器)想使用一些已经存在的子类,但不可能对每一个都进行子类化以匹配他们的接口。对象适配器可以适配它的父类接口。
1 | from abc import ABCMeta, abstractmethod |
2.2 桥模式
-
内容:将一个事物的两个维度分离,使其都可以独立地变化
-
角色
- 抽象(Abstraction)
- 细化抽象(RefinedAbstraction)
- 实现者(Implementor)
- 具体实现者(ConcreteImplementor)
-
应用场景:当事物有两个维度上的表现,两个维度都可能扩展时
-
优点
- 抽象和实现相分离
- 优秀的扩展能力
1 | from abc import ABCMeta, abstractmethod |
2.3 组合模式
- 内容:将对象组合成树形结构以表示“部分-整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。
- 角色
- 抽象组件(Component)
- 叶子组件(Leaf)
- 复合组件(Composite)
- 客户端(Client)
- 适用场景
- 表示对象的“整体-部分”层次结构(特别是结构是递归的)
- 希望用户忽略组合对象与单个对象的不同,用户统一地使用组合结构中的所有对象
- 优点
- 定义了包含基本对象和组合对象的类层次结构
- 简化客户端代码,即客户端可以一致地使用组合对象和单个对象
- 更容易增加新类型的组件
1 | from abc import ABCMeta, abstractmethod |
2.4 外观模式
- 内容:为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用
- 角色
- 外观(facade)
- 子系统类(subsystem classes)
- 优点
- 减少了系统相互依赖
- 提高了灵活性
- 提高了安全性
1 | class CPU: |
2.5 代理模式
- 内容:为其他对象提供一种代理以控制这个对象的访问
- 应用场景
- 远程代理:为远程对象提供代理
- 虚代理:根据需要创建很大的对象
- 保护代理:控制对原始对象的访问,用于对象有不同访问权限时
- 角色
- 抽象实体(Subject)
- 实体(RealSubject)
- 代理(Proxy)
- 优点
- 远程代理:可以隐藏对象位于远程地址空间的事实
- 虚代理:可以进行优化,例如根据要求创建对象
- 保护代理:允许在访问一个对象时有一些附加的内务处理
1 | from abc import ABCMeta, abstractmethod |
3 行为型模式
3.1 责任链模式
-
内容:使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止
-
角色
- 抽象处理者(Handler)
- 具体处理者(ConcreteHandler)
- 客户端(Client)
-
适用场景
- 有多个对象可以处理一个请求,哪个对象处理由运行时决定
- 在不明确接收者的情况下,向多个对象中的一个提交一个请求
-
优点
- 降低耦合度:一个对象无需知道是其他哪一个对象处理请求
1 | from abc import ABCMeta, abstractmethod |
3.2 观察者模式
- 内容:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。观察者模式又称“发布-订阅”模式
- 角色
- 抽象主题(Subject)
- 具体主题(ConcreteSubject)—— 发布者
- 抽象观察者(Observer)
- 具体观察者(ConcreteObserver)—— 订阅者
- 适用场景
- 当一个抽象模型有两个方面,其中一个方面依赖于另一个方面。将这两者封装在独立对象中,以使它们可以各自独立地改变和复用
- 当对一个对象的改变需要同时改变其他对象,而不知道具体有多少对象有待改变
- 当一个对象必须通知其他对象,而它又不能假定其它对象是谁。换言之,你不希望这些对象是紧密耦合的
- 优点
- 目标和观察者之间的抽象耦合最小
- 支持广播通信
1 | from abc import ABCMeta, abstractmethod |
3.3 策略模式
- 内容:定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换。本模式使得算法可独立于使用它的客户而变化。
- 角色
- 抽象策略(Strategy)
- 具体策略(ConcreteStrategy)
- 上下文(Context)
- 优点
- 定义了一系列可重用的算法和行为
- 消除了一些条件语句
- 可以提供相同行为的不同实现
- 缺点
- 客户必须了解不同的策略
1 | from abc import ABCMeta, abstractmethod |
3.4 模板方法模式
- 内容:定义一个操作中的算法骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构,即可重定义该算法的某些特定步骤
- 角色
- 抽象类(AbstractClass):定义抽象的原子操作(钩子操作);实现一个模板方法作为算法的骨架
- 具体类(ConcreteClass):实现原子操作
- 适用场景
- 一次性实现一个算法的不变部分
- 各个子类的公共行为应该被提取出来并集中到一个公共的父类中,以免代码重复
- 控制子类扩展
1 | from abc import ABCMeta, abstractmethod |