2023/10/04

py与设计模式

Python与设计模式

参考资料:

OOP (面向对象编程)

  • 三大特性: 封装、继承、多态

  • 接口: 若干抽象方法的集合。

在 Python 中,有两种主要方式实现抽象类和接口,以支持多态。

1. 在基类方法中抛出 NotImplementedError

这种方式比较简单,但在运行时(调用方法时)才会发现错误。

输出:

2. 使用 abc 模块 (推荐)

abc (Abstract Base Classes) 模块提供了定义抽象基类的标准方式。使用 @abstractmethod 装饰器后,如果子类没有实现所有抽象方法,在实例化时就会直接报错,可以及早发现问题。

输出:

一般推荐使用第二种方式,因为它更严格,在对象创建时就能发现未实现的接口。在这里,Payment 就是一个接口,Alipay 实现了这个接口。上层模块只需要和 Payment 接口交互,无需关心具体的实现细节。


SOLID 原则

  • 开放封闭原则 (Open/Closed Principle): 一个软件实体(如类、模块、函数)应该对扩展开放,对修改关闭。

  • 里氏替换原则 (Liskov Substitution Principle): 所有引用父类的地方必须能透明地使用其子类的对象。

    通常要求子类方法的参数和返回值类型与父类保持一致。

  • 依赖倒置原则 (Dependency Inversion Principle): 高层模块不应依赖低层模块,二者都应依赖其抽象。抽象不应依赖细节,细节应依赖抽象。换言之,要面向接口编程,而不是面向实现编程。

  • 接口隔离原则 (Interface Segregation Principle): 使用多个专门的接口,而不是使用单一的总接口。客户端不应依赖那些它不需要的接口。

  • 单一职责原则 (Single Responsibility Principle): 一个类只负责一项职责。


常用设计模式

设计模式分类

  • 创建型模式: 关注如何创建对象,隐藏创建逻辑,使程序在处理创建过程时更灵活。

  • 结构型模式: 关注类和对象的组合,如何将它们组合成更大的结构。

  • 行为型模式: 关注对象之间的通信,以及如何分配职责。

创建型模式

简单工厂模式

不直接向客户端暴露对象创建的实现细节,而是通过一个工厂类来负责创建产品类的实例。

角色: 工厂(Creator), 抽象产品(Product), 具体产品(Concrete Product)

输出:

缺点:

  • 将所有创建逻辑集中在一个工厂里,违反了单一职责原则。

  • 当添加新产品时,需要修改工厂类代码,违反了开闭原则。

工厂方法模式

定义一个用于创建对象的接口 (工厂接口),让子类决定实例化哪一个产品类。

角色: 抽象工厂、抽象产品、具体工厂、具体产品。

  • 每个具体产品都对应一个具体工厂类。

  • 优点: 遵循开闭原则,增加新产品时只需增加新的具体工厂和产品类,无需修改现有代码。

  • 缺点: 每增加一个产品,就需要增加一个对应的工厂类,导致类的数量增多。

抽象工厂模式

定义一个工厂类接口,让工厂子类来创建一系列相关或相互依赖的对象。

  • 与工厂方法模式相比,抽象工厂模式中的每个具体工厂都生产一个产品族(一系列相关的产品)。

  • 缺点: 增加新的产品种类(例如,增加一个make_camera方法)会非常困难,需要修改所有工厂的接口。

输出:

建造者模式

将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

角色: 抽象建造者(Builder)、具体建造者(ConcreteBuilder)、指挥者(Director)、产品(Product)。

  • 指挥者负责控制构建步骤的顺序。

  • 具体建造者负责实现每个构建步骤。

单例模式

确保一个类只有一个实例,并提供一个全局访问点。

结构型模式

适配器模式

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

角色: 目标接口(Target)、待适配对象(Adaptee)、适配器(Adapter)。

实现方式:

  1. 类适配器: 使用多重继承。

  2. 对象适配器: 使用对象组合(更常用,更灵活)。

