基于类的四种设计模式
1. 工厂方法模式 (Factory Method)
问题
当系统需要创建对象,但具体类型在编译时无法确定,或者创建逻辑复杂需要封装时,直接使用new
关键字会导致代码耦合度高、难以扩展。
解决方案
定义一个创建对象的接口,但让子类决定实例化哪个类。工厂方法让类的实例化推迟到子类。
组件框图
┌─────────────────┐ ┌──────────────────┐
│ Creator │ │ Product │
├─────────────────┤ ├──────────────────┤
│+factoryMethod() │───────>│ │
└─────────────────┘ └──────────────────┘
▲ ▲
│ │
┌─────────────────┐ ┌──────────────────┐
│ ConcreteCreator │ │ ConcreteProduct │
├─────────────────┤ ├──────────────────┤
│+factoryMethod() │ │ │
└─────────────────┘ └──────────────────┘
效果
优点:
符合开闭原则,易于扩展新产品
客户端与具体产品类解耦
将创建逻辑集中管理
缺点:
每增加一个产品就需要增加一个具体工厂类
增加了系统的复杂度
代码示例(Python重点展示)
from abc import ABC, abstractmethodclass Document(ABC):@abstractmethoddef open(self):pass@abstractmethoddef save(self):passclass WordDocument(Document):def open(self):print("打开Word文档")def save(self):print("保存Word文档")class PDFDocument(Document):def open(self):print("打开PDF文档")def save(self):print("保存PDF文档")class Application(ABC):def __init__(self):self.documents = []@abstractmethoddef create_document(self) -> Document:passdef new_document(self):doc = self.create_document()self.documents.append(doc)doc.open()return docclass WordApplication(Application):def create_document(self) -> Document:return WordDocument()class PDFApplication(Application):def create_document(self) -> Document:return PDFDocument()# 使用
app = WordApplication()
doc = app.new_document() # 输出: 打开Word文档
2. 基于类的适配器模式 (Class Adapter)
问题
现有类的接口与客户端期望的接口不兼容,但需要让它们能够协同工作,且不希望修改现有代码。
解决方案
通过多重继承(或实现接口+继承)创建一个适配器类,它同时继承目标接口和被适配者,在适配器类中实现接口转换。
组件框图
┌─────────────────┐ ┌──────────────────┐
│ Client │ │ Target │
├─────────────────┤ ├──────────────────┤
│ │───────>│+request() │
└─────────────────┘ └──────────────────┘▲│┌──────────────────┐│ Adapter │├──────────────────┤│+request() │└──────────────────┘▲│┌──────────────────┐│ Adaptee │├──────────────────┤│+specificRequest()│└──────────────────┘
效果
优点:
可以让任何两个没有关联的类一起运行
提高了类的复用性
符合开闭原则
缺点:
过多使用适配器会让系统变得混乱
由于使用继承,限制了适配器的灵活性
代码示例(C++重点展示)
#include <iostream>
#include <string>// 目标接口 - 欧洲插座
class EuropeanSocket {
public:virtual ~EuropeanSocket() = default;virtual void plugIn() = 0;virtual int getVoltage() = 0;
};// 被适配者 - 美国插座
class AmericanSocket {
public:void connect() {std::cout << "美国插座连接" << std::endl;}int getUSVoltage() {return 110; // 110V}
};// 适配器 - 旅行转换器
class SocketAdapter : public EuropeanSocket, private AmericanSocket {
public:void plugIn() override {std::cout << "使用适配器连接: ";connect(); // 调用被适配者的方法std::cout << "将电压从" << getUSVoltage() << "V转换到" << getVoltage() << "V" << std::endl;}int getVoltage() override {return 220; // 转换为欧洲电压}
};// 欧洲电器
class EuropeanDevice {
public:void use(EuropeanSocket* socket) {socket->plugIn();std::cout << "设备在 " << socket->getVoltage() << "V 电压下工作" << std::endl;}
};// 使用
int main() {EuropeanDevice device;SocketAdapter adapter;device.use(&adapter);return 0;
}
3. 解释器模式 (Interpreter)
问题
需要解释执行特定语言或文法规则,且文法规则可以表示为层次结构。例如:正则表达式、数学表达式、SQL查询等。
解决方案
定义文法的类层次结构,每个规则对应一个类,通过组合模式构建抽象语法树,实现解释操作。
组件框图
┌─────────────────┐ ┌──────────────────┐
│ Client │ │ AbstractExpression
├─────────────────┤ ├──────────────────┤
│ │───────>│+interpret(Context)│
└─────────────────┘ └──────────────────┘△┌────────────────┼────────────────┐┌────────────┴────────────┐ ┌──────────────┴────────────┐│ TerminalExpression │ │ NonterminalExpression │├─────────────────────────┤ ├───────────────────────────┤│+interpret(Context) │ │+interpret(Context) │└─────────────────────────┘ │-expression │└───────────────────────────┘
效果
优点:
易于改变和扩展文法
实现文法很容易,类层次结构清晰
易于实现简单文法
缺点:
复杂的文法难以维护
执行效率较低
类数量会随着规则增加而增加
代码示例(C#重点展示)
using System;
using System.Collections.Generic;// 上下文 - 存储变量值
public class Context
{private Dictionary<string, bool> variables = new Dictionary<string, bool>();public void SetVariable(string name, bool value){variables[name] = value;}public bool GetVariable(string name){return variables.ContainsKey(name) ? variables[name] : false;}
}// 抽象表达式
public interface IExpression
{bool Interpret(Context context);
}// 终结符表达式 - 变量
public class VariableExpression : IExpression
{private string name;public VariableExpression(string name){this.name = name;}public bool Interpret(Context context){return context.GetVariable(name);}
}// 终结符表达式 - 常量
public class ConstantExpression : IExpression
{private bool value;public ConstantExpression(bool value){this.value = value;}public bool Interpret(Context context){return value;}
}// 非终结符表达式 - 与操作
public class AndExpression : IExpression
{private IExpression left;private IExpression right;public AndExpression(IExpression left, IExpression right){this.left = left;this.right = right;}public bool Interpret(Context context){return left.Interpret(context) && right.Interpret(context);}
}// 非终结符表达式 - 或操作
public class OrExpression : IExpression
{private IExpression left;private IExpression right;public OrExpression(IExpression left, IExpression right){this.left = left;this.right = right;}public bool Interpret(Context context){return left.Interpret(context) || right.Interpret(context);}
}// 非终结符表达式 - 非操作
public class NotExpression : IExpression
{private IExpression expression;public NotExpression(IExpression expression){this.expression = expression;}public bool Interpret(Context context){return !expression.Interpret(context);}
}// 使用
class Program
{static void Main(){// 构建上下文:A=true, B=false, C=trueContext context = new Context();context.SetVariable("A", true);context.SetVariable("B", false);context.SetVariable("C", true);// 构建表达式: (A AND B) OR (NOT C)IExpression expression = new OrExpression(new AndExpression(new VariableExpression("A"),new VariableExpression("B")),new NotExpression(new VariableExpression("C")));bool result = expression.Interpret(context);Console.WriteLine($"表达式 (A AND B) OR (NOT C) 的结果: {result}");// 计算: (true AND false) OR (NOT true) = false OR false = false// 另一个表达式: A OR (B AND C)IExpression expression2 = new OrExpression(new VariableExpression("A"),new AndExpression(new VariableExpression("B"),new VariableExpression("C")));bool result2 = expression2.Interpret(context);Console.WriteLine($"表达式 A OR (B AND C) 的结果: {result2}");// 计算: true OR (false AND true) = true OR false = true}
}
4. 模板方法模式 (Template Method)
问题
多个类有相同的工作步骤,但某些步骤的具体实现不同。需要在保持算法结构不变的前提下,允许子类重新定义某些步骤。
解决方案
在抽象类中定义算法的骨架,将一些步骤的实现推迟到子类中。模板方法使得子类可以不改变算法结构的情况下重新定义算法的某些步骤。
组件框图
┌─────────────────┐
│ AbstractClass │
├─────────────────┤
│+templateMethod()│
│+step1() │
│+step2() │
│#primitiveOp1() │
│#primitiveOp2() │
│#hook() │
└─────────────────┘△│
┌─────────────────┐
│ ConcreteClass │
├─────────────────┤
│#primitiveOp1() │
│#primitiveOp2() │
│#hook() │
└─────────────────┘
效果
优点:
代码复用,将不变行为移到超类
符合开闭原则,易于扩展
便于维护,算法结构集中管理
缺点:
每个不同的实现都需要一个子类
超类中的抽象方法必须由子类实现
可能违反里氏替换原则
详细代码示例(Python)
from abc import ABC, abstractmethod
from datetime import datetimeclass ReportGenerator(ABC):"""报表生成器模板类"""def generate_report(self):"""模板方法 - 定义报表生成流程"""self.collect_data()self.process_data()self.format_report()if self.needs_export():self.export_report()self.cleanup()def collect_data(self):"""具体方法 - 收集数据(所有报表通用)"""print(f"[{datetime.now()}] 开始收集数据...")# 模拟数据收集self.data = ["数据1", "数据2", "数据3"]print("数据收集完成")@abstractmethoddef process_data(self):"""抽象方法 - 处理数据(子类必须实现)"""pass@abstractmethoddef format_report(self):"""抽象方法 - 格式化报表(子类必须实现)"""passdef export_report(self):"""钩子方法 - 导出报表(子类可选重写)"""print("默认导出为PDF格式")def needs_export(self):"""钩子方法 - 是否需要导出(子类可选重写)"""return Truedef cleanup(self):"""具体方法 - 清理资源(所有报表通用)"""print("清理临时数据和资源...")class SalesReportGenerator(ReportGenerator):"""销售报表生成器"""def process_data(self):"""实现销售数据处理的特定逻辑"""print("处理销售数据: 计算销售额、增长率等指标")self.processed_data = [f"销售指标: {item}" for item in self.data]def format_report(self):"""实现销售报表的特定格式"""print("格式化销售报表: 使用图表展示销售趋势")print("生成的报表内容:", self.processed_data)def export_report(self):"""重写导出方法 - 销售报表需要多种格式"""print("导出销售报表: PDF, Excel, 和HTML格式")class FinancialReportGenerator(ReportGenerator):"""财务报表生成器"""def process_data(self):"""实现财务数据处理的特定逻辑"""print("处理财务数据: 计算利润、资产负债表等项目")self.processed_data = [f"财务项目: {item}" for item in self.data]def format_report(self):"""实现财务报表的特定格式"""print("格式化财务报表: 使用表格展示财务数据")print("生成的报表内容:", self.processed_data)def needs_export(self):"""重写钩子方法 - 财务报表不需要自动导出"""return Falsedef audit_trail(self):"""财务报表特有的审计追踪方法"""print("生成审计追踪记录")class InventoryReportGenerator(ReportGenerator):"""库存报表生成器"""def process_data(self):"""实现库存数据处理的特定逻辑"""print("处理库存数据: 计算库存周转率、安全库存等")self.processed_data = [f"库存指标: {item}" for item in self.data]def format_report(self):"""实现库存报表的特定格式"""print("格式化库存报表: 使用条形图展示库存水平")print("生成的报表内容:", self.processed_data)def export_report(self):"""重写导出方法 - 库存报表需要实时同步"""print("导出库存报表并同步到ERP系统")# 客户端代码
def main():print("=== 销售报表生成 ===")sales_report = SalesReportGenerator()sales_report.generate_report()print("\n=== 财务报表生成 ===")financial_report = FinancialReportGenerator()financial_report.generate_report()print("\n=== 库存报表生成 ===")inventory_report = InventoryReportGenerator()inventory_report.generate_report()if __name__ == "__main__":main()
模式对比总结
模式 | 主要目的 | 适用场景 | 关键特性 |
---|---|---|---|
工厂方法 | 对象创建解耦 | 创建逻辑复杂或运行时确定类型 | 子类决定创建对象 |
适配器模式 | 接口转换 | 集成不兼容的接口 | 继承+接口实现 |
解释器模式 | 语言解释 | 特定领域语言处理 | 文法类层次结构 |
模板方法 | 算法骨架固定 | 多个类有相同流程但不同实现 | 抽象类定义模板 |
这些模式都体现了面向对象设计的重要原则:开闭原则、依赖倒置原则和里氏替换原则,在实际开发中可以根据具体需求灵活选用。