Python与设计模式
参考资料:
OOP (面向对象编程)
三大特性: 封装、继承、多态
接口: 若干抽象方法的集合。
在 Python 中,有两种主要方式实现抽象类和接口,以支持多态。
1. 在基类方法中抛出 NotImplementedError
这种方式比较简单,但在运行时(调用方法时)才会发现错误。
xclass Payment:
"""支付接口"""
def pay(self, money):
raise NotImplementedError
class Alipay(Payment):
"""支付宝支付,实现了 pay 方法"""
def pay(self, money):
print(f"支付宝成功支付 {money} 元")
class Paypal(Payment):
"""Paypal支付,未实现 pay 方法"""
pass
# --- 客户端代码 ---
p1 = Alipay()
p1.pay(100)
# p2 = Paypal()
# p2.pay(10) # 这行代码会触发 NotImplementedError
输出:
xxxxxxxxxx
支付宝成功支付 100 元
2. 使用 abc
模块 (推荐)
abc
(Abstract Base Classes) 模块提供了定义抽象基类的标准方式。使用 @abstractmethod
装饰器后,如果子类没有实现所有抽象方法,在实例化时就会直接报错,可以及早发现问题。
xxxxxxxxxx
from abc import ABCMeta, abstractmethod
class Payment(metaclass=ABCMeta):
"""支付接口 (抽象基类)"""
def pay(self, money):
pass
class Alipay(Payment):
"""支付宝支付"""
def pay(self, money):
print(f"支付宝成功支付 {money} 元")
# class Paypal(Payment):
# """未实现抽象方法的子类"""
# pass
# --- 客户端代码 ---
p = Alipay()
p.pay(100)
# a = Paypal() # 这行代码会触发 TypeError: Can't instantiate abstract class ...
输出:
xxxxxxxxxx
支付宝成功支付 100 元
一般推荐使用第二种方式,因为它更严格,在对象创建时就能发现未实现的接口。在这里,Payment
就是一个接口,Alipay
实现了这个接口。上层模块只需要和 Payment
接口交互,无需关心具体的实现细节。
SOLID 原则
开放封闭原则 (Open/Closed Principle): 一个软件实体(如类、模块、函数)应该对扩展开放,对修改关闭。
里氏替换原则 (Liskov Substitution Principle): 所有引用父类的地方必须能透明地使用其子类的对象。
通常要求子类方法的参数和返回值类型与父类保持一致。
依赖倒置原则 (Dependency Inversion Principle): 高层模块不应依赖低层模块,二者都应依赖其抽象。抽象不应依赖细节,细节应依赖抽象。换言之,要面向接口编程,而不是面向实现编程。
接口隔离原则 (Interface Segregation Principle): 使用多个专门的接口,而不是使用单一的总接口。客户端不应依赖那些它不需要的接口。
单一职责原则 (Single Responsibility Principle): 一个类只负责一项职责。
常用设计模式
设计模式分类
创建型模式: 关注如何创建对象,隐藏创建逻辑,使程序在处理创建过程时更灵活。
结构型模式: 关注类和对象的组合,如何将它们组合成更大的结构。
行为型模式: 关注对象之间的通信,以及如何分配职责。
创建型模式
简单工厂模式
不直接向客户端暴露对象创建的实现细节,而是通过一个工厂类来负责创建产品类的实例。
角色: 工厂(Creator), 抽象产品(Product), 具体产品(Concrete Product)
xxxxxxxxxx
from abc import ABCMeta, abstractmethod
# 抽象产品
class Payment(metaclass=ABCMeta):
def pay(self, money):
pass
# 具体产品
class Alipay(Payment):
def pay(self, money):
print(f"支付宝支付成功: {money}元")
class WechatPay(Payment):
def pay(self, money):
print(f"微信支付成功: {money}元")
# 工厂类
class PaymentFactory:
"""
支付工厂,根据方法名创建具体的支付对象。
工厂模式可以隐藏对象创建的复杂细节(如初始化需要多个参数等)。
"""
def create_payment(self, method):
if method == "alipay":
return Alipay()
elif method == "wechat":
return WechatPay()
else:
raise TypeError(f"不支持的支付方式: {method}")
# 客户端代码
pf = PaymentFactory()
p = pf.create_payment("alipay")
p.pay(123)
输出:
xxxxxxxxxx
支付宝支付成功: 123元
缺点:
将所有创建逻辑集中在一个工厂里,违反了单一职责原则。
当添加新产品时,需要修改工厂类代码,违反了开闭原则。
工厂方法模式
定义一个用于创建对象的接口 (工厂接口),让子类决定实例化哪一个产品类。
角色: 抽象工厂、抽象产品、具体工厂、具体产品。
每个具体产品都对应一个具体工厂类。
优点: 遵循开闭原则,增加新产品时只需增加新的具体工厂和产品类,无需修改现有代码。
缺点: 每增加一个产品,就需要增加一个对应的工厂类,导致类的数量增多。
抽象工厂模式
定义一个工厂类接口,让工厂子类来创建一系列相关或相互依赖的对象。
与工厂方法模式相比,抽象工厂模式中的每个具体工厂都生产一个产品族(一系列相关的产品)。
缺点: 增加新的产品种类(例如,增加一个
make_camera
方法)会非常困难,需要修改所有工厂的接口。
xxxxxxxxxx
from abc import ABCMeta, abstractmethod
# --- 抽象产品族 ---
class CPU(metaclass=ABCMeta):
def show_cpu(self):
pass
class OS(metaclass=ABCMeta):
def show_os(self):
pass
# --- 抽象工厂 ---
class PhoneFactory(metaclass=ABCMeta):
def make_cpu(self):
pass
def make_os(self):
pass
# --- 具体产品 ---
class SnapdragonCPU(CPU):
def show_cpu(self):
print("骁龙CPU")
class AppleCPU(CPU):
def show_cpu(self):
print("苹果A系列CPU")
class AndroidOS(OS):
def show_os(self):
print("安卓系统")
class IOS(OS):
def show_os(self):
print("iOS系统")
# --- 具体工厂 ---
class XiaomiFactory(PhoneFactory):
def make_cpu(self):
return SnapdragonCPU()
def make_os(self):
return AndroidOS()
class AppleFactory(PhoneFactory):
def make_cpu(self):
return AppleCPU()
def make_os(self):
return IOS()
# --- 客户端代码 ---
class Phone:
def __init__(self, cpu: CPU, os: OS):
self.cpu = cpu
self.os = os
def show_info(self):
print("手机配置:")
self.cpu.show_cpu()
self.os.show_os()
def make_phone(factory: PhoneFactory):
return Phone(factory.make_cpu(), factory.make_os())
# 使用小米工厂创建手机
p1 = make_phone(XiaomiFactory())
p1.show_info()
print("-" * 20)
# 使用苹果工厂创建手机
p2 = make_phone(AppleFactory())
p2.show_info()
输出:
xxxxxxxxxx
手机配置:
骁龙CPU
安卓系统
--------------------
手机配置:
苹果A系列CPU
iOS系统
建造者模式
将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
角色: 抽象建造者(Builder)、具体建造者(ConcreteBuilder)、指挥者(Director)、产品(Product)。
指挥者负责控制构建步骤的顺序。
具体建造者负责实现每个构建步骤。
单例模式
确保一个类只有一个实例,并提供一个全局访问点。
结构型模式
适配器模式
将一个类的接口转换成客户希望的另一个接口,使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
角色: 目标接口(Target)、待适配对象(Adaptee)、适配器(Adapter)。
实现方式:
类适配器: 使用多重继承。
对象适配器: 使用对象组合(更常用,更灵活)。
xxxxxxxxxx
from abc import ABCMeta, abstractmethod
# 目标接口
class Payment(metaclass=ABCMeta):
def pay(self, money):
pass
# 待适配的类
class BankPay:
def cost(self, amount):
print(f"银行卡支付 {amount} 元")
# 1. 类适配器 (通过多继承)
class ClassAdapter(Payment, BankPay):
def pay(self, money):
self.cost(amount=money)
# 2. 对象适配器 (通过组合)
class ObjectAdapter(Payment):
def __init__(self, payment_adaptee):
self.payment = payment_adaptee
def pay(self, money):
self.payment.cost(money)
# 客户端代码
p1 = ClassAdapter()
p1.pay(123)
p2 = ObjectAdapter(BankPay()) # 传入待适配的对象
p2.pay(456)
输出:
xxxxxxxxxx
银行卡支付 123 元
银行卡支付 456 元
桥接模式
将一个事物的多个变化维度分离,使其可以独立变化。例如,形状和颜色是两个维度,它们可以独立扩展。
角色: 抽象(Abstraction)、细化抽象(RefinedAbstraction)、实现者(Implementor)、具体实现者(ConcreteImplementor)。
xxxxxxxxxx
from abc import ABCMeta, abstractmethod
# 实现者接口 (颜色)
class Color(metaclass=ABCMeta):
def paint(self, shape_name):
pass
# 抽象接口 (形状)
class Shape(metaclass=ABCMeta):
def __init__(self, color: Color):
self.color = color
def draw(self):
pass
# --- 具体实现者 (具体颜色) ---
class Red(Color):
def paint(self, shape_name):
print(f"绘制红色的{shape_name}")
class Blue(Color):
def paint(self, shape_name):
print(f"绘制蓝色的{shape_name}")
# --- 细化抽象 (具体形状) ---
class Rectangle(Shape):
def draw(self):
self.color.paint("矩形")
class Circle(Shape):
def draw(self):
self.color.paint("圆形")
# 客户端代码
# 自由组合形状和颜色
s1 = Rectangle(Red())
s1.draw()
s2 = Circle(Blue())
s2.draw()
输出:
xxxxxxxxxx
绘制红色的矩形
绘制蓝色的圆形
组合模式
将对象组合成树形结构以表示“部分-整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。
角色:
抽象组件 (Component): 为组合中的对象声明接口。
叶子组件 (Leaf): 表示叶节点对象,没有子节点。
复合组件 (Composite): 表示有子节点的复合对象。
客户端 (Client): 通过 Component 接口操作组合部件。
如果你发现需要表示对象的“部分-整体”结构,并且希望用户能够统一地处理单个对象和组合对象,那么组合模式是一个很好的选择。
外观模式
为子系统中的一组接口提供一个一致的界面。外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
角色: 外观(Facade)、子系统类(Subsystem classes)。
优点: 减少耦合、提高灵活性和安全性。
xxxxxxxxxx
# --- 子系统类 ---
class CPU:
def run(self):
print("CPU 开始运行")
def stop(self):
print("CPU 停止运行")
class Memory:
def load(self):
print("内存加载数据")
def unload(self):
print("内存释放数据")
class Disk:
def read(self):
print("硬盘读取数据")
def write(self):
print("硬盘写入数据")
# --- 外观类 ---
class Computer:
def __init__(self):
self.cpu = CPU()
self.memory = Memory()
self.disk = Disk()
def start(self):
print("电脑开机...")
self.disk.read()
self.memory.load()
self.cpu.run()
print("电脑启动成功!")
def stop(self):
print("电脑关机...")
self.cpu.stop()
self.memory.unload()
print("电脑已关闭。")
# 客户端代码
computer = Computer()
computer.start()
print("-" * 20)
computer.stop()
输出:
xxxxxxxxxx
电脑开机...
硬盘读取数据
内存加载数据
CPU 开始运行
电脑启动成功!
--------------------
电脑关机...
CPU 停止运行
内存释放数据
电脑已关闭。
代理模式
为其他对象提供一种代理以控制对这个对象的访问。
应用场景:
远程代理: 为一个位于不同地址空间的对象提供一个本地的代表。
虚代理: 根据需要创建开销很大的对象(例如,懒加载)。
保护代理: 控制对原始对象的访问,用于对象有不同访问权限时。
角色: 抽象实体(Subject)、真实实体(RealSubject)、代理(Proxy)。
xxxxxxxxxx
from abc import ABCMeta, abstractmethod
import time
# 抽象实体
class Subject(metaclass=ABCMeta):
def get_content(self):
pass
def set_content(self, content):
pass
# 真实实体
class RealSubject(Subject):
def __init__(self, filename):
self.filename = filename
print("...从磁盘加载文件...")
time.sleep(1) # 模拟耗时操作
with open(self.filename, "r", encoding="utf-8") as f:
self.content = f.read()
def get_content(self):
return self.content
def set_content(self, content):
with open(self.filename, "w", encoding="utf-8") as f:
f.write(content)
# 虚代理 (懒加载)
class VirtualProxy(Subject):
def __init__(self, filename):
self.filename = filename
self.real_subject = None # 初始不创建真实对象
def get_content(self):
if not self.real_subject:
print("虚代理:首次访问,正在创建真实对象...")
self.real_subject = RealSubject(self.filename)
return self.real_subject.get_content()
def set_content(self, content):
if not self.real_subject:
self.real_subject = RealSubject(self.filename)
return self.real_subject.set_content(content)
# 保护代理
class ProtectedProxy(Subject):
def __init__(self, filename):
self.real_subject = RealSubject(filename)
def get_content(self):
print("保护代理:允许读取。")
return self.real_subject.get_content()
def set_content(self, content):
raise PermissionError("保护代理:禁止写入!")
# 准备一个测试文件
with open("test.txt", "w", encoding="utf-8") as f:
f.write("这是原始文本。")
print("--- 使用虚代理 ---")
proxy = VirtualProxy("test.txt")
print("代理已创建,但真实对象未加载。")
print(proxy.get_content()) # 第一次get时才会真正加载
print("\n--- 使用保护代理 ---")
protected_proxy = ProtectedProxy("test.txt")
print(protected_proxy.get_content())
try:
protected_proxy.set_content("尝试写入新内容。")
except PermissionError as e:
print(e)
输出:
xxxxxxxxxx
--- 使用虚代理 ---
代理已创建,但真实对象未加载。
虚代理:首次访问,正在创建真实对象...
...从磁盘加载文件...
这是原始文本。
--- 使用保护代理 ---
...从磁盘加载文件...
保护代理:允许读取。
这是原始文本。
保护代理:禁止写入!
享元模式 (Flyweight)
运用共享技术有效地支持大量细粒度的对象,以减少内存占用和提高性能。它将对象的状态分为内部状态(可共享)和外部状态(不可共享)。
何时使用:
系统中有大量相似对象。
这些对象消耗大量内存。
对象的大部分状态可以外部化。
xxxxxxxxxx
# 享元接口
class ChessPiece:
def __init__(self, color):
self.color = color # 内部状态
def display(self, coordinates):
# 外部状态 (坐标) 由客户端提供
print(f"在 {coordinates} 位置放置了一个 {self.color} 的棋子")
# 享元工厂
class ChessPieceFactory:
def __init__(self):
self.pieces = {} # 存储已创建的享元对象
def get_piece(self, color):
if color not in self.pieces:
print(f"创建新的享元对象:{color}色棋子")
self.pieces[color] = ChessPiece(color)
return self.pieces[color]
# 客户端代码
factory = ChessPieceFactory()
# 获取黑棋,这是第一次,会创建新对象
black_piece1 = factory.get_piece("黑色")
black_piece1.display("A1")
# 再次获取黑棋,会返回已有的共享对象
black_piece2 = factory.get_piece("黑色")
black_piece2.display("C3")
# 获取白棋
white_piece = factory.get_piece("白色")
white_piece.display("B2")
print(f"black_piece1 和 black_piece2 是同一个对象吗? {id(black_piece1) == id(black_piece2)}")
输出:
xxxxxxxxxx
创建新的享元对象:黑色色棋子
在 A1 位置放置了一个 黑色的棋子
在 C3 位置放置了一个 黑色的棋子
创建新的享元对象:白色色棋子
在 B2 位置放置了一个 白色的棋子
black_piece1 和 black_piece2 是同一个对象吗? True
行为型模式
责任链模式
使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。
角色: 抽象处理者(Handler)、具体处理者(ConcreteHandler)、客户端(Client)。
xxxxxxxxxx
from abc import ABCMeta, abstractmethod
# 抽象处理者
class Handler(metaclass=ABCMeta):
def __init__(self):
self.next_handler = None
def set_next(self, handler):
self.next_handler = handler
return handler
def handle_request(self, level):
pass
# 具体处理者
class EasyHandler(Handler):
def handle_request(self, level):
if 1 <= level <= 3:
print(f"难度 {level}:由初级处理者解决。")
elif self.next_handler:
self.next_handler.handle_request(level)
class MediumHandler(Handler):
def handle_request(self, level):
if 4 <= level <= 6:
print(f"难度 {level}:由中级处理者解决。")
elif self.next_handler:
self.next_handler.handle_request(level)
class HardHandler(Handler):
def handle_request(self, level):
if 7 <= level <= 9:
print(f"难度 {level}:由高级处理者解决。")
else:
print("超出处理范围。")
# 客户端代码:构建责任链
chain = EasyHandler()
chain.set_next(MediumHandler()).set_next(HardHandler())
# 发送请求
chain.handle_request(2)
chain.handle_request(5)
chain.handle_request(8)
chain.handle_request(10)
输出:
xxxxxxxxxx
难度 2:由初级处理者解决。
难度 5:由中级处理者解决。
难度 8:由高级处理者解决。
超出处理范围。
观察者模式 (发布-订阅模式)
定义对象间的一种一对多的依赖关系。当一个对象的状态发生改变时, 所有依赖于它的对象都会得到通知并被自动更新。
角色:
抽象主题 (Subject): 提供附加和解绑观察者对象的接口。
具体主题 (ConcreteSubject/Publisher): 发布者,状态改变时通知所有观察者。
抽象观察者 (Observer): 定义一个更新接口,在得到主题通知时更新自己。
具体观察者 (ConcreteObserver/Subscriber): 订阅者,实现更新接口。
xxxxxxxxxx
from abc import ABCMeta, abstractmethod
# 抽象观察者
class Observer(metaclass=ABCMeta):
def update(self, subject):
pass
# 抽象主题
class Subject:
def __init__(self):
self._observers = []
def attach(self, observer: Observer):
if observer not in self._observers:
self._observers.append(observer)
def detach(self, observer: Observer):
self._observers.remove(observer)
def notify(self):
for observer in self._observers:
observer.update(self)
# 具体主题 (发布者)
class CompanyNotice(Subject):
def __init__(self):
super().__init__()
self.__info = None
def info(self):
return self.__info
setter .
def info(self, new_info):
self.__info = new_info
print("【公司发布新通知】")
self.notify() # 状态改变时,通知所有观察者
# 具体观察者 (订阅者)
class Staff(Observer):
def __init__(self, name):
self.name = name
self.company_info = None
def update(self, subject: CompanyNotice):
self.company_info = subject.info
print(f"员工 {self.name} 收到通知: {self.company_info}")
# 客户端代码
notice = CompanyNotice()
s1 = Staff("张三")
s2 = Staff("李四")
notice.attach(s1)
notice.attach(s2)
notice.info = "明天放假一天!"
print("-" * 20)
notice.detach(s1) # 张三取消订阅
notice.info = "明天下午开会。"
输出:
xxxxxxxxxx
【公司发布新通知】
员工 张三 收到通知: 明天放假一天!
员工 李四 收到通知: 明天放假一天!
--------------------
【公司发布新通知】
员工 李四 收到通知: 明天下午开会。
策略模式
定义一系列算法,将它们各自封装起来,并使它们可以相互替换。此模式让算法的变化独立于使用算法的客户。
角色: 抽象策略(Strategy)、具体策略(ConcreteStrategy)、上下文(Context)。
xxxxxxxxxx
from abc import ABCMeta, abstractmethod
import time
# 抽象策略
class SortStrategy(metaclass=ABCMeta):
def sort(self, data):
pass
# 具体策略
class QuickSort(SortStrategy):
def sort(self, data):
print("使用快速排序...")
# (此处省略具体实现)
return sorted(data)
class BubbleSort(SortStrategy):
def sort(self, data):
print("使用冒泡排序...")
# (此处省略具体实现)
return sorted(data, reverse=True) # 假设这是另一种排序逻辑
# 上下文
class Sorter:
def __init__(self, strategy: SortStrategy):
self.strategy = strategy
def set_strategy(self, strategy: SortStrategy):
print("---切换排序策略---")
self.strategy = strategy
def execute_sort(self, data):
start = time.time()
result = self.strategy.sort(data)
duration = time.time() - start
print(f"排序结果: {result}, 耗时: {duration:.6f}s")
# 客户端代码
data = [5, 2, 8, 1, 9]
sorter = Sorter(QuickSort())
sorter.execute_sort(data)
sorter.set_strategy(BubbleSort())
sorter.execute_sort(data)
输出:
xxxxxxxxxx
使用快速排序...
排序结果: [1, 2, 5, 8, 9], 耗时: 0.000000s
---切换排序策略---
使用冒泡排序...
排序结果: [9, 8, 5, 2, 1], 耗时: 0.000000s
模板方法模式
定义一个操作中的算法骨架,而将一些步骤的实现延迟到子类中。模板方法使得子类可以不改变一个算法的结构,即可重定义该算法的某些特定步骤。
角色: 抽象类(AbstractClass)、具体类(ConcreteClass)。
xxxxxxxxxx
from abc import ABCMeta, abstractmethod
from time import sleep
# 抽象类 (定义模板方法和钩子操作)
class DocumentProcessor(metaclass=ABCMeta):
# 模板方法,定义了算法的骨架
def process_document(self):
self.open_file()
self.extract_data()
self.analyze_data()
self.close_file()
# 抽象方法 (钩子操作),由子类实现
def extract_data(self):
pass
def analyze_data(self):
pass
# 具体方法
def open_file(self):
print("打开文件...")
def close_file(self):
print("关闭文件...")
# 具体类
class PDFProcessor(DocumentProcessor):
def extract_data(self):
print("从PDF中提取文本和图片...")
def analyze_data(self):
print("分析PDF内容...")
class CSVProcessor(DocumentProcessor):
def extract_data(self):
print("从CSV中提取行和列...")
def analyze_data(self):
print("分析CSV数据...")
# 客户端代码
print("---处理PDF文档---")
pdf_processor = PDFProcessor()
pdf_processor.process_document()
print("\n---处理CSV文档---")
csv_processor = CSVProcessor()
csv_processor.process_document()
输出:
xxxxxxxxxx
---处理PDF文档---
打开文件...
从PDF中提取文本和图片...
分析PDF内容...
关闭文件...
---处理CSV文档---
打开文件...
从CSV中提取行和列...
分析CSV数据...
关闭文件...
命令模式
将一个请求封装成一个对象,从而可以使用不同的请求对客户进行参数化。它能将请求的发送者和接收者解耦。
角色: 命令(Command)、具体命令(ConcreteCommand)、接收者(Receiver)、调用者(Invoker)。
xxxxxxxxxx
from abc import ABCMeta, abstractmethod
# 接收者 (真正的命令执行对象)
class Light:
def turn_on(self):
print("灯已打开")
def turn_off(self):
print("灯已关闭")
# 命令接口
class Command(metaclass=ABCMeta):
def execute(self):
pass
# 具体命令
class TurnOnCommand(Command):
def __init__(self, light: Light):
self.light = light
def execute(self):
self.light.turn_on()
class TurnOffCommand(Command):
def __init__(self, light: Light):
self.light = light
def execute(self):
self.light.turn_off()
# 调用者
class RemoteControl:
def __init__(self):
self.command_history = []
def set_command(self, command: Command):
self.command = command
def press_button(self):
self.command.execute()
self.command_history.append(self.command)
# 客户端代码
living_room_light = Light()
turn_on = TurnOnCommand(living_room_light)
turn_off = TurnOffCommand(living_room_light)
remote = RemoteControl()
remote.set_command(turn_on)
remote.press_button()
remote.set_command(turn_off)
remote.press_button()
输出:
xxxxxxxxxx
灯已打开
灯已关闭
其他行为型模式
访问者模式 (Visitor): 将数据结构与数据操作分离。适用于数据结构稳定但操作易变的情况。
数据访问对象模式 (DAO): 为数据源(如数据库)提供一个抽象的接口,将业务逻辑与数据访问逻辑分离。
前端控制器模式 (Front Controller): 提供一个集中的请求处理机制,所有请求由一个单一的处理程序处理,然后分发给相应的具体处理程序。
拦截过滤器模式 (Intercepting Filter): 对应用程序的请求或响应做一些预处理/后处理。通过过滤器链实现。
0 评论:
发表评论