序言:为什么需要设计模式?
假设你是一位刚接触编程的开发者,接到一个需求:开发一个 “咖啡店订单系统”,支持用户点单咖啡(如浓缩咖啡、美式咖啡),并可以添加配料(如牛奶、糖、香草)。你可能会直接写这样的代码:
class Coffee:
def __init__(self, type: str, has_milk: bool = False, has_sugar: bool = False):
self.type = type
self.has_milk = has_milk
self.has_sugar = has_sugar
def cost(self):
base = 20 if self.type == "Espresso" else 15
milk = 5 if self.has_milk else 0
sugar = 2 if self.has_sugar else 0
return base + milk
def description(self):
desc = self.type
if self.has_milk:
desc += " with Milk"
if self.has_sugar:
desc += " with Sugar"
return desc
这样的代码能满足当前需求,但问题很快就会出现:
- 若新增 10 种咖啡和 5 种配料,构造函数会变得无比复杂;
- 若配料价格变化,需修改
cost方法的硬编码; - 若要添加 “双倍牛奶”“少糖” 等特殊需求,需大面积修改现有代码。
设计模式就是为解决这类 “重复出现的软件设计问题” 而总结的可复用解决方案模板。它不是现成的代码,而是一种 “设计思路”—— 让代码更易读、易维护、易扩展。
1994 年,四位软件工程师(Erich Gamma、Richard Helm、Ralph Johnson、John Vlissides,合称 GoF)出版了《设计模式:可复用面向对象软件的基础》,首次提出 23 种经典设计模式,并将其分为三大类:创建型模式(控制对象创建的方式)、结构型模式(类或对象的组合方式)、行为型模式(对象间的通信与职责分配)。此外,针对 Java 企业级开发场景,还衍生出J2EE 设计模式,用于解决分布式、分层架构下的通用问题。
本文将以零基础可理解的方式,详细讲解这 4 类设计模式,每个模式均包含:现实类比、核心问题、实现步骤、Python 代码示例、应用场景、优缺点。
一、设计模式的分类与核心思想
1. 创建型模式(Creational Patterns):5 种
核心思想:控制对象的创建过程,避免硬编码依赖具体类,提高代码的灵活性。
- 包含:单例模式、工厂方法模式、抽象工厂模式、建造者模式、原型模式。
2. 结构型模式(Structural Patterns):7 种
核心思想:将类或对象组合成更大的结构,同时保持结构的灵活性和可复用性。
- 包含:适配器模式、桥接模式、组合模式、装饰器模式、外观模式、享元模式、代理模式。
3. 行为型模式(Behavioral Patterns):11 种
核心思想:定义对象间的通信规则与职责分配,解决 “对象如何交互” 的问题。
- 包含:责任链模式、命令模式、迭代器模式、中介者模式、备忘录模式、观察者模式、状态模式、策略模式、模板方法模式、访问者模式、解释器模式。
4. J2EE 设计模式:企业级扩展
核心思想:针对 Java EE(企业级 Java 平台)的分层架构、分布式通信、资源管理等场景,提炼的通用解决方案。
- 包含:MVC 模式、数据访问对象(DAO)模式、前端控制器模式、服务定位器模式、业务代表模式等。
二、创建型模式详解
创建型模式的本质是 “封装对象创建的细节”,让客户端代码不直接依赖具体类的构造函数,而是通过 “工厂”“建造者” 等中间层创建对象。
2.1 单例模式(Singleton Pattern)
现实类比:公司的 CEO
一个公司只能有一位 CEO,所有员工咨询 CEO 时,都会找到同一个人。
核心问题:保证一个类仅有一个实例,并提供一个全局访问点。
常用于资源密集型对象(如数据库连接池、线程池、配置管理器),避免重复创建浪费资源。
实现方式:5 种经典实现(Python 版)
(1)饿汉式:类加载时直接创建实例
# 饿汉式单例模式
class SingletonEager:
# 类加载阶段(Python导入时)自动创建唯一实例
__instance = object.__new__(cls)
# 禁止外部通过__init__重复初始化
def __init__(self):
if SingletonEager.__instance is not None:
raise RuntimeError("该类已实例化,请勿重复创建")
# 提供全局访问点
@classmethod
def get_instance(cls):
return cls.__instance
# 测试代码
if __name__ == "__main__":
ceo1 = SingletonEager.get_instance()
ceo2 = SingletonEager.get_instance()
print(f"是否为同一实例:{ceo1 is ceo2}") # 输出:True
优缺点:
- 优点:线程安全(Python 导入时执行,无并发问题)、实现简单;
- 缺点:提前加载,若实例未被使用则浪费资源。
(2)懒汉式:使用时才创建实例
# 懒汉式单例模式(线程不安全)
class SingletonLazy:
__instance = None
def __init__(self):
if SingletonLazy.__instance is not None:
raise RuntimeError("该类已实例化")
@classmethod
def get_instance(cls):
if cls.__instance is None:
cls.__instance = cls() # 第一次调用时创建实例
return cls.__instance
优缺点:
- 优点:延迟加载,节省资源;
- 缺点:多线程环境下可能创建多个实例(两个线程同时检查到__instance 为 None,都会执行创建)。
(3)线程安全的懒汉式:加锁保护
import threading
# 线程安全的懒汉式单例模式
class SingletonLazySafe:
__instance = None
__lock = threading.Lock() # 定义锁对象
def __init__(self):
if SingletonLazySafe.__instance is not None:
raise RuntimeError("该类已实例化")
@classmethod
def get_instance(cls):
with cls.__lock: # 加锁,确保同一时间只有一个线程执行创建逻辑
if cls.__instance is None:
cls.__instance = cls()
return cls.__instance
优缺点:
- 优点:线程安全、延迟加载;
- 缺点:每次获取实例都要加锁,效率低。
(4)双重检查锁定(DCL):优化效率
import threading
# 双重检查锁定单例模式
class SingletonDCL:
__instance = None
__lock = threading.Lock()
def __init__(self):
if SingletonDCL.__instance is not None:
raise RuntimeError("该类已实例化")
@classmethod
def get_instance(cls):
# 第一次检查:无锁,快速判断是否已创建实例
if cls.__instance is None:
with cls.__lock:
# 第二次检查:加锁后再次判断(防止多个线程进入第一次检查的间隙后重复创建)
if cls.__instance is None:
cls.__instance = cls()
return cls.__instance
优缺点:
- 优点:线程安全、延迟加载、效率高(仅第一次创建时加锁);
- 缺点:实现稍复杂。
(5)元类实现:Pythonic 的单例
Python 中,“类是元类的实例”。通过定义元类,可以控制类的创建过程,确保仅生成一个实例。
# 元类单例模式
class SingletonMeta(type):
__instances = {} # 保存所有单例类的实例
# 控制类的实例化过程
def __call__(cls, *args, **kwargs):
if cls not in cls.__instances:
# 创建实例并保存
cls.__instances[cls] = super().__call__(*args, **kwargs)
return cls.__instances[cls]
# 使用元类创建单例类
class ConfigManager(metaclass=SingletonMeta):
def __init__(self):
self.config = {"host": "localhost", "port": 3306}
# 测试代码
if __name__ == "__main__":
config1 = ConfigManager()
config2 = ConfigManager()
print(f"是否为同一实例:{config1 is config2}") # 输出:True
优缺点:
- 优点:代码简洁、支持继承、最符合 Python 特性;
- 缺点:需要理解元类的工作机制,对零基础开发者有一定门槛。
应用场景
- 数据库连接池、Redis 客户端实例;
- 系统配置管理器、日志记录器;
- 线程池、线程安全的计数器。
2.2 工厂方法模式(Factory Method Pattern)
现实类比:手机工厂
苹果手机由苹果工厂生产,华为手机由华为工厂生产 —— 所有工厂都遵循 “生产手机” 的统一标准,但具体实现不同。
核心问题:定义一个创建对象的接口,但让子类决定实例化哪个类,将对象创建的细节延迟到子类。
解决 “客户端直接依赖具体类” 的问题,符合开闭原则(对扩展开放,对修改关闭)。
实现步骤
- 定义抽象产品类:规范产品的共同方法(如
Phone类的call()); - 定义具体产品类:继承抽象产品类,实现具体逻辑(如
IPhone、HuaweiPhone); - 定义抽象工厂类:规范工厂的创建方法(如
PhoneFactory的create_phone()); - 定义具体工厂类:继承抽象工厂类,实现产品的创建逻辑(如
IPhoneFactory、HuaweiPhoneFactory)。
Python 代码示例
# 1. 抽象产品类:手机
class Phone:
def call(self):
pass
# 2. 具体产品类:苹果手机
class IPhone(Phone):
def call(self):
print("IPhone正在打电话")
# 2. 具体产品类:华为手机
class HuaweiPhone(Phone):
def call(self):
print("HuaweiPhone正在打电话")
# 3. 抽象工厂类:手机工厂
class PhoneFactory:
def create_phone(self):
pass
# 4. 具体工厂类:苹果手机工厂
class IPhoneFactory(PhoneFactory):
def create_phone(self):
return IPhone() # 创建并返回苹果手机实例
# 4. 具体工厂类:华为手机工厂
class HuaweiPhoneFactory(PhoneFactory):
def create_phone(self):
return HuaweiPhone() # 创建并返回华为手机实例
# 客户端代码
if __name__ == "__main__":
# 客户端仅依赖抽象工厂和抽象产品,不直接接触具体类
iphone_factory = IPhoneFactory()
iphone = iphone_factory.create_phone()
iphone.call() # 输出:IPhone正在打电话
huawei_factory = HuaweiPhoneFactory()
huawei = huawei_factory.create_phone()
huawei.call() # 输出:HuaweiPhone正在打电话
# 若新增小米手机,仅需:
# 1. 定义XiaomiPhone类继承Phone;
# 2. 定义XiaomiPhoneFactory类继承PhoneFactory;
# 客户端无需修改任何现有代码,符合开闭原则。
应用场景
- 当客户端需要创建多个具有共同接口的产品时;
- 当需要将产品创建的细节与客户端分离时;
- 当产品类型可能随时间扩展时。
与 “简单工厂模式” 的区别
简单工厂模式是 “工厂方法模式的简化版”:仅定义一个静态工厂类,通过条件判断创建不同产品。但简单工厂模式违反开闭原则(新增产品需修改工厂的条件判断逻辑),因此不属于 GoF 的 23 种经典设计模式。
2.3 抽象工厂模式(Abstract Factory Pattern)
现实类比:电子设备工厂
苹果工厂不仅生产手机,还生产电脑、平板 —— 所有产品都属于 “苹果生态”;华为工厂同理,生产 “华为生态” 的设备。抽象工厂模式用于创建一系列相关或相互依赖的产品族。
核心问题:提供一个创建一系列相关或相互依赖产品的接口,无需指定其具体类。
解决 “工厂方法模式仅能创建单个产品” 的问题,适用于需要 “配套产品” 的场景。
实现步骤
- 定义抽象产品族:如
Phone、Computer; - 定义具体产品族:如
ApplePhone、AppleComputer(苹果产品族),HuaweiPhone、HuaweiComputer(华为产品族); - 定义抽象工厂类:规范创建产品族的方法(如
DeviceFactory的create_phone()、create_computer()); - 定义具体工厂类:继承抽象工厂类,实现产品族的创建逻辑(如
AppleFactory、HuaweiFactory)。
Python 代码示例
# 1. 抽象产品族1:手机
class Phone:
def call(self):
pass
# 2. 具体产品族1-1:苹果手机
class ApplePhone(Phone):
def call(self):
print("Apple Phone正在打电话")
# 2. 具体产品族1-2:华为手机
class HuaweiPhone(Phone):
def call(self):
print("Huawei Phone正在打电话")
# 1. 抽象产品族2:电脑
class Computer:
def run(self):
pass
# 2. 具体产品族2-1:苹果电脑
class AppleComputer(Computer):
def run(self):
print("Apple Computer正在运行")
# 2. 具体产品族2-2:华为电脑
class HuaweiComputer(Computer):
def run(self):
print("Huawei Computer正在运行")
# 3. 抽象工厂类:电子设备工厂
class DeviceFactory:
def create_phone(self):
pass
def create_computer(self):
pass
# 4. 具体工厂类1:苹果设备工厂
class AppleFactory(DeviceFactory):
def create_phone(self):
return ApplePhone()
def create_computer(self):
return AppleComputer()
# 4. 具体工厂类2:华为设备工厂
class HuaweiFactory(DeviceFactory):
def create_phone(self):
return HuaweiPhone()
def create_computer(self):
return HuaweiComputer()
# 客户端代码
if __name__ == "__main__":
# 创建苹果设备工厂
apple_factory = AppleFactory()
apple_phone = apple_factory.create_phone()
apple_computer = apple_factory.create_computer()
apple_phone.call() # 输出:Apple Phone正在打电话
apple_computer.run() # 输出:Apple Computer正在运行
# 创建华为设备工厂
huawei_factory = HuaweiFactory()
huawei_phone = huawei_factory.create_phone()
huawei_computer = huawei_factory.create_computer()
huawei_phone.call() # 输出:Huawei Phone正在打电话
huawei_computer.run() # 输出:Huawei Computer正在运行
# 若新增小米设备族,仅需:
# 1. 定义XiaomiPhone、XiaomiComputer;
# 2. 定义XiaomiFactory;
# 客户端无需修改现有代码。
应用场景
- 当需要创建 “产品族”(如同一品牌的手机、电脑、平板)时;
- 当系统需要独立于产品的创建、组合和表示方式时;
- 当系统配置为使用多个产品族中的一个时。
2.4 建造者模式(Builder Pattern)
现实类比:汽车组装
汽车由引擎、底盘、车身、轮胎等部件组成,不同型号的汽车(如 SUV、轿车)部件组合方式不同。建造者模式将 “产品的构建过程” 与 “最终产品” 分离,让同一构建过程可以创建不同的产品。
核心问题:将复杂对象的构建过程分解为多个步骤,允许逐步构建并灵活组合,最终生成不同配置的产品。
解决 “复杂对象构造函数参数过多” 的问题,如Car(engine, chassis, body, tire, color, seat, ...)。
实现步骤
- 定义产品类:包含所有部件的复杂对象(如
Car); - 定义建造者接口:规范产品的构建步骤(如
CarBuilder的build_engine()、build_chassis()等); - 定义具体建造者:实现建造者接口,构建具体的产品部件(如
SUVBuilder、SedanBuilder); - 定义指挥者:管理建造者的构建流程,确保按顺序执行步骤(如
CarDirector的construct_car())。
Python 代码示例
# 1. 产品类:汽车
class Car:
def __init__(self):
self.engine = None # 引擎
self.chassis = None # 底盘
self.body = None # 车身
self.tire = None # 轮胎
self.color = "白色" # 默认颜色
def __str__(self):
return f"汽车配置:引擎={self.engine},底盘={self.chassis},车身={self.body},轮胎={self.tire},颜色={self.color}"
# 2. 建造者接口:汽车建造者
class CarBuilder:
def build_engine(self):
pass
def build_chassis(self):
pass
def build_body(self):
pass
def build_tire(self):
pass
def get_car(self):
pass
# 3. 具体建造者1:SUV建造者
class SUVBuilder(CarBuilder):
def __init__(self):
self.car = Car() # 创建空汽车对象
def build_engine(self):
self.car.engine = "3.0T V6引擎"
def build_chassis(self):
self.car.chassis = "SUV专用底盘"
def build_body(self):
self.car.body = "SUV硬派车身"
def build_tire(self):
self.car.tire = "越野轮胎"
def get_car(self):
return self.car # 返回构建完成的SUV
# 3. 具体建造者2:轿车建造者
class SedanBuilder(CarBuilder):
def __init__(self):
self.car = Car()
def build_engine(self):
self.car.engine = "1.5T L4引擎"
def build_chassis(self):
self.car.chassis = "轿车舒适底盘"
def build_body(self):
self.car.body = "轿车流线车身"
def build_tire(self):
self.car.tire = "静音轮胎"
def get_car(self):
return self.car # 返回构建完成的轿车
# 4. 指挥者:汽车组装导演
class CarDirector:
def __init__(self, builder):
self.builder = builder
# 控制构建流程:按顺序执行建造步骤
def construct_car(self):
self.builder.build_engine()
self.builder.build_chassis()
self.builder.build_body()
self.builder.build_tire()
# 客户端代码
if __name__ == "__main__":
# 构建SUV
suv_builder = SUVBuilder()
suv_director = CarDirector(suv_builder)
suv_director.construct_car() # 执行组装流程
suv = suv_builder.get_car()
print(suv) # 输出:汽车配置:引擎=3.0T V6引擎,底盘=SUV专用底盘,车身=SUV硬派车身,轮胎=越野轮胎,颜色=白色
# 构建轿车
sedan_builder = SedanBuilder()
sedan_director = CarDirector(sedan_builder)
sedan_director.construct_car()
sedan = sedan_builder.get_car()
print(sedan) # 输出:汽车配置:引擎=1.5T L4引擎,底盘=轿车舒适底盘,车身=轿车流线车身,轮胎=静音轮胎,颜色=白色
# 自定义颜色(扩展构建步骤)
sedan.color = "黑色"
print(sedan) # 输出:颜色=黑色
应用场景
- 当产品是复杂对象(包含多个部件、构建步骤)时;
- 当需要灵活配置产品(如不同型号、不同颜色、不同功能)时;
- 当需要将产品的构建与表示分离时。
2.5 原型模式(Prototype Pattern)
现实类比:复印机
当需要复制一份复杂文档时,直接使用复印机复制(克隆)比重新编写效率更高。原型模式通过 “克隆已有对象” 的方式创建新对象,避免重复执行复杂的初始化过程。
核心问题:通过克隆已有实例创建新对象,而不是通过构造函数重新初始化。
解决 “创建复杂对象的成本过高” 的问题,如需要从数据库查询大量数据才能初始化的对象。
实现方式:浅克隆与深克隆
- 浅克隆:仅克隆对象本身及基本数据类型成员,引用类型成员仍指向原对象(Python 中
copy.copy()); - 深克隆:克隆对象本身及所有成员(包括引用类型成员),生成完全独立的副本(Python 中
copy.deepcopy())。
Python 代码示例
import copy
# 原型类:文档
class Document:
def __init__(self, title: str, content: str, images: list):
self.title = title
self.content = content
self.images = images # 引用类型成员(列表)
# 浅克隆方法
def shallow_copy(self):
return copy.copy(self)
# 深克隆方法
def deep_copy(self):
return copy.deepcopy(self)
# 客户端代码
if __name__ == "__main__":
# 原始文档
original_doc = Document("设计模式", "这是一篇关于设计模式的文档", ["图1.png", "图2.png"])
print(f"原始文档:标题={original_doc.title},内容={original_doc.content},图片={original_doc.images}")
print(f"原始文档图片列表地址:{id(original_doc.images)}")
# 浅克隆
shallow_clone = original_doc.shallow_copy()
shallow_clone.title = "设计模式(浅克隆版)"
shallow_clone.images.append("图3.png") # 修改引用类型成员
print(f"\n浅克隆文档:标题={shallow_clone.title},内容={shallow_clone.content},图片={shallow_clone.images}")
print(f"浅克隆文档图片列表地址:{id(shallow_clone.images)}") # 与原始文档相同
print(f"原始文档图片列表:{original_doc.images}") # 原始文档的图片列表也被修改了!
# 深克隆
deep_clone = original_doc.deep_copy()
deep_clone.title = "设计模式(深克隆版)"
deep_clone.images.append("图4.png") # 修改引用类型成员
print(f"\n深克隆文档:标题={deep_clone.title},内容={deep_clone.content},图片={deep_clone.images}")
print(f"深克隆文档图片列表地址:{id(deep_clone.images)}") # 与原始文档不同
print(f"原始文档图片列表:{original_doc.images}") # 原始文档的图片列表未被修改
应用场景
- 当创建对象的成本远高于克隆成本时(如需要网络请求、数据库查询、复杂计算);
- 当需要批量创建相似对象时;
- 当需要避免构造函数的副作用时。
三、结构型模式详解
结构型模式的本质是 “组合类或对象以形成更大的结构”,同时保持结构的灵活性和可复用性。它关注的是类与对象之间的 “关系”(如继承、聚合、组合)。
3.1 适配器模式(Adapter Pattern)
现实类比:电源适配器
中国的电源插座是 220V 交流电,而手机充电器需要 5V 直流电 —— 电源适配器将 220V 转换为 5V,让不兼容的接口可以协同工作。
核心问题:将一个类的接口转换为客户端期望的另一个接口,解决 “接口不兼容” 的问题。
适配器模式分为类适配器(通过继承实现)和对象适配器(通过组合实现),后者更灵活,是 GoF 推荐的方式。
实现步骤
- 定义目标接口:客户端期望的接口(如
Target); - 定义适配者类:需要被适配的现有接口(如
Adaptee); - 定义适配器类:实现目标接口,并组合或继承适配者类,将适配者的方法转换为目标接口的方法。
Python 代码示例:对象适配器
# 1. 目标接口:客户端期望的电源接口(输出5V)
class TargetPower:
def output_5v(self):
pass
# 2. 适配者类:现有的电源接口(输出220V)
class AdapteePower:
def output_220v(self):
print("输出220V交流电")
# 3. 适配器类:电源适配器(将220V转换为5V)
class PowerAdapter(TargetPower):
def __init__(self, adaptee):
self.adaptee = adaptee # 组合适配者对象
def output_5v(self):
self.adaptee.output_220v() # 调用适配者的方法
print("通过适配器转换为5V直流电") # 适配逻辑
# 客户端代码
if __name__ == "__main__":
# 客户端仅依赖目标接口TargetPower
power_220v = AdapteePower()
adapter = PowerAdapter(power_220v)
adapter.output_5v() # 输出:
# 输出220V交流电
# 通过适配器转换为5V直流电
应用场景
- 当需要使用现有类,但它的接口与客户端期望的不一致时;
- 当需要复用多个现有类,但它们的接口不统一时;
- 当需要与第三方库集成,但第三方库的接口与系统接口不兼容时。
3.2 桥接模式(Bridge Pattern)
现实类比:笔与颜色
笔有 “钢笔”“铅笔” 等类型,颜色有 “红色”“蓝色” 等 —— 若为每种笔 - 颜色组合创建一个类(如RedPen、BluePen、RedPencil、BluePencil),会导致类爆炸(若有 N 种笔和 M 种颜色,则需 N×M 个类)。桥接模式将 “笔类型” 与 “颜色” 分离为两个独立的维度,通过 “桥接”(组合)的方式灵活组合。
核心问题:将抽象部分与实现部分分离,使它们可以独立变化,避免类爆炸。
桥接模式的关键是组合替代继承:将一个维度的实现作为另一个维度的成员变量。
实现步骤
- 定义实现维度接口:如颜色接口
Color; - 定义具体实现类:如
RedColor、BlueColor; - 定义抽象维度类:如笔类
Pen,组合实现维度的接口; - 定义具体抽象类:如
Pen的子类FountainPen、Pencil。
Python 代码示例
# 1. 实现维度接口:颜色
class Color:
def apply_color(self):
pass
# 2. 具体实现类1:红色
class RedColor(Color):
def apply_color(self):
return "红色"
# 2. 具体实现类2:蓝色
class BlueColor(Color):
def apply_color(self):
return "蓝色"
# 3. 抽象维度类:笔
class Pen:
def __init__(self, color: Color):
self.color = color # 桥接:组合颜色接口
def draw(self):
pass
# 4. 具体抽象类1:钢笔
class FountainPen(Pen):
def draw(self):
print(f"用{self.color.apply_color()}的钢笔绘画")
# 4. 具体抽象类2:铅笔
class Pencil(Pen):
def draw(self):
print(f"用{self.color.apply_color()}的铅笔绘画")
# 客户端代码
if __name__ == "__main__":
# 红色钢笔
red_pen = FountainPen(RedColor())
red_pen.draw() # 输出:用红色的钢笔绘画
# 蓝色铅笔
blue_pencil = Pencil(BlueColor())
blue_pencil.draw() # 输出:用蓝色的铅笔绘画
# 新增绿色(实现维度扩展)
# class GreenColor(Color): ...
# green_pen = FountainPen(GreenColor())
# 无需修改Pen或FountainPen类
# 新增毛笔(抽象维度扩展)
# class BrushPen(Pen): ...
# red_brush = BrushPen(RedColor())
# 无需修改Color或RedColor类
应用场景
- 当一个类存在两个或多个独立变化的维度(如笔的类型与颜色、操作系统与 UI 组件)时;
- 当需要避免类爆炸(如 N×M 个类)时;
- 当需要扩展多个维度(如新增笔类型或颜色)时。
3.3 组合模式(Composite Pattern)
现实类比:文件系统
文件系统由 “文件” 和 “文件夹” 组成 —— 文件夹可以包含文件或子文件夹,无论操作文件还是文件夹,都可以使用统一的接口(如open()、delete())。
核心问题:将对象组合成树形结构,并让客户端可以统一处理单个对象和组合对象。
组合模式的关键是抽象组件类:规范叶子节点(单个对象)和容器节点(组合对象)的共同接口。
实现步骤
- 定义抽象组件类:如
FileSystemItem,规范open()、delete()等方法; - 定义叶子节点类:如
File,实现抽象组件的方法; - 定义容器节点类:如
Folder,实现抽象组件的方法,并维护一个子组件列表。
Python 代码示例
# 1. 抽象组件类:文件系统项
class FileSystemItem:
def open(self):
pass
def delete(self):
pass
# 2. 叶子节点类:文件
class File(FileSystemItem):
def __init__(self, name: str):
self.name = name
def open(self):
print(f"打开文件:{self.name}")
def delete(self):
print(f"删除文件:{self.name}")
# 3. 容器节点类:文件夹
class Folder(FileSystemItem):
def __init__(self, name: str):
self.name = name
self.children = [] # 维护子组件列表
# 新增子组件
def add(self, item: FileSystemItem):
self.children.append(item)
# 删除子组件
def remove(self, item: FileSystemItem):
self.children.remove(item)
def open(self):
print(f"打开文件夹:{self.name}")
# 递归打开所有子组件
for child in self.children:
child.open()
def delete(self):
print(f"删除文件夹:{self.name}")
# 递归删除所有子组件
for child in self.children:
child.delete()
# 客户端代码
if __name__ == "__main__":
# 创建文件
file1 = File("笔记.txt")
file2 = File("照片.jpg")
file3 = File("文档.pdf")
# 创建文件夹
folder1 = Folder("个人资料")
folder2 = Folder("工作资料")
# 组合结构
folder1.add(file1)
folder1.add(file2)
folder2.add(file3)
# 创建根文件夹
root = Folder("根目录")
root.add(folder1)
root.add(folder2)
# 统一处理组合对象和单个对象
print("=== 打开根目录 ===")
root.open() # 输出:
# 打开文件夹:根目录
# 打开文件夹:个人资料
# 打开文件:笔记.txt
# 打开文件:照片.jpg
# 打开文件夹:工作资料
# 打开文件:文档.pdf
print("\n=== 删除个人资料文件夹 ===")
folder1.delete() # 输出:
# 删除文件夹:个人资料
# 删除文件:笔记.txt
# 删除文件:照片.jpg
应用场景
- 当需要处理树形结构的数据(如文件系统、菜单、组织结构)时;
- 当需要统一接口处理单个对象和组合对象时;
- 当需要递归遍历组合结构时。
3.4 装饰器模式(Decorator Pattern)
现实类比:咖啡配料
浓缩咖啡(Espresso)可以加牛奶、糖、香草等配料 —— 每种配料都会增加咖啡的价格和描述,但不需要修改咖啡本身的类。装饰器模式通过动态包裹对象的方式,在不修改原有类的情况下添加功能。
核心问题:不修改原有类的情况下,动态地为对象添加功能,支持多层嵌套装饰。
装饰器模式与 Python 的语法糖@decorator思想一致,但 GoF 的装饰器模式是一种 “对象组合” 的设计思路。
实现步骤
- 定义抽象组件类:如
Coffee,规范cost()(价格)和description()(描述)方法; - 定义具体组件类:如
Espresso、Americano,实现抽象组件的方法; - 定义抽象装饰器类:继承抽象组件类,组合抽象组件对象;
- 定义具体装饰器类:如
Milk、Sugar、Vanilla,实现抽象装饰器的方法,添加新功能。
Python 代码示例
# 1. 抽象组件类:咖啡
class Coffee:
def cost(self) -> float:
pass
def description(self) -> str:
pass
# 2. 具体组件类1:浓缩咖啡
class Espresso(Coffee):
def cost(self) -> float:
return 20.0 # 基础价格20元
def description(self) -> str:
return "Espresso" # 基础描述
# 2. 具体组件类2:美式咖啡
class Americano(Coffee):
def cost(self) -> float:
return 15.0
def description(self) -> str:
return "Americano"
# 3. 抽象装饰器类:咖啡配料
class CondimentDecorator(Coffee):
def __init__(self, coffee: Coffee):
self._coffee = coffee # 组合咖啡对象
def cost(self) -> float:
return self._coffee.cost()
def description(self) -> str:
return self._coffee.description()
# 4. 具体装饰器类1:牛奶(价格+5元)
class Milk(CondimentDecorator):
def cost(self) -> float:
return self._coffee.cost() + 5.0
def description(self) -> str:
return f"{self._coffee.description()}, Milk"
# 4. 具体装饰器类2:糖(价格+2元)
class Sugar(CondimentDecorator):
def cost(self) -> float:
return self._coffee.cost() + 2.0
def description(self) -> str:
return f"{self._coffee.description()}, Sugar"
# 4. 具体装饰器类3:香草(价格+3元)
class Vanilla(CondimentDecorator):
def cost(self) -> float:
return self._coffee.cost() + 3.0
def description(self) -> str:
return f"{self._coffee.description()}, Vanilla"
# 客户端代码
if __name__ == "__main__":
# 纯浓缩咖啡
espresso = Espresso()
print(f"{espresso.description()}: {espresso.cost()}元") # 输出:Espresso: 20.0元
# 浓缩咖啡+牛奶
espresso_with_milk = Milk(espresso)
print(f"{espresso_with_milk.description()}: {espresso_with_milk.cost()}元") # 输出:Espresso, Milk: 25.0元
# 浓缩咖啡+牛奶+糖+香草
espresso_with_all = Vanilla(Sugar(Milk(espresso)))
print(f"{espresso_with_all.description()}: {espresso_with_all.cost()}元") # 输出:Espresso, Milk, Sugar, Vanilla: 30.0元
# 新增配料“焦糖”仅需定义Caramel类,无需修改原有代码
# class Caramel(CondimentDecorator): ...
# espresso_with_caramel = Caramel(espresso)
# print(f"{espresso_with_caramel.description()}: {espresso_with_caramel.cost()}元")
应用场景
- 当需要动态为对象添加功能,且功能可以组合时;
- 当修改原有类会违反开闭原则时;
- 当需要多层嵌套功能(如咖啡加多种配料)时。
3.5 外观模式(Facade Pattern)
现实类比:电脑开机
电脑开机需要依次启动 CPU、内存、硬盘、显卡、显示器等部件 —— 若让用户手动操作每一步,会非常复杂。外观模式提供一个 “统一的入口”(如开机按钮),隐藏内部的复杂流程。
核心问题:为复杂的子系统提供一个简化的统一接口,降低客户端与子系统的耦合度。
外观模式的关键是封装子系统的复杂性,只暴露客户端需要的接口。
实现步骤
- 定义子系统类:如
CPU、Memory、HardDisk等,实现各自的功能; - 定义外观类:组合所有子系统对象,提供简化的统一接口(如
ComputerFacade的startup()、shutdown())。
Python 代码示例
# 1. 子系统类1:CPU
class CPU:
def start(self):
print("CPU开始运行")
def shutdown(self):
print("CPU停止运行")
# 1. 子系统类2:内存
class Memory:
def load(self):
print("内存加载完成")
def clear(self):
print("内存清空")
# 1. 子系统类3:硬盘
class HardDisk:
def read(self):
print("硬盘读取数据")
def write(self):
print("硬盘写入数据")
# 1. 子系统类4:显示器
class Monitor:
def turn_on(self):
print("显示器开机")
def turn_off(self):
print("显示器关机")
# 2. 外观类:电脑外观
class ComputerFacade:
def __init__(self):
self.cpu = CPU()
self.memory = Memory()
self.hard_disk = HardDisk()
self.monitor = Monitor()
# 统一的开机接口
def startup(self):
print("=== 开始开机 ===")
self.cpu.start()
self.memory.load()
self.hard_disk.read()
self.monitor.turn_on()
print("=== 开机完成 ===")
# 统一的关机接口
def shutdown(self):
print("=== 开始关机 ===")
self.monitor.turn_off()
self.hard_disk.write()
self.memory.clear()
self.cpu.shutdown()
print("=== 关机完成 ===")
# 客户端代码
if __name__ == "__main__":
# 客户端仅依赖外观类,无需了解子系统的细节
computer = ComputerFacade()
computer.startup() # 输出:
# === 开始开机 ===
# CPU开始运行
# 内存加载完成
# 硬盘读取数据
# 显示器开机
# === 开机完成 ===
computer.shutdown() # 输出:
# === 开始关机 ===
# 显示器关机
# 硬盘写入数据
# 内存清空
# CPU停止运行
# === 关机完成 ===
应用场景
- 当需要简化复杂子系统的使用时;
- 当需要降低客户端与子系统的耦合度时;
- 当需要为子系统提供统一入口时(如 API 网关)。
3.6 享元模式(Flyweight Pattern)
现实类比:围棋棋盘
围棋棋盘有 361 个交叉点,每个交叉点可以放置黑色或白色棋子。若为每个交叉点创建一个 “棋子” 对象,会占用大量内存 —— 享元模式将 “棋子颜色”(内部状态)与 “位置”(外部状态)分离,相同颜色的棋子共享一个对象。
核心问题:共享对象以减少内存消耗,适用于 “大量相似对象” 的场景。
享元模式的关键是内部状态与外部状态的分离:
- 内部状态:对象可共享的不变部分(如棋子颜色);
- 外部状态:对象随环境变化的可变部分(如棋子位置)。
实现步骤
- 定义享元类:包含内部状态(如
Chessman的color); - 定义享元工厂:管理享元对象,确保相同内部状态的对象仅创建一个;
- 客户端通过享元工厂获取享元对象,并传入外部状态(如位置)。
Python 代码示例
# 1. 享元类:围棋棋子
class Chessman:
def __init__(self, color: str):
self._color = color # 内部状态:颜色(可共享)
def show(self, position: tuple):
# position:外部状态(不可共享,由客户端传入)
print(f"棋子颜色:{self._color},位置:{position}")
# 2. 享元工厂:棋子工厂
class ChessmanFactory:
_chessmen = {} # 缓存享元对象
@classmethod
def get_chessman(cls, color: str):
# 若缓存中不存在,创建并缓存;否则直接返回
if color not in cls._chessmen:
cls._chessmen[color] = Chessman(color)
return cls._chessmen[color]
# 客户端代码
if __name__ == "__main__":
# 获取黑色棋子享元对象
black_chessman1 = ChessmanFactory.get_chessman("黑色")
black_chessman2 = ChessmanFactory.get_chessman("黑色")
print(f"黑色棋子是否为同一对象:{black_chessman1 is black_chessman2}") # 输出:True
# 获取白色棋子享元对象
white_chessman1 = ChessmanFactory.get_chessman("白色")
white_chessman2 = ChessmanFactory.get_chessman("白色")
print(f"白色棋子是否为同一对象:{white_chessman1 is white_chessman2}") # 输出:True
# 显示棋子(传入外部状态位置)
black_chessman1.show((1, 2)) # 输出:棋子颜色:黑色,位置:(1, 2)
black_chessman2.show((3, 4)) # 输出:棋子颜色:黑色,位置:(3, 4)
white_chessman1.show((5, 6)) # 输出:棋子颜色:白色,位置:(5, 6)
white_chessman2.show((7, 8)) # 输出:棋子颜色:白色,位置:(7, 8)
应用场景
- 当需要创建大量相似对象(如文字、图标、棋子)时;
- 当对象的内部状态占比高,外部状态可由客户端传入时;
- 当需要减少内存消耗时(如游戏开发、GUI 界面)。
3.7 代理模式(Proxy Pattern)
现实类比:代理服务器
用户访问国外网站时,会通过代理服务器转发请求 —— 代理服务器与国外网站通信,将响应返回给用户。代理模式为对象提供一个 “代理”,控制对原对象的访问。
核心问题:为另一个对象提供代理,以控制对原对象的访问(如权限控制、延迟加载、日志记录)。
代理模式的类型
- 静态代理:编译时就确定代理类;
- 动态代理:运行时动态生成代理类(如 Python 的
functools.wraps、Java 的Proxy); - 虚拟代理:延迟加载原对象(如大图片加载前先显示占位符);
- 保护代理:控制原对象的访问权限;
- 远程代理:代理远程对象(如 RPC 调用)。
Python 代码示例:静态代理(日志记录)
# 1. 业务接口:用户服务
class UserService:
def create_user(self, username: str):
pass
def get_user(self, user_id: int):
pass
# 2. 具体业务类:用户服务实现
class UserServiceImpl(UserService):
def create_user(self, username: str):
print(f"创建用户:{username}")
def get_user(self, user_id: int):
print(f"获取用户:{user_id}")
# 3. 代理类:用户服务代理(添加日志记录功能)
class UserServiceProxy(UserService):
def __init__(self, user_service: UserService):
self._user_service = user_service # 组合具体业务对象
def create_user(self, username: str):
# 代理逻辑:日志记录
print(f"【日志】开始调用create_user,参数:username={username}")
# 调用原对象的方法
self._user_service.create_user(username)
print(f"【日志】结束调用create_user")
def get_user(self, user_id: int):
print(f"【日志】开始调用get_user,参数:user_id={user_id}")
self._user_service.get_user(user_id)
print(f"【日志】结束调用get_user")
# 客户端代码
if __name__ == "__main__":
# 创建具体业务对象
user_service = UserServiceImpl()
# 创建代理对象
proxy = UserServiceProxy(user_service)
# 通过代理对象调用业务方法
proxy.create_user("张三") # 输出:
# 【日志】开始调用create_user,参数:username=张三
# 创建用户:张三
# 【日志】结束调用create_user
proxy.get_user(1) # 输出:
# 【日志】开始调用get_user,参数:user_id=1
# 获取用户:1
# 【日志】结束调用get_user
应用场景
- 当需要控制对原对象的访问权限时;
- 当需要为原对象添加额外功能(如日志、缓存)时;
- 当需要延迟加载原对象(如大文件、数据库查询)时;
- 当需要代理远程对象(如 RPC、API 调用)时。
四、行为型模式详解
行为型模式的本质是 “定义对象间的通信规则与职责分配”,解决 “对象如何交互” 的问题。它关注的是对象的 “行为” 和 “协作”。
4.1 责任链模式(Chain of Responsibility Pattern)
现实类比:公司审批流程
员工请假需要经过组长→部门经理→CEO 的审批:组长可审批 1 天以内的请假,部门经理可审批 3 天以内的请假,CEO 可审批 10 天以内的请假。若审批人无权审批,则自动将请求传递给下一个审批人。
核心问题:将请求的发送者与接收者解耦,让多个接收者都有机会处理请求,请求沿着 “责任链” 依次传递,直到被处理。
实现步骤
- 定义请求类:包含请求的信息(如
LeaveRequest的days、reason); - 定义抽象处理者类:规范处理请求的方法(如
Approver的approve()),并维护下一个处理者的引用; - 定义具体处理者类:如
TeamLeader、DepartmentManager、CEO,实现处理请求的逻辑,若无法处理则传递给下一个处理者。
Python 代码示例
# 1. 请求类:请假请求
class LeaveRequest:
def __init__(self, days: int, reason: str):
self.days = days # 请假天数
self.reason = reason # 请假理由
# 2. 抽象处理者类:审批人
class Approver:
def __init__(self, name: str):
self.name = name
self._next_approver = None # 下一个审批人
# 设置下一个审批人
def set_next(self, approver):
self._next_approver = approver
return self # 链式调用
# 处理请假请求
def approve(self, request: LeaveRequest):
pass
# 3. 具体处理者类1:组长(可审批1天以内)
class TeamLeader(Approver):
def approve(self, request: LeaveRequest):
if request.days <= 1:
print(f"组长{self.name}审批通过请假:{request.days}天,理由:{request.reason}")
elif self._next_approver:
print(f"组长{self.name}无权审批,转交给下一个审批人")
self._next_approver.approve(request)
else:
print(f"请假请求无人审批")
# 3. 具体处理者类2:部门经理(可审批3天以内)
class DepartmentManager(Approver):
def approve(self, request: LeaveRequest):
if request.days <= 3:
print(f"部门经理{self.name}审批通过请假:{request.days}天,理由:{request.reason}")
elif self._next_approver:
print(f"部门经理{self.name}无权审批,转交给下一个审批人")
self._next_approver.approve(request)
else:
print(f"请假请求无人审批")
# 3. 具体处理者类3:CEO(可审批10天以内)
class CEO(Approver):
def approve(self, request: LeaveRequest):
if request.days <= 10:
print(f"CEO{self.name}审批通过请假:{request.days}天,理由:{request.reason}")
else:
print(f"CEO{self.name}拒绝审批请假:{request.days}天,理由:超过最大请假天数")
# 客户端代码
if __name__ == "__main__":
# 创建审批人
team_leader = TeamLeader("张三")
department_manager = DepartmentManager("李四")
ceo = CEO("王五")
# 构建责任链:组长→部门经理→CEO
team_leader.set_next(department_manager).set_next(ceo)
# 测试1:请假0.5天(组长审批)
request1 = LeaveRequest(0.5, "感冒")
print("\n=== 请假请求1 ===")
team_leader.approve(request1) # 输出:组长张三审批通过请假:0.5天,理由:感冒
# 测试2:请假2天(部门经理审批)
request2 = LeaveRequest(2, "家中有事")
print("\n=== 请假请求2 ===")
team_leader.approve(request2) # 输出:组长张三无权审批,转交给下一个审批人;部门经理李四审批通过请假:2天,理由:家中有事
# 测试3:请假5天(CEO审批)
request3 = LeaveRequest(5, "探亲")
print("\n=== 请假请求3 ===")
team_leader.approve(request3) # 输出:组长张三无权审批;部门经理李四无权审批;CEO王五审批通过请假:5天,理由:探亲
# 测试4:请假15天(CEO拒绝)
request4 = LeaveRequest(15, "旅游")
print("\n=== 请假请求4 ===")
team_leader.approve(request4) # 输出:CEO王五拒绝审批请假:15天,理由:超过最大请假天数
应用场景
- 当需要多个对象处理同一请求时;
- 当需要动态组合处理者时;
- 当需要请求的发送者与接收者解耦时(如审批流程、日志级别过滤、异常处理)。
4.2 命令模式(Command Pattern)
现实类比:遥控器
电视遥控器上的每个按钮对应一个 “命令”(如开、关、音量 +、音量 -),当用户按下按钮时,遥控器将命令发送给电视执行。命令模式将 “请求” 封装为对象,让请求的发送者与执行者解耦。
核心问题:将请求封装为对象,允许参数化请求(如不同的命令)、队列请求(如命令历史)、撤销请求(如撤销操作)。
实现步骤
- 定义命令接口:规范命令的执行方法(如
Command的execute()); - 定义具体命令类:如
TurnOnCommand、TurnOffCommand,实现命令接口,并组合接收者对象; - 定义接收者类:如
TV,实现具体的业务逻辑(如turn_on()、turn_off()); - 定义请求者类:如
RemoteControl,维护命令对象,并调用命令的execute()方法。
Python 代码示例
# 1. 接收者类:电视
class TV:
def turn_on(self):
print("电视已打开")
def turn_off(self):
print("电视已关闭")
def volume_up(self):
print("音量+")
def volume_down(self):
print("音量-")
# 2. 命令接口
class Command:
def execute(self):
pass
def undo(self):
pass # 可选:撤销命令
# 3. 具体命令类1:打开电视
class TurnOnCommand(Command):
def __init__(self, tv: TV):
self._tv = tv # 组合接收者对象
def execute(self):
self._tv.turn_on()
def undo(self):
self._tv.turn_off() # 撤销打开命令:关闭电视
# 3. 具体命令类2:关闭电视
class TurnOffCommand(Command):
def __init__(self, tv: TV):
self._tv = tv
def execute(self):
self._tv.turn_off()
def undo(self):
self._tv.turn_on() # 撤销关闭命令:打开电视
# 3. 具体命令类3:音量+
class VolumeUpCommand(Command):
def __init__(self, tv: TV):
self._tv = tv
def execute(self):
self._tv.volume_up()
def undo(self):
self._tv.volume_down() # 撤销音量+:音量-
# 4. 请求者类:遥控器
class RemoteControl:
def __init__(self):
self._command = None # 当前命令
self._history = [] # 命令历史,用于撤销
# 设置当前命令
def set_command(self, command: Command):
self._command = command
# 执行当前命令
def press_button(self):
if self._command:
self._command.execute()
self._history.append(self._command) # 记录命令历史
# 撤销最后一个命令
def press_undo(self):
if self._history:
last_command = self._history.pop()
last_command.undo()
# 客户端代码
if __name__ == "__main__":
# 创建接收者
tv = TV()
# 创建命令
turn_on_cmd = TurnOnCommand(tv)
turn_off_cmd = TurnOffCommand(tv)
volume_up_cmd = VolumeUpCommand(tv)
# 创建请求者
remote = RemoteControl()
# 测试打开电视
print("\n=== 按下打开按钮 ===")
remote.set_command(turn_on_cmd)
remote.press_button() # 输出:电视已打开
# 测试音量+
print("\n=== 按下音量+按钮 ===")
remote.set_command(volume_up_cmd)
remote.press_button() # 输出:音量+
remote.press_button() # 输出:音量+
# 测试撤销
print("\n=== 按下撤销按钮 ===")
remote.press_undo() # 输出:音量-
remote.press_undo() # 输出:音量-
# 测试关闭电视
print("\n=== 按下关闭按钮 ===")
remote.set_command(turn_off_cmd)
remote.press_button() # 输出:电视已关闭
# 测试撤销关闭
print("\n=== 按下撤销按钮 ===")
remote.press_undo() # 输出:电视已打开
应用场景
- 当需要将请求的发送者与执行者解耦时;
- 当需要支持命令队列(如任务调度)时;
- 当需要支持撤销 / 重做操作时(如编辑器、游戏);
- 当需要记录命令历史(如审计日志)时。
4.3 迭代器模式(Iterator Pattern)
现实类比:图书馆书架
图书馆的书架上有很多书,用户不需要知道书的排列方式,只需要通过 “迭代器” 依次获取每一本书。迭代器模式提供一种遍历集合对象元素的统一方式,无需暴露集合的内部结构。
核心问题:提供一种遍历集合对象元素的统一接口,与集合的具体实现无关。
实现步骤
- 定义迭代器接口:规范遍历的方法(如
Iterator的has_next()、next()); - 定义具体迭代器类:如
BookShelfIterator,实现迭代器接口,维护遍历的当前位置; - 定义集合接口:规范集合的方法(如
Aggregate的create_iterator()); - 定义具体集合类:如
BookShelf,实现集合接口,创建并返回具体迭代器。
Python 代码示例
# 1. 迭代器接口
class Iterator:
def has_next(self) -> bool:
# 是否还有下一个元素
pass
def next(self):
# 返回下一个元素
pass
# 2. 集合接口(聚合接口)
class Aggregate:
def create_iterator(self) -> Iterator:
# 创建迭代器
pass
# 3. 具体集合类:书架
class BookShelf(Aggregate):
def __init__(self):
self._books = [] # 内部存储结构
def add_book(self, book: str):
self._books.append(book)
def get_book(self, index: int) -> str:
return self._books[index]
def size(self) -> int:
return len(self._books)
def create_iterator(self) -> Iterator:
return BookShelfIterator(self)
# 4. 具体迭代器类:书架迭代器
class BookShelfIterator(Iterator):
def __init__(self, book_shelf: BookShelf):
self._book_shelf = book_shelf
self._index = 0 # 当前遍历位置
def has_next(self) -> bool:
return self._index < self._book_shelf.size()
def next(self):
if self.has_next():
book = self._book_shelf.get_book(self._index)
self._index += 1
return book
return None
# 客户端代码
if __name__ == "__main__":
# 创建书架
book_shelf = BookShelf()
book_shelf.add_book("设计模式")
book_shelf.add_book("Python编程")
book_shelf.add_book("算法导论")
# 获取迭代器
iterator = book_shelf.create_iterator()
# 遍历集合
print("书架上的书:")
while iterator.has_next():
book = iterator.next()
print(f" - {book}") # 输出:设计模式、Python编程、算法导论
应用场景
- 当需要遍历集合对象但不暴露其内部结构时;
- 当需要为不同的集合提供统一的遍历接口时;
- 当需要在遍历过程中修改集合时(如安全遍历)。
注意:Python 中的迭代器
Python 内置支持迭代器模式,通过__iter__()和__next__()方法实现。例如,上述BookShelf类可直接实现__iter__()方法:
class BookShelf:
def __init__(self):
self._books = []
def add_book(self, book: str):
self._books.append(book)
def __iter__(self):
# 返回迭代器对象
return iter(self._books)
# 客户端代码
book_shelf = BookShelf()
book_shelf.add_book("设计模式")
for book in book_shelf:
print(book) # 输出:设计模式
4.4 中介者模式(Mediator Pattern)
现实类比:机场塔台
飞机在机场起降时,不会直接与其他飞机通信,而是通过塔台(中介者)协调 —— 塔台告诉飞机何时起降、如何滑行,避免飞机之间的直接耦合。
核心问题:减少对象间的直接通信,通过中介者集中管理对象间的交互,降低系统的耦合度。
实现步骤
- 定义中介者接口:规范中介者的方法(如
Mediator的notify()); - 定义具体中介者类:如
AirTrafficControl,维护所有同事对象的引用,并处理它们的交互; - 定义同事类:如
Airplane,包含中介者的引用,当需要与其他同事通信时,通过中介者。
Python 代码示例
# 1. 中介者接口
class Mediator:
def notify(self, sender: "Colleague", message: str):
pass
# 2. 同事类(抽象)
class Colleague:
def __init__(self, mediator: Mediator, name: str):
self._mediator = mediator
self._name = name
def send(self, message: str):
# 通过中介者发送消息
print(f"{self._name}发送消息:{message}")
self._mediator.notify(self, message)
def receive(self, message: str):
# 接收消息
print(f"{self._name}接收消息:{message}")
# 3. 具体同事类:飞机
class Airplane(Colleague):
pass
# 4. 具体中介者类:空中交通管制(塔台)
class AirTrafficControl(Mediator):
def __init__(self):
self._planes = [] # 维护所有飞机的引用
def add_plane(self, plane: Airplane):
self._planes.append(plane)
def notify(self, sender: Colleague, message: str):
# 中介者处理消息:将消息转发给所有其他飞机
for plane in self._planes:
if plane != sender:
plane.receive(message)
# 客户端代码
if __name__ == "__main__":
# 创建中介者
atc = AirTrafficControl()
# 创建同事对象(飞机)
plane1 = Airplane(atc, "飞机1")
plane2 = Airplane(atc, "飞机2")
plane3 = Airplane(atc, "飞机3")
# 注册飞机到中介者
atc.add_plane(plane1)
atc.add_plane(plane2)
atc.add_plane(plane3)
# 飞机1发送消息
print("\n=== 飞机1发送起飞请求 ===")
plane1.send("请求起飞") # 输出:
# 飞机1发送消息:请求起飞
# 飞机2接收消息:请求起飞
# 飞机3接收消息:请求起飞
# 飞机2发送消息
print("\n=== 飞机2发送降落请求 ===")
plane2.send("请求降落") # 输出:
# 飞机2发送消息:请求降落
# 飞机1接收消息:请求降落
# 飞机3接收消息:请求降落
应用场景
- 当对象间的通信复杂且多对多时;
- 当需要降低对象间的耦合度时;
- 当需要集中管理对象间的交互时(如聊天室、GUI 组件通信、分布式系统)。
4.5 备忘录模式(Memento Pattern)
现实类比:游戏存档
玩游戏时,玩家会定期存档 —— 存档包含当前游戏的进度(关卡、生命值、装备等),当玩家失败时可以读档恢复到之前的状态。备忘录模式用于保存对象的内部状态,并在需要时恢复,且不会暴露对象的内部结构。
核心问题:保存对象的内部状态,在不暴露对象内部结构的前提下,允许在需要时恢复到之前的状态。
实现步骤
- 定义备忘录类:如
Memento,保存发起人的内部状态; - 定义发起人类:如
Game,创建备忘录(保存状态)并从备忘录恢复状态; - 定义负责人类:如
GameCaretaker,管理备忘录对象(如存档列表)。
Python 代码示例
# 1. 备忘录类:游戏存档
class Memento:
def __init__(self, level: int, hp: int, equipment: list):
# 保存发起人的内部状态(关卡、生命值、装备)
self._level = level
self._hp = hp
self._equipment = equipment.copy() # 深拷贝,避免引用问题
# 仅提供状态的读取方法,不提供修改方法
def get_level(self):
return self._level
def get_hp(self):
return self._hp
def get_equipment(self):
return self._equipment
# 2. 发起人类:游戏
class Game:
def __init__(self):
self._level = 1
self._hp = 100
self._equipment = []
# 更新游戏状态
def progress(self, level_up: bool, hp_change: int, new_equipment: str = None):
if level_up:
self._level += 1
print(f"关卡提升到:{self._level}")
self._hp += hp_change
print(f"生命值变化为:{self._hp}")
if new_equipment:
self._equipment.append(new_equipment)
print(f"获得装备:{new_equipment}")
# 创建备忘录:保存当前状态
def create_memento(self) -> Memento:
return Memento(self._level, self._hp, self._equipment)
# 从备忘录恢复状态
def restore_from_memento(self, memento: Memento):
self._level = memento.get_level()
self._hp = memento.get_hp()
self._equipment = memento.get_equipment()
print(f"游戏状态恢复完成:关卡={self._level},生命值={self._hp},装备={self._equipment}")
# 3. 负责人类:游戏存档管理器
class GameCaretaker:
def __init__(self):
self._mementos = [] # 保存所有存档
# 保存存档
def save_memento(self, memento: Memento):
self._mementos.append(memento)
print(f"存档已保存,当前存档数量:{len(self._mementos)}")
# 读取指定索引的存档
def get_memento(self, index: int) -> Memento:
if 0 <= index < len(self._mementos):
return self._mementos[index]
return None
# 客户端代码
if __name__ == "__main__":
# 创建游戏
game = Game()
# 创建存档管理器
caretaker = GameCaretaker()
# 游戏进度1:关卡1,生命值100,无装备
print("\n=== 游戏进度1 ===")
game.progress(False, 0)
caretaker.save_memento(game.create_memento()) # 存档
# 游戏进度2:关卡2,生命值80,获得剑
print("\n=== 游戏进度2 ===")
game.progress(True, -20, "剑")
caretaker.save_memento(game.create_memento()) # 存档
# 游戏进度3:关卡2,生命值50,获得盾
print("\n=== 游戏进度3 ===")
game.progress(False, -30, "盾")
# 恢复到存档1
print("\n=== 恢复到存档1 ===")
memento1 = caretaker.get_memento(0)
game.restore_from_memento(memento1) # 输出:游戏状态恢复完成:关卡=1,生命值=100,装备=[]
# 恢复到存档2
print("\n=== 恢复到存档2 ===")
memento2 = caretaker.get_memento(1)
game.restore_from_memento(memento2) # 输出:游戏状态恢复完成:关卡=2,生命值=80,装备=['剑']
应用场景
- 当需要保存和恢复对象的状态时(如编辑器的撤销 / 重做、游戏存档、数据库事务回滚);
- 当需要避免暴露对象的内部结构时;
- 当需要记录对象的历史状态时。
4.6 观察者模式(Observer Pattern)
现实类比:公众号订阅
用户订阅公众号后,公众号发布新文章时会自动通知所有订阅者。观察者模式定义了一对多的依赖关系,当一个对象(主题)的状态变化时,所有依赖它的对象(观察者)都会自动收到通知并更新。
核心问题:实现对象间的自动通知机制,当主题状态变化时,所有观察者自动更新,无需主题主动调用。
实现步骤
- 定义主题接口:规范主题的方法(如
Subject的register_observer()、remove_observer()、notify_observers()); - 定义具体主题类:如
WeatherStation,维护观察者列表,并在状态变化时通知所有观察者; - 定义观察者接口:规范观察者的方法(如
Observer的update()); - 定义具体观察者类:如
PhoneDisplay、PCDisplay,实现update()方法,处理主题的通知。
Python 代码示例
# 1. 主题接口
class Subject:
def __init__(self):
self._observers = [] # 观察者列表
# 注册观察者
def register_observer(self, observer):
if observer not in self._observers:
self._observers.append(observer)
# 移除观察者
def remove_observer(self, observer):
if observer in self._observers:
self._observers.remove(observer)
# 通知所有观察者
def notify_observers(self):
for observer in self._observers:
observer.update()
# 2. 具体主题类:气象站
class WeatherStation(Subject):
def __init__(self):
super().__init__()
self._temperature = 0.0 # 温度(主题状态)
# 获取温度
def get_temperature(self):
return self._temperature
# 设置温度:状态变化时通知观察者
def set_temperature(self, temperature: float):
self._temperature = temperature
print(f"气象站:温度更新为{temperature}°C")
self.notify_observers() # 自动通知所有观察者
# 3. 观察者接口
class Observer:
def update(self):
pass
# 4. 具体观察者类1:手机显示器
class PhoneDisplay(Observer):
def __init__(self, weather_station: WeatherStation):
self._weather_station = weather_station
def update(self):
temperature = self._weather_station.get_temperature()
print(f"手机显示器:当前温度是{temperature}°C")
# 4. 具体观察者类2:电脑显示器
class PCDisplay(Observer):
def __init__(self, weather_station: WeatherStation):
self._weather_station = weather_station
def update(self):
temperature = self._weather_station.get_temperature()
print(f"电脑显示器:当前温度是{temperature}°C")
# 客户端代码
if __name__ == "__main__":
# 创建主题
weather_station = WeatherStation()
# 创建观察者
phone_display = PhoneDisplay(weather_station)
pc_display = PCDisplay(weather_station)
# 注册观察者
weather_station.register_observer(phone_display)
weather_station.register_observer(pc_display)
# 测试温度变化
print("\n=== 温度变化1:25°C ===")
weather_station.set_temperature(25.0) # 输出:
# 气象站:温度更新为25.0°C
# 手机显示器:当前温度是25.0°C
# 电脑显示器:当前温度是25.0°C
# 移除观察者(手机显示器)
weather_station.remove_observer(phone_display)
print("\n=== 温度变化2:30°C ===")
weather_station.set_temperature(30.0) # 输出:
# 气象站:温度更新为30.0°C
# 电脑显示器:当前温度是30.0°C
应用场景
- 当需要一对多的依赖关系时(如订阅 - 发布、监控系统、事件驱动架构);
- 当需要对象间的自动通知时;
- 当需要低耦合的通信机制时。
4.7 状态模式(State Pattern)
现实类比:交通信号灯
交通信号灯有红、黄、绿三种状态,每种状态下的行为不同(红灯停、绿灯行、黄灯警告),且状态之间可以自动切换(绿→黄→红→绿...)。状态模式将对象的状态封装为独立的类,让对象在不同状态下表现出不同的行为。
核心问题:当对象的行为依赖于其状态,且状态可以动态变化时,将状态封装为独立的类,避免使用大量的条件判断。
实现步骤
- 定义状态接口:规范状态的行为(如
TrafficLightState的show()、next_state()); - 定义具体状态类:如
RedState、YellowState、GreenState,实现状态接口的方法; - 定义上下文类:如
TrafficLight,维护当前状态对象,并将请求委托给当前状态。
Python 代码示例
# 1. 状态接口:交通信号灯状态
class TrafficLightState:
def show(self):
pass # 显示当前状态
def next_state(self, traffic_light):
pass # 切换到下一个状态
# 2. 具体状态类1:红灯
class RedState(TrafficLightState):
def show(self):
print("交通信号灯:红灯亮(禁止通行)")
def next_state(self, traffic_light):
traffic_light.set_state(YellowState()) # 红灯→黄灯
# 2. 具体状态类2:黄灯
class YellowState(TrafficLightState):
def show(self):
print("交通信号灯:黄灯亮(警告)")
def next_state(self, traffic_light):
traffic_light.set_state(GreenState()) # 黄灯→绿灯
# 2. 具体状态类3:绿灯
class GreenState(TrafficLightState):
def show(self):
print("交通信号灯:绿灯亮(可以通行)")
def next_state(self, traffic_light):
traffic_light.set_state(RedState()) # 绿灯→红灯
# 3. 上下文类:交通信号灯
class TrafficLight:
def __init__(self):
self._state = RedState() # 初始状态为红灯
# 设置当前状态
def set_state(self, state: TrafficLightState):
self._state = state
# 显示当前状态
def show(self):
self._state.show()
# 切换到下一个状态
def next(self):
self._state.next_state(self)
# 客户端代码
if __name__ == "__main__":
traffic_light = TrafficLight()
print("\n=== 信号灯状态1 ===")
traffic_light.show() # 输出:交通信号灯:红灯亮(禁止通行)
print("\n=== 切换到下一个状态 ===")
traffic_light.next()
traffic_light.show() # 输出:交通信号灯:黄灯亮(警告)
print("\n=== 切换到下一个状态 ===")
traffic_light.next()
traffic_light.show() # 输出:交通信号灯:绿灯亮(可以通行)
print("\n=== 切换到下一个状态 ===")
traffic_light.next()
traffic_light.show() # 输出:交通信号灯:红灯亮(禁止通行)
应用场景
- 当对象的行为随状态变化时;
- 当对象的状态数量较多,且状态之间可以动态切换时;
- 当对象的条件判断语句过于复杂时(如大量的 if-elif-else 或 switch-case)。
4.8 策略模式(Strategy Pattern)
现实类比:支付方式
用户在电商平台购物时,可以选择支付宝、微信支付、银行卡支付等不同的支付方式 —— 每种支付方式的逻辑不同,但都是 “支付” 这个功能的实现。策略模式将不同的算法封装为独立的策略类,让客户端可以动态切换算法。
核心问题:定义一系列算法,将它们封装为独立的策略类,让它们可以相互替换,且客户端与算法的具体实现解耦。
实现步骤
- 定义策略接口:规范算法的方法(如
PaymentStrategy的pay()); - 定义具体策略类:如
AlipayStrategy、WechatPayStrategy,实现策略接口; - 定义上下文类:如
Order,维护策略对象,并委托策略类执行算法。
Python 代码示例
# 1. 策略接口:支付策略
class PaymentStrategy:
def pay(self, amount: float):
pass # 支付逻辑
# 2. 具体策略类1:支付宝支付
class AlipayStrategy(PaymentStrategy):
def pay(self, amount: float):
print(f"使用支付宝支付:{amount}元")
# 2. 具体策略类2:微信支付
class WechatPayStrategy(PaymentStrategy):
def pay(self, amount: float):
print(f"使用微信支付:{amount}元")
# 2. 具体策略类3:银行卡支付
class BankCardPayStrategy(PaymentStrategy):
def pay(self, amount: float):
print(f"使用银行卡支付:{amount}元")
# 3. 上下文类:订单
class Order:
def __init__(self, amount: float):
self._amount = amount # 订单金额
self._payment_strategy = None # 当前支付策略
# 设置支付策略
def set_payment_strategy(self, strategy: PaymentStrategy):
self._payment_strategy = strategy
# 执行支付
def pay(self):
if self._payment_strategy:
self._payment_strategy.pay(self._amount)
else:
print("未选择支付方式")
# 客户端代码
if __name__ == "__main__":
# 创建订单
order = Order(100.0)
# 使用支付宝支付
print("\n=== 支付宝支付 ===")
order.set_payment_strategy(AlipayStrategy())
order.pay() # 输出:使用支付宝支付:100.0元
# 使用微信支付
print("\n=== 微信支付 ===")
order.set_payment_strategy(WechatPayStrategy())
order.pay() # 输出:使用微信支付:100.0元
# 使用银行卡支付
print("\n=== 银行卡支付 ===")
order.set_payment_strategy(BankCardPayStrategy())
order.pay() # 输出:使用银行卡支付:100.0元
# 新增支付方式仅需定义策略类,无需修改Order类
# class ApplePayStrategy(PaymentStrategy): ...
# order.set_payment_strategy(ApplePayStrategy())
# order.pay()
应用场景
- 当需要多种算法解决同一问题时;
- 当需要动态切换算法时;
- 当需要避免算法的硬编码时(如支付方式、排序算法、搜索算法)。
4.9 模板方法模式(Template Method Pattern)
现实类比:泡茶与泡咖啡
泡茶的步骤:烧水→放茶叶→冲泡→倒杯子;泡咖啡的步骤:烧水→放咖啡粉→冲泡→倒杯子。两者的核心步骤相同,但某些步骤的实现不同。模板方法模式将核心步骤定义为 “模板方法”,将可变步骤延迟到子类实现。
核心问题:定义算法的骨架,将可变部分延迟到子类实现,确保算法的结构不变,同时允许子类自定义某些步骤。
实现步骤
- 定义抽象类:规范算法的核心步骤(模板方法),并将可变步骤定义为抽象方法;
- 定义具体子类:实现抽象类中的可变步骤。
Python 代码示例
# 1. 抽象类:冲泡饮料
class Beverage:
# 模板方法:定义算法的骨架(核心步骤)
def prepare(self):
self.boil_water() # 烧水(固定步骤)
self.add_ingredient() # 加原料(可变步骤)
self.brew() # 冲泡(固定步骤)
self.pour_in_cup() # 倒杯子(固定步骤)
# 固定步骤1:烧水
def boil_water(self):
print("烧水")
# 可变步骤1:加原料(抽象方法,子类实现)
def add_ingredient(self):
pass
# 固定步骤2:冲泡
def brew(self):
print("冲泡")
# 固定步骤3:倒杯子
def pour_in_cup(self):
print("倒入杯子")
# 2. 具体子类1:泡茶
class Tea(Beverage):
# 实现可变步骤:加茶叶
def add_ingredient(self):
print("加茶叶")
# 2. 具体子类2:泡咖啡
class Coffee(Beverage):
# 实现可变步骤:加咖啡粉
def add_ingredient(self):
print("加咖啡粉")
# 客户端代码
if __name__ == "__main__":
# 泡茶
print("\n=== 泡茶 ===")
tea = Tea()
tea.prepare() # 输出:
# 烧水
# 加茶叶
# 冲泡
# 倒入杯子
# 泡咖啡
print("\n=== 泡咖啡 ===")
coffee = Coffee()
coffee.prepare() # 输出:
# 烧水
# 加咖啡粉
# 冲泡
# 倒入杯子
应用场景
- 当多个类的算法结构相似,只有部分步骤不同时;
- 当需要确保算法的结构不变,同时允许子类自定义某些步骤时;
- 当需要代码复用(固定步骤在抽象类中实现,无需子类重复编写)时。
4.10 访问者模式(Visitor Pattern)
现实类比:快递员配送
快递员需要配送不同类型的包裹(如文件、商品、易碎品),每种包裹的配送方式不同,但快递员(访问者)需要遍历所有包裹并执行配送。访问者模式用于在不修改集合元素类的前提下,为元素添加新的操作。
核心问题:将数据结构与操作数据的算法分离,允许动态扩展操作,而无需修改数据结构。
实现步骤
- 定义访问者接口:规范访问不同元素的方法(如
Visitor的visit_document()、visit_goods()); - 定义具体访问者类:如
CourierVisitor,实现访问者接口的方法; - 定义元素接口:规范元素的
accept()方法(接收访问者); - 定义具体元素类:如
Document、Goods,实现accept()方法,调用访问者的对应方法; - 定义对象结构:如
ParcelList,维护元素列表,并提供遍历方法。
Python 代码示例
# 1. 访问者接口
class Visitor:
def visit_document(self, document: "Document"):
pass
def visit_goods(self, goods: "Goods"):
pass
# 2. 具体访问者类:快递员
class CourierVisitor(Visitor):
def visit_document(self, document: "Document"):
print(f"配送文件包裹:{document.name},重量:{document.weight}kg")
def visit_goods(self, goods: "Goods"):
print(f"配送商品包裹:{goods.name},重量:{goods.weight}kg")
# 3. 元素接口
class Parcel:
def accept(self, visitor: Visitor):
pass
# 4. 具体元素类1:文件包裹
class Document(Parcel):
def __init__(self, name: str, weight: float):
self.name = name
self.weight = weight
def accept(self, visitor: Visitor):
visitor.visit_document(self) # 调用访问者的对应方法
# 4. 具体元素类2:商品包裹
class Goods(Parcel):
def __init__(self, name: str, weight: float):
self.name = name
self.weight = weight
def accept(self, visitor: Visitor):
visitor.visit_goods(self) # 调用访问者的对应方法
# 5. 对象结构:包裹列表
class ParcelList:
def __init__(self):
self._parcels = []
def add_parcel(self, parcel: Parcel):
self._parcels.append(parcel)
# 遍历所有包裹,接受访问者访问
def accept(self, visitor: Visitor):
for parcel in self._parcels:
parcel.accept(visitor)
# 客户端代码
if __name__ == "__main__":
# 创建包裹列表
parcel_list = ParcelList()
# 添加包裹
parcel_list.add_parcel(Document("合同文档", 0.5))
parcel_list.add_parcel(Goods("手机", 0.3))
parcel_list.add_parcel(Document("会议纪要", 0.2))
# 创建访问者(快递员)
courier = CourierVisitor()
# 遍历包裹并配送
print("\n=== 快递员开始配送 ===")
parcel_list.accept(courier) # 输出:
# 配送文件包裹:合同文档,重量:0.5kg
# 配送商品包裹:手机,重量:0.3kg
# 配送文件包裹:会议纪要,重量:0.2kg
# 新增访问者(如仓库管理员)仅需定义Visitor子类,无需修改Parcel或ParcelList类
# class WarehouseVisitor(Visitor): ...
# warehouse_visitor = WarehouseVisitor()
# parcel_list.accept(warehouse_visitor)
应用场景
- 当需要为数据结构添加新的操作,但又不想修改数据结构的类时;
- 当数据结构稳定,但操作需要频繁扩展时;
- 当需要遍历复杂数据结构并执行不同操作时。
4.11 解释器模式(Interpreter Pattern)
现实类比:计算器
计算器可以解释并执行数学表达式(如 “1+2*3”)。解释器模式用于定义一种语言的文法,并创建一个解释器来解释该语言的表达式。
核心问题:定义简单语言的文法,并实现解释器来解析和执行该语言的表达式。
实现步骤
- 定义抽象表达式类:规范解释的方法(如
Expression的interpret()); - 定义终结符表达式类:如
NumberExpression(数字),实现解释方法; - 定义非终结符表达式类:如
AddExpression(加法)、MultiplyExpression(乘法),实现解释方法; - 客户端构建抽象语法树(AST),并调用根节点的
interpret()方法。
Python 代码示例:简单计算器
# 1. 抽象表达式类
class Expression:
def interpret(self) -> float:
pass
# 2. 终结符表达式类:数字
class NumberExpression(Expression):
def __init__(self, value: float):
self._value = value
def interpret(self) -> float:
return self._value
# 3. 非终结符表达式类:加法
class AddExpression(Expression):
def __init__(self, left: Expression, right: Expression):
self._left = left # 左操作数
self._right = right # 右操作数
def interpret(self) -> float:
return self._left.interpret() + self._right.interpret()
# 3. 非终结符表达式类:乘法
class MultiplyExpression(Expression):
def __init__(self, left: Expression, right: Expression):
self._left = left
self._right = right
def interpret(self) -> float:
return self._left.interpret() * self._right.interpret()
# 客户端代码
if __name__ == "__main__":
# 构建抽象语法树:1 + 2 * 3
# 先构建乘法表达式:2 * 3
multiply_expr = MultiplyExpression(NumberExpression(2.0), NumberExpression(3.0))
# 再构建加法表达式:1 + (2 * 3)
add_expr = AddExpression(NumberExpression(1.0), multiply_expr)
# 解释并执行表达式
result = add_expr.interpret()
print(f"1 + 2 * 3 = {result}") # 输出:7.0
# 构建另一个表达式:(1 + 2) * 3
add_expr2 = AddExpression(NumberExpression(1.0), NumberExpression(2.0))
multiply_expr2 = MultiplyExpression(add_expr2, NumberExpression(3.0))
result2 = multiply_expr2.interpret()
print(f"(1 + 2) * 3 = {result2}") # 输出:9.0
应用场景
- 当需要解析简单语言或表达式时(如计算器、正则表达式、配置文件解析);
- 当文法规则简单时;
- 当需要扩展语言的文法时。
五、J2EE 设计模式详解
J2EE 设计模式是针对 Java 企业级开发场景(如分层架构、分布式通信、资源管理)提炼的通用解决方案。虽然其名称基于 Java EE,但核心思想可应用于所有企业级开发语言(如 Python、Java、Go)。
5.1 MVC 模式(Model-View-Controller)
核心思想:分离关注点,将应用分为三个部分:
- Model:数据层,负责数据的存储、获取和业务逻辑(如数据库操作、用户认证);
- View:视图层,负责数据的展示(如 HTML 页面、API 响应);
- Controller:控制器层,负责接收请求、调用 Model 处理数据、传递结果给 View 渲染。
Python 代码示例(基于 Flask 框架简化版)
# Model层:用户数据模型
class User:
def __init__(self, id: int, name: str, email: str):
self.id = id
self.name = name
self.email = email
# Model层:用户业务逻辑
class UserModel:
def __init__(self):
# 模拟数据库
self._users = [
User(1, "张三", "zhangsan@example.com"),
User(2, "李四", "lisi@example.com")
]
def get_user_by_id(self, user_id: int) -> User:
for user in self._users:
if user.id == user_id:
return user
return None
# View层:HTML视图模板(模拟)
class UserView:
def render(self, user: User) -> str:
return f"""
<html>
<body>
<h1>用户信息</h1>
<p>ID: {user.id}</p>
<p>姓名: {user.name}</p>
<p>邮箱: {user.email}</p>
</body>
</html>
"""
# Controller层:用户控制器
class UserController:
def __init__(self, user_model: UserModel, user_view: UserView):
self._user_model = user_model
self._user_view = user_view
# 处理用户查询请求
def handle_user_request(self, user_id: int) -> str:
# 1. 调用Model获取数据
user = self._user_model.get_user_by_id(user_id)
if user is None:
return "<h1>用户不存在</h1>"
# 2. 调用View渲染数据
return self._user_view.render(user)
# 客户端代码(模拟Flask路由)
if __name__ == "__main__":
# 初始化三层
user_model = UserModel()
user_view = UserView()
user_controller = UserController(user_model, user_view)
# 模拟请求:查询用户ID=1
html = user_controller.handle_user_request(1)
print(html) # 输出用户1的HTML信息
# 模拟请求:查询用户ID=3
html = user_controller.handle_user_request(3)
print(html) # 输出用户不存在
应用场景
- 所有分层架构的 Web 应用;
- 需要分离数据、业务逻辑和展示的应用;
- 需要提高代码复用性和可维护性的应用。
5.2 数据访问对象模式(DAO Pattern)
核心思想:将数据访问逻辑与业务逻辑分离,提供一个统一的接口(DAO)来访问不同的数据源(如数据库、文件、API)。
实现步骤
- 定义实体类:如
User,对应数据源中的数据; - 定义DAO 接口:规范数据访问的方法(如
UserDAO的save()、update()、find_all()); - 定义具体 DAO 类:如
MySQLUserDAO、FileUserDAO,实现 DAO 接口,访问具体的数据源。
Python 代码示例
# 1. 实体类:用户
class User:
def __init__(self, id: int, name: str):
self.id = id
self.name = name
def __str__(self):
return f"User(id={self.id}, name={self.name})"
# 2. DAO接口:用户数据访问
class UserDAO:
def save(self, user: User):
pass
def find_by_id(self, user_id: int) -> User:
pass
def find_all(self) -> list:
pass
# 3. 具体DAO类1:MySQL数据访问
class MySQLUserDAO(UserDAO):
def save(self, user: User):
print(f"MySQL: 保存用户 {user}")
def find_by_id(self, user_id: int) -> User:
print(f"MySQL: 查询用户ID={user_id}")
return User(user_id, f"MySQL用户{user_id}")
def find_all(self) -> list:
print(f"MySQL: 查询所有用户")
return [User(1, "MySQL用户1"), User(2, "MySQL用户2")]
# 3. 具体DAO类2:文件数据访问
class FileUserDAO(UserDAO):
def save(self, user: User):
print(f"File: 保存用户 {user} 到文件")
def find_by_id(self, user_id: int) -> User:
print(f"File: 从文件查询用户ID={user_id}")
return User(user_id, f"File用户{user_id}")
def find_all(self) -> list:
print(f"File: 从文件查询所有用户")
return [User(1, "File用户1"), User(2, "File用户2")]
# 客户端代码(业务逻辑)
if __name__ == "__main__":
# 使用MySQL DAO
mysql_dao = MySQLUserDAO()
user1 = mysql_dao.find_by_id(1)
print(user1) # 输出:User(id=1, name=MySQL用户1)
# 使用File DAO
file_dao = FileUserDAO()
users = file_dao.find_all()
for user in users:
print(user) # 输出:File用户1、File用户2
# 切换数据源仅需修改DAO实例,业务逻辑无需修改
应用场景
- 需要访问多种数据源(如 MySQL、Redis、文件)的应用;
- 需要分离数据访问逻辑与业务逻辑的应用;
- 需要提高数据访问层的可扩展性的应用。
5.3 前端控制器模式(Front Controller Pattern)
核心思想:提供一个统一的入口来处理所有请求,集中管理请求的路由、认证、日志等通用逻辑,避免重复代码。
实现步骤
- 定义前端控制器:如
FrontController,处理所有请求; - 定义视图解析器:如
ViewResolver,根据视图名称解析为实际的视图; - 定义命令控制器:如
CommandController,处理具体的业务请求; - 客户端请求发送到前端控制器,前端控制器路由到对应的命令控制器,返回结果。
Python 代码示例
# 1. 视图解析器:将视图名称解析为HTML
class ViewResolver:
def resolve(self, view_name: str) -> str:
# 模拟视图解析
if view_name == "home":
return "<h1>首页</h1>"
elif view_name == "user":
return "<h1>用户页</h1>"
return "<h1>404 页面不存在</h1>"
# 2. 命令控制器:处理具体请求
class HomeController:
def handle(self) -> str:
return "home" # 返回视图名称
class UserController:
def handle(self) -> str:
return "user" # 返回视图名称
# 3. 前端控制器:统一处理所有请求
class FrontController:
def __init__(self):
self._view_resolver = ViewResolver()
# 路由映射:请求路径 → 命令控制器
self._route_map = {
"/": HomeController(),
"/user": UserController()
}
# 处理请求
def handle_request(self, path: str) -> str:
# 1. 日志记录(通用逻辑)
print(f"请求路径:{path}")
# 2. 认证检查(通用逻辑)
# ...
# 3. 路由到命令控制器
if path in self._route_map:
controller = self._route_map[path]
view_name = controller.handle()
# 4. 解析视图
return self._view_resolver.resolve(view_name)
return "<h1>404 路径不存在</h1>"
# 客户端代码
if __name__ == "__main__":
front_controller = FrontController()
# 模拟请求首页
html = front_controller.handle_request("/")
print(html) # 输出:<h1>首页</h1>
# 模拟请求用户页
html = front_controller.handle_request("/user")
print(html) # 输出:<h1>用户页</h1>
# 模拟请求不存在的路径
html = front_controller.handle_request("/nonexistent")
print(html) # 输出:<h1>404 路径不存在</h1>
应用场景
- Web 应用的统一请求入口;
- 需要集中管理通用请求逻辑(如认证、日志、路由)的应用;
- 需要减少重复代码的应用。
六、设计模式的使用原则:SOLID
设计模式的使用需要遵循SOLID 原则,这是软件工程的 5 个核心设计原则,确保代码的可维护性和可扩展性:
- 单一职责原则(Single Responsibility Principle):一个类只负责一个功能,避免 “多功能类” 导致的代码复杂;
- 开闭原则(Open/Closed Principle):对扩展开放,对修改关闭 —— 新增功能通过扩展类实现,无需修改原有代码;
- 里氏替换原则(Liskov Substitution Principle):子类可以替换父类,且不会影响系统的正确性;
- 接口隔离原则(Interface Segregation Principle):使用小的、专门的接口,避免 “大而全” 的接口导致的依赖冗余;
- 依赖倒置原则(Dependency Inversion Principle):依赖抽象,而不是具体实现 —— 客户端依赖抽象接口,不直接依赖具体类。
七、如何选择合适的设计模式
-
分析问题类型:
- 若问题与对象创建有关,选择创建型模式(如单例、工厂、建造者);
- 若问题与类或对象组合有关,选择结构型模式(如适配器、装饰器、外观);
- 若问题与对象间通信有关,选择行为型模式(如观察者、策略、责任链);
- 若问题与企业级架构有关,选择 J2EE 模式(如 MVC、DAO、前端控制器)。
-
遵循 SOLID 原则:确保所选模式符合代码的可维护性和可扩展性要求。
-
考虑场景复杂度:避免过度设计 —— 若问题简单,直接使用简单代码即可,无需强制使用设计模式。
总结
23 种经典设计模式(创建型 5 种、结构型 7 种、行为型 11 种)是软件工程的最佳实践,它们解决了 “重复出现的软件设计问题”,让代码更易读、易维护、易扩展。J2EE 设计模式则是针对企业级开发场景的补充,解决了分层架构、数据访问、请求处理等问题。
作为零基础开发者,学习设计模式的关键是理解核心思想,而非死记硬背代码 —— 从现实类比入手,思考模式解决的问题,再结合代码示例加深理解。随着开发经验的积累,你会逐渐掌握设计模式的使用场景,并能灵活应用于实际项目。

165

被折叠的 条评论
为什么被折叠?