输出:

桥接模式

将一个事物的多个变化维度分离,使其可以独立变化。例如,形状和颜色是两个维度,它们可以独立扩展。

角色: 抽象(Abstraction)、细化抽象(RefinedAbstraction)、实现者(Implementor)、具体实现者(ConcreteImplementor)。

输出:

组合模式

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

角色:

  • 抽象组件 (Component): 为组合中的对象声明接口。

  • 叶子组件 (Leaf): 表示叶节点对象,没有子节点。

  • 复合组件 (Composite): 表示有子节点的复合对象。

  • 客户端 (Client): 通过 Component 接口操作组合部件。

如果你发现需要表示对象的“部分-整体”结构,并且希望用户能够统一地处理单个对象和组合对象,那么组合模式是一个很好的选择。

外观模式

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

角色: 外观(Facade)、子系统类(Subsystem classes)。

优点: 减少耦合、提高灵活性和安全性。

输出:

代理模式

为其他对象提供一种代理以控制对这个对象的访问。

应用场景:

  • 远程代理: 为一个位于不同地址空间的对象提供一个本地的代表。

  • 虚代理: 根据需要创建开销很大的对象(例如,懒加载)。

  • 保护代理: 控制对原始对象的访问,用于对象有不同访问权限时。

角色: 抽象实体(Subject)、真实实体(RealSubject)、代理(Proxy)。

输出:

享元模式 (Flyweight)

运用共享技术有效地支持大量细粒度的对象,以减少内存占用和提高性能。它将对象的状态分为内部状态(可共享)和外部状态(不可共享)。

何时使用:

  1. 系统中有大量相似对象。

  2. 这些对象消耗大量内存。

  3. 对象的大部分状态可以外部化。

输出:

行为型模式

责任链模式

使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。

角色: 抽象处理者(Handler)、具体处理者(ConcreteHandler)、客户端(Client)。

输出:

观察者模式 (发布-订阅模式)

定义对象间的一种一对多的依赖关系。当一个对象的状态发生改变时, 所有依赖于它的对象都会得到通知并被自动更新。

角色:

  • 抽象主题 (Subject): 提供附加和解绑观察者对象的接口。

  • 具体主题 (ConcreteSubject/Publisher): 发布者,状态改变时通知所有观察者。

  • 抽象观察者 (Observer): 定义一个更新接口,在得到主题通知时更新自己。

  • 具体观察者 (ConcreteObserver/Subscriber): 订阅者,实现更新接口。

输出:

策略模式

定义一系列算法,将它们各自封装起来,并使它们可以相互替换。此模式让算法的变化独立于使用算法的客户。

角色: 抽象策略(Strategy)、具体策略(ConcreteStrategy)、上下文(Context)。

输出:

模板方法模式

定义一个操作中的算法骨架,而将一些步骤的实现延迟到子类中。模板方法使得子类可以不改变一个算法的结构,即可重定义该算法的某些特定步骤。

角色: 抽象类(AbstractClass)、具体类(ConcreteClass)。

输出:

命令模式

将一个请求封装成一个对象,从而可以使用不同的请求对客户进行参数化。它能将请求的发送者和接收者解耦。

角色: 命令(Command)、具体命令(ConcreteCommand)、接收者(Receiver)、调用者(Invoker)。

输出:

其他行为型模式

  • 访问者模式 (Visitor): 将数据结构与数据操作分离。适用于数据结构稳定但操作易变的情况。

  • 数据访问对象模式 (DAO): 为数据源(如数据库)提供一个抽象的接口,将业务逻辑与数据访问逻辑分离。

  • 前端控制器模式 (Front Controller): 提供一个集中的请求处理机制,所有请求由一个单一的处理程序处理,然后分发给相应的具体处理程序。

  • 拦截过滤器模式 (Intercepting Filter): 对应用程序的请求或响应做一些预处理/后处理。通过过滤器链实现。

0 评论:

发表评论