开发原则和设计模式一直是软件开发中的圣经, 但是这仅仅适用于中大型的项目开发, 在小型项目的开发中, 这些规则会降低你的开发效率, 使你的工程变得繁杂. 所以只有适合你的才是最好的.
- 设计模式六大原则
-
- [1. 单一职责原则(Single Responsibility Principle)](#1. 单一职责原则(Single Responsibility Principle))
-
- [1. 定义](#1. 定义)
- [2. 核心思想](#2. 核心思想)
- [3. 为什么要遵守单一职责原则?](#3. 为什么要遵守单一职责原则?)
- [4. 实际案例](#4. 实际案例)
- [5. 如何实现单一职责原则?](#5. 如何实现单一职责原则?)
- [2. 开放封闭原则(Open Closed Principle)](#2. 开放封闭原则(Open Closed Principle))
-
- [1. 定义](#1. 定义)
- [2. 核心思想](#2. 核心思想)
- [3. 为什么要遵守开放封闭原则?](#3. 为什么要遵守开放封闭原则?)
- [4. 实际案例](#4. 实际案例)
- [5. 如何实现开放封闭原则?](#5. 如何实现开放封闭原则?)
- [3. 里氏替换原则(Liskov Substitution Principle)](#3. 里氏替换原则(Liskov Substitution Principle))
-
- [1. 定义](#1. 定义)
- [2. 核心思想](#2. 核心思想)
- [3. 为什么要遵守里氏替换原则?](#3. 为什么要遵守里氏替换原则?)
- [4. 实际案例](#4. 实际案例)
- [5. 如何实现里氏替换原则?](#5. 如何实现里氏替换原则?)
- [4. 依赖倒置原则(Dependency Inversion Principle)](#4. 依赖倒置原则(Dependency Inversion Principle))
-
- [1. 定义](#1. 定义)
- [2. 核心思想](#2. 核心思想)
- [3. 为什么要遵守依赖倒置原则?](#3. 为什么要遵守依赖倒置原则?)
- [4. 实际案例](#4. 实际案例)
- [5. 如何实现依赖倒置原则?](#5. 如何实现依赖倒置原则?)
- [5. 接口隔离原则(Interface Segregation Principle)](#5. 接口隔离原则(Interface Segregation Principle))
-
- [1. 定义](#1. 定义)
- [2. 核心思想](#2. 核心思想)
- [3. 为什么要遵守接口隔离原则?](#3. 为什么要遵守接口隔离原则?)
- [4. 实际案例](#4. 实际案例)
- [5. 如何实现接口隔离原则?](#5. 如何实现接口隔离原则?)
- [6. 迪米特法则(Law of Demeter)](#6. 迪米特法则(Law of Demeter))
-
- [1. 定义](#1. 定义)
- [2. 核心思想](#2. 核心思想)
- [3. 为什么要遵守迪米特法则?](#3. 为什么要遵守迪米特法则?)
- [4. 实际案例](#4. 实际案例)
- [5. 如何实现迪米特法则?](#5. 如何实现迪米特法则?)
- [7. 合成复用原则(Composite Reuse Principle)](#7. 合成复用原则(Composite Reuse Principle))
-
- [1. 定义](#1. 定义)
- [2. 核心思想](#2. 核心思想)
- [3. 为什么要遵守合成复用原则?](#3. 为什么要遵守合成复用原则?)
- [4. 实际案例](#4. 实际案例)
- 5.如何实现合成复用原则?
- 23种设计模式
-
- 创建型模式
-
- [1. 单例模式(Singleton Pattern)](#1. 单例模式(Singleton Pattern))
- [2. 工厂方法模式(Factory Method Pattern)](#2. 工厂方法模式(Factory Method Pattern))
- [3. 抽象工厂模式(Abstract Factory Pattern)](#3. 抽象工厂模式(Abstract Factory Pattern))
- [4. 建造者模式(Builder Pattern)](#4. 建造者模式(Builder Pattern))
- [5. 原型模式详解](#5. 原型模式详解)
- [6. 组合模式(Composite Pattern) ★](#6. 组合模式(Composite Pattern) ★)
- [7. 适配器模式(Adapter Pattern)](#7. 适配器模式(Adapter Pattern))
- [8. 桥接模式(Bridge Pattern)](#8. 桥接模式(Bridge Pattern))
- [9. 装饰模式(Decorator Pattern)★](#9. 装饰模式(Decorator Pattern)★)
- [10. 外观模式(Facade Pattern)★](#10. 外观模式(Facade Pattern)★)
- [11. 享元模式(Flyweight Pattern)](#11. 享元模式(Flyweight Pattern))
- [12. 代理模式(Proxy Pattern)★](#12. 代理模式(Proxy Pattern)★)
- [13. 职责链模式(Chain of Responsibility Pattern)](#13. 职责链模式(Chain of Responsibility Pattern))
- [14. 命令模式(Command Pattern)](#14. 命令模式(Command Pattern))
- [15. 解释器模式(Interpreter Pattern)](#15. 解释器模式(Interpreter Pattern))
- [16. ### 迭代器模式详解](### 迭代器模式详解)
- [17. 中介者模式](#17. 中介者模式)
- 18. 备忘录模式#
- [19. 观察者模式### 观察者模式详解](### 观察者模式详解)
- [20. 状态模式### 状态模式详解](### 状态模式详解)
- [21. 策略模式](#21. 策略模式)
- [22. 模版方法模式](#22. 模版方法模式)
- [23. 访问者模式](#23. 访问者模式)
设计模式六大原则
1. 单一职责原则(Single Responsibility Principle)
1. 定义
一个类应该只有一个引起它变化的原因,即一个类应该只做一件事情。
2. 核心思想
尽量使用组合,尽量不要使用继承。通过将不同的职责分离到不同的类中,降低类的复杂度,提高代码的可维护性。
3. 为什么要遵守单一职责原则?
好处:
- 提高代码的可读性和可维护性:每个类的职责单一,代码更易于理解和修改。
- 增强系统的灵活性和可扩展性:职责分离后,修改一个类的实现不会影响其他类。
坏处:
- 增加类的数量:可能会导致系统中类的数量增加,增加管理的复杂性。
- 可能导致过度设计:在简单的系统中,过度分离职责可能导致不必要的复杂性。
4. 实际案例
不遵守单一职责原则的案例
csharp
public class Employee
{
public void CalculatePay() { /* 计算工资 */ }
public void Save() { /* 保存到数据库 */ }
}
在这个例子中,Employee
类同时负责工资计算和数据存储,违反了单一职责原则。
遵守单一职责原则的案例
csharp
public class Employee
{
public void CalculatePay() { /* 计算工资 */ }
}
public class EmployeeRepository
{
public void Save(Employee employee) { /* 保存到数据库 */ }
}
在这个例子中,Employee
类只负责工资计算,而 EmployeeRepository
类负责数据存储,符合单一职责原则。
5. 如何实现单一职责原则?
- 识别类的职责:分析类的功能,识别出不同的职责。
- 分离职责:将不同的职责分离到不同的类中。
- 使用接口和抽象类:通过接口和抽象类定义职责,确保类的实现符合单一职责。
- 重构现有代码:在不影响现有功能的前提下,逐步重构代码以符合单一职责原则。
2. 开放封闭原则(Open Closed Principle)
1. 定义
类、模块和函数应该对扩展开放,对修改关闭。
2. 核心思想
通过抽象类和接口来实现扩展,而不是通过修改现有代码。
3. 为什么要遵守开放封闭原则?
好处:
- 提高系统的稳定性:通过扩展而不是修改来实现新功能,减少了引入新错误的风险。
- 增强系统的可扩展性:可以在不修改现有代码的情况下添加新功能。
坏处:
- 可能导致过度设计:在简单的系统中,过度使用抽象可能导致不必要的复杂性。
- 增加系统的初始设计成本:需要在设计初期考虑扩展性,可能增加开发时间。
4. 实际案例
违反开放封闭原则的示例
csharp
public class ShapeCalculator
{
public double CalculateArea(object shape)
{
if (shape is Circle)
{
Circle circle = (Circle)shape;
return Math.PI * circle.Radius * circle.Radius;
}
else if (shape is Rectangle)
{
Rectangle rectangle = (Rectangle)shape;
return rectangle.Width * rectangle.Height;
}
return 0;
}
}
在这个例子中,每次添加新形状都需要修改 ShapeCalculator
类。
遵守开放封闭原则的示例
csharp
public abstract class Shape
{
public abstract double CalculateArea();
}
public class Circle : Shape
{
public double Radius { get; set; }
public override double CalculateArea()
{
return Math.PI * Radius * Radius;
}
}
public class Rectangle : Shape
{
public double Width { get; set; }
public double Height { get; set; }
public override double CalculateArea()
{
return Width * Height;
}
}
public class ShapeCalculator
{
public double CalculateArea(Shape shape)
{
return shape.CalculateArea();
}
}
在这个例子中,ShapeCalculator
类不需要修改即可支持新形状。
5. 如何实现开放封闭原则?
- 使用抽象类和接口:定义抽象类和接口来隔离变化。
- 依赖于抽象而不是具体实现:通过依赖注入等方式使用抽象类和接口。
- 使用设计模式:如策略模式、装饰模式等来实现扩展。
- 重构现有代码:在不影响现有功能的前提下,逐步重构代码以符合开放封闭原则。
3. 里氏替换原则(Liskov Substitution Principle)
1. 定义
子类必须能够替换父类,并且程序的行为不会改变。即子类对象必须能够替换程序中父类对象出现的任何地方,保证原有程序的逻辑行为不变。
2. 核心思想
- 子类继承父类时,应该扩展父类的功能,而不是改变父类原有的功能
- 子类的行为必须与父类保持一致,不能违背父类的"契约"
- 面向抽象编程,依赖父类接口而不是具体实现
3. 为什么要遵守里氏替换原则?
好处:
- 保证系统的稳定性:子类不会破坏父类的行为约定,避免运行时错误
- 提高代码的可复用性:可以安全地在任何使用父类的地方使用子类
- 增强系统的可扩展性:新增子类不会影响现有系统的行为
- 实现基于契约设计:通过接口约定确保行为的一致性
坏处:
- 可能限制继承的使用:某些情况下可能需要更灵活的继承关系
- 增加设计难度:需要仔细考虑父类的契约和子类的实现
- 可能导致类层次变深:为了保持行为一致可能需要增加抽象层次
4. 实际案例
不遵守里氏替换原则的案例
csharp
public class Rectangle
{
public virtual int Width { get; set; }
public virtual int Height { get; set; }
public int GetArea()
{
return Width * Height;
}
}
public class Square : Rectangle
{
public override int Width
{
set { base.Width = base.Height = value; }
}
public override int Height
{
set { base.Width = base.Height = value; }
}
}
// 使用示例
Rectangle rect = new Square();
rect.Width = 5;
rect.Height = 10; // 违反了矩形的行为约定
Console.WriteLine(rect.GetArea()); // 期望50,实际得到100
遵守里氏替换原则的案例
csharp
public class Rectangle
{
public int Width { get; set; }
public int Height { get; set; }
public int GetArea()
{
return Width * Height;
}
}
public class Square
{
private Rectangle rectangle = new Rectangle();
public int Side
{
get { return rectangle.Width; }
set { rectangle.Width = rectangle.Height = value; }
}
public int GetArea()
{
return rectangle.GetArea();
}
}
5. 如何实现里氏替换原则?
-
正确使用继承关系
- 确保子类扩展而不是改变父类的功能
- 子类必须完全实现父类的方法
- 子类可以有自己的个性
-
严格遵守契约
- 子类必须遵守父类的行为约定
- 子类可以适当扩展约定,但不能违背
-
使用组合替代继承
- 当继承可能破坏父类的行为约定时,使用组合
- 将继承关系改为组合关系
-
面向接口编程
- 优先使用接口定义行为契约
- 通过接口隔离变化
-
加强单元测试
- 编写测试用例验证子类行为
- 确保子类可以在任何使用父类的地方正常工作。
4. 依赖倒置原则(Dependency Inversion Principle)
依赖倒置原则(Dependency Inversion Principle, DIP) 是面向对象设计的六大原则之一,它的核心思想是高层模块不应该依赖于低层模块,二者都应该依赖于抽象;抽象不应该依赖于细节,细节应该依赖于抽象。简单来说,就是通过面向抽象(接口或抽象类)编程,而不是面向实现编程。
1. 定义
* 高层模块不应该依赖低层模块,两者都应该依赖其抽象
* 抽象不应该依赖于细节,细节应该依赖于抽象
- 高层模块不应该依赖于低层模块,二者都应该依赖于抽象。
- 高层模块:通常是指业务逻辑层或核心功能模块。
- 低层模块:通常是指具体实现层或基础设施模块。
- 抽象(接口或抽象类)不应该依赖于细节(具体实现),细节应该依赖于抽象。
- 抽象:通常指接口或抽象类。
- 细节:通常指具体实现类。
2. 核心思想
- 依赖倒置原则的核心思想是:
- 通过抽象解耦:高层模块和低层模块通过抽象(接口或抽象类)进行交互,而不是直接依赖具体实现。
- 减少代码耦合:高层模块不依赖于低层模块的具体实现,从而使得系统更灵活、更易扩展。
- 面向接口编程:通过定义接口或抽象类,屏蔽具体实现细节,使代码更加稳定。
3. 为什么要遵守依赖倒置原则?
好处:
- 降低系统耦合性:通过抽象接口降低了各模块间的依赖
- 提高系统稳定性:高层模块不依赖于低层模块的实现细节
- 提高代码可维护性:各模块通过抽象接口交互,修改实现时不会互相影响
- 提高代码可测试性:可以通过抽象接口轻松地模拟依赖对象
坏处:
- 系统抽象层次增加:可能会引入更多的接口和抽象类
- 开发成本上升:需要进行更详细的前期设计
- 难度增加:对开发人员的要求更高,需要更好的抽象思维能力
4. 实际案例
不遵守依赖倒置原则的案例
csharp
// 高层模块直接依赖低层模块
public class MessageSender
{
private EmailSender emailSender;
public MessageSender()
{
emailSender = new EmailSender(); // 直接依赖具体实现
}
public void SendMessage(string message)
{
emailSender.SendEmail(message);
}
}
public class EmailSender
{
public void SendEmail(string message)
{
Console.WriteLine("Sending Email: " + message);
}
}
遵守依赖倒置原则的案例
csharp
// 定义抽象接口
public interface IMessageSender
{
void Send(string message);
}
// 低层模块实现接口
public class EmailSender : IMessageSender
{
public void Send(string message)
{
Console.WriteLine("Sending Email: " + message);
}
}
public class SmsSender : IMessageSender
{
public void Send(string message)
{
Console.WriteLine("Sending SMS: " + message);
}
}
// 高层模块依赖于抽象
public class MessageSender
{
private readonly IMessageSender _messageSender;
public MessageSender(IMessageSender messageSender)
{
_messageSender = messageSender;
}
public void SendMessage(string message)
{
_messageSender.Send(message);
}
}
5. 如何实现依赖倒置原则?
-
定义抽象层
- 创建接口或抽象类
- 在抽象层定义行为契约
- 确保抽象层稳定性
-
实现依赖注入
- 构造函数注入
- 属性注入
- 方法注入
- 使用依赖注入容器
-
遵循面向接口编程
- 优先使用接口而不是具体类型
- 避免在高层模块中直接实例化低层模块
- 通过工厂模式创建对象
-
合理设计抽象
- 抽象要足够稳定
- 抽象粒度要合适
- 避免过度抽象
-
使用设计模式
- 工厂模式
- 抽象工厂模式
- 模板方法模式
5. 接口隔离原则(Interface Segregation Principle)
接口隔离原则(Interface Segregation Principle, ISP) 是面向对象设计的六大原则之一,其核心思想是客户端不应该被强迫依赖于它不需要的接口。换句话说,一个接口应该只包含客户端真正需要的方法,不应该将多个不相关的功能强行组合到一个接口中。
1. 定义
接口隔离原则的关键点是:
- 接口应该小而专一 :
- 一个接口只应该包含客户端需要的方法,避免臃肿和多余。
- 接口的职责要单一 :
- 每个接口应该只负责一种功能,避免接口承担过多职责。
2. 核心思想
接口隔离原则的核心思想是通过拆分接口来降低系统的耦合性。当一个接口过于庞大时,可能会导致以下问题:
- 实现类需要实现不必要的方法 :
- 如果一个实现类只需要接口的一部分功能,却被迫实现整个接口,就会导致代码膨胀。
- 接口的修改会影响所有实现类 :
- 如果一个庞大的接口被修改,所有依赖该接口的实现类都可能需要调整,增加维护成本。
- 违背单一职责原则 :
- 一个接口承担了过多的职责,导致接口变得难以理解和维护。
通过遵守接口隔离原则,可以确保接口的设计更贴合实际需求,降低系统的复杂度。
3. 为什么要遵守接口隔离原则?
问题:如果不遵守,会带来以下问题:
-
实现类承担额外的负担:
- 如果一个接口包含了不相关的功能,所有实现类都需要实现这些无关的方法,即使它们并不需要这些功能。
-
接口的变更影响范围过大:
- 当一个庞大的接口发生变更时,所有依赖该接口的实现类都可能受到影响,增加了系统的维护成本。
-
代码难以复用:
- 一个接口承担过多职责时,复用性会降低,因为不同的类可能只需要接口的一部分功能。
好处:遵守接口隔离原则的优势
- 提高系统的灵活性 :
- 接口更加精简,类只需要依赖它真正需要的接口,减少了不必要的耦合。
- 降低维护成本 :
- 接口的修改只会影响少量的实现类,降低了修改的风险。
- 提高代码的复用性 :
- 小而专一的接口可以更容易被复用。
4. 实际案例
不遵守接口隔离原则的设计
假设我们设计一个动物行为的接口 IAnimal
,包含以下方法:
csharp
//错误示例
public interface IAnimal
{
void Eat();
void Fly();
void Swim();
}
//如果我们有一个 Bird 类和一个 Fish 类需要实现这个接口:
public class Bird : IAnimal
{
public void Eat()
{
Console.WriteLine("Bird is eating");
}
public void Fly()
{
Console.WriteLine("Bird is flying");
}
public void Swim()
{
// 鸟不会游泳,但必须实现这个方法
throw new NotImplementedException("Bird can't swim");
}
}
public class Fish : IAnimal
{
public void Eat()
{
Console.WriteLine("Fish is eating");
}
public void Fly()
{
// 鱼不会飞,但必须实现这个方法
throw new NotImplementedException("Fish can't fly");
}
public void Swim()
{
Console.WriteLine("Fish is swimming");
}
}
//问题:
//Bird 和 Fish 都必须实现它们不需要的方法(Swim 或 Fly),导致代码臃肿。
//如果接口 IAnimal 增加新的方法(如 Run),所有实现类都需要修改,影响范围过大。
csharp
//正确示例
public interface IEater
{
void Eat();
}
public interface IFlyer
{
void Fly();
}
public interface ISwimmer
{
void Swim();
}
//然后让 Bird 和 Fish 分别实现它们需要的接口
public class Bird : IEater, IFlyer
{
public void Eat()
{
Console.WriteLine("Bird is eating");
}
public void Fly()
{
Console.WriteLine("Bird is flying");
}
}
public class Fish : IEater, ISwimmer
{
public void Eat()
{
Console.WriteLine("Fish is eating");
}
public void Swim()
{
Console.WriteLine("Fish is swimming");
}
}
5. 如何实现接口隔离原则?
- 拆分接口
- 将一个大的接口拆分为多个小接口,每个接口只包含一种功能。
- 例如,将 IAnimal 接口拆分为 IEater、IFlyer 和 ISwimmer 接口。
- 根据实际需求设计接口
- 接口的设计应该基于实际需求,而不是试图设计一个"万能接口"。
- 避免接口污染
- 不要在接口中添加客户端不需要的方法,保持接口的精简和专一。
6. 迪米特法则(Law of Demeter)
迪米特法则(Law of Demeter, LoD) ,也称为最少知识原则(Least Knowledge Principle, LKP) ,
是面向对象设计中的一项重要原则。它的核心思想是:一个对象应当尽可能少地了解其他对象的细节 。
换句话说,一个模块或类不应该直接依赖于过多的其他模块或类,而是通过有限的接口与外部进行通信。
1. 定义
迪米特法则的核心定义是:
- 一个对象只与它直接相关的对象进行通信,不应该直接调用其他对象的内部细节。
- 不要"越级"访问对象的成员或方法,即避免"对象链式调用"。
非正式的描述**
"只与你的直接朋友通信,不要和'陌生人'说话。"
- 直接朋友:当前类的成员变量、方法参数或方法返回值中的对象。
- 陌生人:通过其他对象间接获得的对象。
2. 核心思想
迪米特法则的核心思想是降低对象之间的耦合度,使系统更加模块化和易于维护。具体包括:
- 减少依赖关系:通过限制类之间的通信范围,降低系统的耦合性。
- 提高可维护性:当一个类发生变化时,对其他类的影响会更小。
- 隐藏实现细节:通过封装,避免暴露不必要的内部细节。
3. 为什么要遵守迪米特法则?
问题:如果不遵守,会带来以下问题:**
-
系统耦合性过高:
- 如果一个对象直接依赖于多个其他对象的具体实现,当这些对象发生变化时,当前对象也需要修改,增加了维护成本。
-
代码难以复用:
- 高耦合的代码很难在其他场景中复用,因为它依赖于太多其他对象。
-
难以测试:
- 如果一个对象需要依赖多个其他对象才能工作,那么单独测试它会变得非常困难。
好处:遵守迪米特法则的优势**
- 降低耦合性 :
- 类之间的依赖关系减少,使得系统更稳定。
- 提高模块化 :
- 每个类只需要关注自己的职责,代码更清晰。
- 增强可维护性 :
- 当一个类发生变化时,对其他类的影响更小。
4. 实际案例
不遵守迪米特法则的设计
csharp
//错误示例
//假设我们有一个 `Person` 类和一个 `Car` 类,`Person` 想通过 `Car` 的引擎(`Engine`)启动汽车:
public class Engine
{
public void Start()
{
Console.WriteLine("Engine started.");
}
}
public class Car
{
public Engine GetEngine()
{
return new Engine();
}
}
public class Person
{
public void Drive(Car car)
{
// 通过 Car 获取 Engine,然后调用 Engine 的方法
car.GetEngine().Start();
}
}
//问题:
//Person 类直接依赖于 Car 和 Engine 两个类的具体实现。
//如果 Engine 类的实现发生变化(例如方法名从 Start 改为 Ignite),Person 类也需要修改。
//Person 类越级访问了 Engine 的内部细节,违反了迪米特法则。
csharp
//正确示例
public class Engine
{
public void Start()
{
Console.WriteLine("Engine started.");
}
}
public class Car
{
private Engine engine = new Engine();
public void Start()
{
engine.Start();
}
}
public class Person
{
public void Drive(Car car)
{
// 直接调用 Car 的方法,而不是访问 Car 的内部细节
car.Start();
}
}
5. 如何实现迪米特法则?
- 封装内部细节
- 不要暴露类的内部成员(如成员变量或方法),通过提供公共方法来对外提供服务。
csharp
//错误示例:
public Engine GetEngine() { return engine; }
//正确示例:
public void Start() { engine.Start(); }
- 避免链式调用
- 链式调用会导致类对多个对象的内部细节产生依赖,增加了耦合性。
csharp
//错误示例:
person.GetCar().GetEngine().Start();
//正确示例:
person.Drive();
- 只与直接朋友通信
一个类只与它的成员变量、方法参数或方法返回值中的对象通信。
不要通过一个对象访问另一个对象的内部细节。
7. 合成复用原则(Composite Reuse Principle)
核心思想是:尽量使用对象组合(Composition)来实现代码复用,而不是通过继承(Inheritance)来扩展功能。
1. 定义
合成复用原则的关键定义是:
- 优先使用组合,而非继承 :
- 通过将现有对象作为新对象的成员变量(即组合),来复用已有的功能。
- 避免继承的过度使用 :
- 继承虽然是代码复用的一种方式,但会导致子类与父类之间的强耦合,限制了子类的灵活性。
2. 核心思想
合成复用原则的核心思想是:通过组合和委托的方式复用代码,降低系统的耦合性,提高灵活性和可扩展性。
- 继承的局限性 :
- 继承是一种"白箱复用",父类的实现细节对子类是透明的,子类过于依赖父类。
- 继承是一种强耦合关系,子类的变化可能受到父类的限制。
- 组合的优势 :
- 组合是一种"黑箱复用",对象的内部实现对外部是不可见的,调用方只需通过接口与对象交互。
- 组合降低了耦合性,增强了系统的灵活性。
3. 为什么要遵守合成复用原则?
问题:如果过度使用继承,会带来以下问题:
- 父类的修改会影响所有子类 :
- 如果父类的实现发生变化,所有继承该父类的子类都可能需要调整。
- 继承限制了子类的灵活性 :
- 子类必须遵从父类的结构和行为,无法灵活地进行扩展。
- 代码复用的局限性 :
- 继承只能复用单一父类的功能,而组合可以复用多个类的功能。
好处:遵守合成复用原则的优势
- 降低耦合性 :
- 对象之间通过接口进行交互,而不是直接依赖具体实现。
- 提高灵活性 :
- 通过组合,可以动态地更换或扩展对象的功能。
- 增强可维护性 :
- 组合的方式更容易进行功能的修改和扩展,而不需要改变现有的类。
4. 实际案例
不遵守合成复用原则的设计
csharp
//错误示例
//假设我们有一个 `Car` 类和一个 `ElectricCar` 类,`ElectricCar` 通过继承 `Car` 来扩展功能:
public class Car
{
public void Drive()
{
Console.WriteLine("Driving a car...");
}
}
public class ElectricCar : Car
{
public void ChargeBattery()
{
Console.WriteLine("Charging the battery...");
}
}
//问题:
//如果 Car 类发生变化(例如增加新的方法或修改现有方法),ElectricCar 类也需要同步调整。
//如果我们需要一个混合动力车(既有燃油车的功能,又有电动车的功能),会导致复杂的多重继承问题(C# 不支持多继承)。
遵守合成复用原则的设计
csharp
//正确示例
//我们可以通过组合的方式,将 Car 和 Battery 的功能组合在一起,而不是通过继承来扩展功能:
public class Car
{
public void Drive()
{
Console.WriteLine("Driving a car...");
}
}
public class Battery
{
public void Charge()
{
Console.WriteLine("Charging the battery...");
}
}
public class ElectricCar
{
private Car car = new Car();
private Battery battery = new Battery();
public void Drive()
{
car.Drive();
}
public void ChargeBattery()
{
battery.Charge();
}
}
//改进:
//ElectricCar 类通过组合 Car 和 Battery 的功能实现代码复用,而不是通过继承。
//如果 Car 或 Battery 的实现发生变化,只需要修改它们自身,不会影响 ElectricCar。
//可以更灵活地扩展功能,例如再添加一个 FuelTank 类,实现混合动力车的功能。
5.如何实现合成复用原则?
- 优先使用组合
将已有类作为新类的成员变量,通过组合来复用功能。
示例:
csharp
public class Engine
{
public void Start()
{
Console.WriteLine("Engine started.");
}
}
public class Car
{
private Engine engine = new Engine();
public void StartCar()
{
engine.Start();
}
}
- 使用接口保证灵活性
通过接口定义行为,使类之间的依赖关系更加灵活。
示例:
csharp
public interface IEngine
{
void Start();
}
public class ElectricEngine : IEngine
{
public void Start()
{
Console.WriteLine("Electric engine started.");
}
}
public class Car
{
private IEngine engine;
public Car(IEngine engine)
{
this.engine = engine;
}
public void StartCar()
{
engine.Start();
}
}
- 避免继承的滥用
继承应该仅在 "is-a" 关系明确时使用,而不是单纯为了代码复用。
错误示例:
csharp
public class Printer
{
public void Print()
{
Console.WriteLine("Printing...");
}
}
public class Scanner : Printer
{
public void Scan()
{
Console.WriteLine("Scanning...");
}
}
正确示例:
csharp
public class Printer
{
public void Print()
{
Console.WriteLine("Printing...");
}
}
public class Scanner
{
public void Scan()
{
Console.WriteLine("Scanning...");
}
}
public class AllInOnePrinter
{
private Printer printer = new Printer();
private Scanner scanner = new Scanner();
public void Print()
{
printer.Print();
}
public void Scan()
{
scanner.Scan();
}
}
23种设计模式
创建型模式
1. 单例模式(Singleton Pattern)
1. 定义
- 单例模式(Singleton Pattern)是一种创建型设计模式,它确保一个类只有一个实例,并提供一个全局访问点。
2. 适用场景
- 当类只能有一个实例且客户端可以从一个众所周知的访问点访问它时
- 当唯一实例应该通过子类化可扩展,并且客户端应该能够使用扩展实例而不需要修改代码时
- 需要严格控制全局变量的场景
- 资源共享的场景,如数据库连接池、线程池等
- 配置管理类
- 日志记录器
3. 结构
plaintext
+-------------------+
| Singleton |
+-------------------+
| - instance |
+-------------------+
| - Singleton() |
| + GetInstance() |
+-------------------+
推荐使用下面的Lazy方法实现线程安全的懒汉模式
csharp
using System;
using System.IO;
using System.Threading;
public sealed class SingletonLogger
{
// 使用Lazy<T>实现线程安全的单例
private static readonly Lazy<SingletonLogger> _instance =
new Lazy<SingletonLogger>(() => new SingletonLogger());
// 日志文件路径
private readonly string _logFilePath = "application.log";
// 私有构造函数防止外部实例化
private SingletonLogger()
{
// 初始化日志文件
File.WriteAllText(_logFilePath, "Log started at " + DateTime.Now + "\n");
}
// 全局访问点
public static SingletonLogger GetInstance() => _instance.Value;
// 记录日志方法
public void Log(string message)
{
File.AppendAllText(_logFilePath,
$"[{DateTime.Now}] {message}\n");
}
// 示例用法
public static void Main(string[] args)
{
// 获取单例实例
var logger = SingletonLogger.GetInstance();
// 记录日志
logger.Log("Application started");
// 尝试创建新实例
var anotherLogger = SingletonLogger.GetInstance();
// 检查是否是同一个实例
Console.WriteLine(logger == anotherLogger); // 输出:True
// 记录更多日志
logger.Log("Processing data...");
logger.Log("Application shutdown");
}
}
4. 实现步骤
- 将类的构造函数设为私有,防止外部实例化
- 创建一个静态私有变量来保存类的唯一实例
- 提供一个静态公共方法来获取该实例
- 考虑线程安全问题
5. 效果
好处:
- 严格控制实例数量
- 全局访问点方便管理
- 节省系统资源
- 避免对资源的多重占用
坏处:
- 违反单一职责原则(既管理实例又处理业务逻辑)
- 可能造成代码耦合
- 难以进行单元测试
- 在多线程环境下需要特殊处理
6. 总结
- 单例模式是一种简单但强大的设计模式,它通过控制实例化过程来确保系统中某个类只有一个实例。虽然它有很多优点,但也存在一些潜在的问题,特别是在多线程环境下需要特别注意线程安全问题。在使用时需要权衡利弊,确保在合适的场景下使用。
2. 工厂方法模式(Factory Method Pattern)
1. 定义
- 工厂方法模式(Factory Method Pattern)是一种创建型设计模式,它定义了一个创建对象的接口,但让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。
2. 适用场景
- 当一个类不知道它所需要的对象的类时
- 当一个类希望由它的子类来指定它所创建的对象时
- 当类将创建对象的职责委托给多个帮助子类中的某一个,并且你希望将哪一个帮助子类是代理者这一信息局部化时
- 需要灵活、可扩展的框架时
- 需要解耦具体产品与使用者时
3. 结构
plaintext
+-------------------+ +-------------------+
| Creator | | Product |
+-------------------+ +-------------------+
| + FactoryMethod() |<------>| + Operation() |
+-------------------+ +-------------------+
^ ^
| |
+-------------------+ +-------------------+
| ConcreteCreator | | ConcreteProduct |
+-------------------+ +-------------------+
| + FactoryMethod() | | + Operation() |
+-------------------+ +-------------------+
4. 实现步骤
- 创建抽象产品接口
- 创建具体产品类实现接口
- 创建抽象工厂类,定义工厂方法
- 创建具体工厂类,实现工厂方法
- 客户端通过工厂类创建产品对象
5. 效果
好处:
- 提供灵活的扩展性
- 解耦具体产品与使用者
- 符合开闭原则
- 代码可维护性高
- 便于单元测试
坏处:
- 增加了类的数量
- 增加了系统的复杂性
- 需要额外的设计工作
- 可能引入不必要的抽象层
6. 总结
- 工厂方法模式通过将对象的创建过程封装在工厂类中,实现了创建逻辑与使用逻辑的分离。它提供了一种灵活的方式来创建对象,同时保持了代码的可扩展性和可维护性。虽然它增加了系统的复杂性,但在需要灵活创建对象的场景中非常有用。
7. 实际案例 - 日志记录器工厂
以下是一个C#工厂方法模式实现示例:
csharp
using System;
// 产品接口
public interface ILogger
{
void Log(string message);
}
// 具体产品 - 文件日志
public class FileLogger : ILogger
{
public void Log(string message)
{
Console.WriteLine($"File Logger: {message}");
}
}
// 具体产品 - 控制台日志
public class ConsoleLogger : ILogger
{
public void Log(string message)
{
Console.WriteLine($"Console Logger: {message}");
}
}
// 抽象工厂
public abstract class LoggerFactory
{
public abstract ILogger CreateLogger();
public void Log(string message)
{
var logger = CreateLogger();
logger.Log(message);
}
}
// 具体工厂 - 文件日志工厂
public class FileLoggerFactory : LoggerFactory
{
public override ILogger CreateLogger()
{
return new FileLogger();
}
}
// 具体工厂 - 控制台日志工厂
public class ConsoleLoggerFactory : LoggerFactory
{
public override ILogger CreateLogger()
{
return new ConsoleLogger();
}
}
// 客户端代码
public class Program
{
public static void Main(string[] args)
{
// 使用文件日志
LoggerFactory fileFactory = new FileLoggerFactory();
fileFactory.Log("This is a file log message");
// 使用控制台日志
LoggerFactory consoleFactory = new ConsoleLoggerFactory();
consoleFactory.Log("This is a console log message");
}
}
3. 抽象工厂模式(Abstract Factory Pattern)
1. 定义
- 抽象工厂模式(Abstract Factory Pattern)是一种创建型设计模式,它提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
2. 适用场景
- 系统需要独立于其产品的创建、组合和表示时
- 系统需要配置多个产品族中的一个时
- 需要提供一个产品类库,且只暴露接口而不是实现时
- 强调一系列相关产品对象的设计约束时
- 需要保证产品一致性时
3. 结构
plaintext
+-------------------+ +-------------------+
| AbstractFactory | | AbstractProductA |
+-------------------+ +-------------------+
| + CreateProductA()|<------>| + Operation() |
| + CreateProductB()| +-------------------+
+-------------------+ ^
^ |
| |
+-------------------+ +-------------------+
| ConcreteFactory1 | | ProductA1 |
+-------------------+ +-------------------+
| + CreateProductA()| | + Operation() |
| + CreateProductB()| +-------------------+
+-------------------+ ^
| |
+-------------------+ +-------------------+
| ConcreteFactory2 | | ProductA2 |
+-------------------+ +-------------------+
| + CreateProductA()| | + Operation() |
| + CreateProductB()| +-------------------+
+-------------------+
4. 实现步骤
- 创建抽象产品接口
- 创建具体产品类实现接口
- 创建抽象工厂接口
- 创建具体工厂类实现工厂接口
- 客户端通过抽象工厂接口创建产品对象
5. 效果
好处:
- 隔离具体类的生成
- 易于交换产品系列
- 保证产品一致性
- 符合开闭原则
- 符合单一职责原则
坏处:
- 增加系统复杂性
- 增加类的数量
- 扩展产品族困难
- 增加系统抽象性
6. 总结
抽象工厂模式通过提供一个创建一系列相关或相互依赖对象的接口,而不指定它们具体的类,实现了产品族的创建与使用的分离。它特别适用于需要保证产品一致性的场景,但也会增加系统的复杂性。
7. 实际案例 - 跨平台UI组件
以下是一个C#抽象工厂模式实现示例:
csharp
// 抽象产品 - 按钮
public interface IButton
{
void Render();
void OnClick();
}
// 具体产品 - Windows按钮
public class WindowsButton : IButton
{
public void Render()
{
Console.WriteLine("Render a button in Windows style");
}
public void OnClick()
{
Console.WriteLine("Handle a Windows button click event");
}
}
// 具体产品 - MacOS按钮
public class MacOSButton : IButton
{
public void Render()
{
Console.WriteLine("Render a button in MacOS style");
}
public void OnClick()
{
Console.WriteLine("Handle a MacOS button click event");
}
}
// 抽象产品 - 复选框
public interface ICheckbox
{
void Render();
void OnCheck();
}
// 具体产品 - Windows复选框
public class WindowsCheckbox : ICheckbox
{
public void Render()
{
Console.WriteLine("Render a checkbox in Windows style");
}
public void OnCheck()
{
Console.WriteLine("Handle a Windows checkbox check event");
}
}
// 具体产品 - MacOS复选框
public class MacOSCheckbox : ICheckbox
{
public void Render()
{
Console.WriteLine("Render a checkbox in MacOS style");
}
public void OnCheck()
{
Console.WriteLine("Handle a MacOS checkbox check event");
}
}
// 抽象工厂
public interface IGUIFactory
{
IButton CreateButton();
ICheckbox CreateCheckbox();
}
// 具体工厂 - Windows工厂
public class WindowsFactory : IGUIFactory
{
public IButton CreateButton()
{
return new WindowsButton();
}
public ICheckbox CreateCheckbox()
{
return new WindowsCheckbox();
}
}
// 具体工厂 - MacOS工厂
public class MacOSFactory : IGUIFactory
{
public IButton CreateButton()
{
return new MacOSButton();
}
public ICheckbox CreateCheckbox()
{
return new MacOSCheckbox();
}
}
// 客户端代码
public class Application
{
private IButton _button;
private ICheckbox _checkbox;
public Application(IGUIFactory factory)
{
_button = factory.CreateButton();
_checkbox = factory.CreateCheckbox();
}
public void RenderUI()
{
_button.Render();
_checkbox.Render();
}
public void SimulateUserInteraction()
{
_button.OnClick();
_checkbox.OnCheck();
}
}
// 使用示例
public class Program
{
public static void Main(string[] args)
{
Console.WriteLine("Creating Windows UI:");
//具体创建哪一种UI可以由配置文件指定
var windowsApp = new Application(new WindowsFactory());
windowsApp.RenderUI();
windowsApp.SimulateUserInteraction();
Console.WriteLine("\nCreating MacOS UI:");
var macApp = new Application(new MacOSFactory());
macApp.RenderUI();
macApp.SimulateUserInteraction();
}
}
4. 建造者模式(Builder Pattern)
1. 定义
建造者模式(Builder Pattern)是一种创建型设计模式,它允许你分步骤创建复杂对象。该模式使你能够使用相同的创建过程生成不同类型和表示的对象。
2. 适用场景
- 当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时
- 当构造过程必须允许被构造的对象有不同的表示时
- 当需要更好地控制对象的创建过程时
- 当需要创建的对象包含多个组成部分,且这些部分需要按特定顺序组装时
- 当需要避免"重叠构造函数"问题时
3. 结构
plaintext
+-------------------+ +-------------------+
| Director | | Builder |
+-------------------+ +-------------------+
| + Construct() |<------>| + BuildPartA() |
+-------------------+ | + BuildPartB() |
^ | + GetResult() |
| +-------------------+
| ^
+-------------------+ |
| Product | |
+-------------------+ +-------------------+
| + PartA | | ConcreteBuilder |
| + PartB | +-------------------+
+-------------------+ | + BuildPartA() |
| + BuildPartB() |
| + GetResult() |
+-------------------+
4. 实现步骤
- 创建产品类
- 创建抽象建造者接口
- 创建具体建造者类
- 创建指挥者类
- 客户端通过指挥者创建产品
5. 效果
好处:
- 封装复杂对象的创建过程
- 允许对象分步骤创建
- 复用相同的创建过程
- 符合单一职责原则
- 符合开闭原则
坏处:
- 增加系统复杂性
- 增加类的数量
- 需要额外设计工作
- 可能引入不必要的抽象层
6. 总结
建造者模式通过将复杂对象的构建过程分解为多个简单步骤,使得相同的构建过程可以创建不同的表示。它特别适用于需要分步骤创建复杂对象的场景,但也会增加系统的复杂性。
7. 实际案例 - 电脑组装
以下是一个C#建造者模式实现示例:
csharp
// 产品 - 电脑
public class Computer
{
public string CPU { get; set; }
public string RAM { get; set; }
public string Storage { get; set; }
public string GPU { get; set; }
public void DisplayConfiguration()
{
Console.WriteLine("Computer Configuration:");
Console.WriteLine($"CPU: {CPU}");
Console.WriteLine($"RAM: {RAM}");
Console.WriteLine($"Storage: {Storage}");
Console.WriteLine($"GPU: {GPU ?? "Integrated"}");
}
}
// 抽象建造者
public interface IComputerBuilder
{
void SetCPU();
void SetRAM();
void SetStorage();
void SetGPU();
Computer GetComputer();
}
// 具体建造者 - 游戏电脑
public class GamingComputerBuilder : IComputerBuilder
{
private Computer _computer = new Computer();
public void SetCPU()
{
_computer.CPU = "Intel Core i9-13900K";
}
public void SetRAM()
{
_computer.RAM = "32GB DDR5";
}
public void SetStorage()
{
_computer.Storage = "2TB NVMe SSD";
}
public void SetGPU()
{
_computer.GPU = "NVIDIA RTX 4090";
}
public Computer GetComputer()
{
return _computer;
}
}
// 具体建造者 - 办公电脑
public class OfficeComputerBuilder : IComputerBuilder
{
private Computer _computer = new Computer();
public void SetCPU()
{
_computer.CPU = "Intel Core i5-13400";
}
public void SetRAM()
{
_computer.RAM = "16GB DDR4";
}
public void SetStorage()
{
_computer.Storage = "512GB SSD";
}
public void SetGPU()
{
// 办公电脑不需要独立显卡
}
public Computer GetComputer()
{
return _computer;
}
}
// 指挥者
public class ComputerDirector
{
public Computer Build(IComputerBuilder builder)
{
builder.SetCPU();
builder.SetRAM();
builder.SetStorage();
builder.SetGPU();
return builder.GetComputer();
}
}
// 客户端代码
public class Program
{
public static void Main(string[] args)
{
var director = new ComputerDirector();
Console.WriteLine("Building Gaming Computer:");
var gamingBuilder = new GamingComputerBuilder();
var gamingComputer = director.Build(gamingBuilder);
gamingComputer.DisplayConfiguration();
Console.WriteLine("\nBuilding Office Computer:");
var officeBuilder = new OfficeComputerBuilder();
var officeComputer = director.Build(officeBuilder);
officeComputer.DisplayConfiguration();
}
}
5. 原型模式详解
1. 定义
原型模式(Prototype Pattern)是一种创建型设计模式 ,它通过复制 现有对象来创建新对象,而不是通过new 关键字实例化。原型模式特别适用于创建成本高 的对象,或者需要动态配置的对象。
2. 适用场景
- 需要避免 与产品类层次结构耦合时
- 需要动态配置对象时
- 需要减少 子类的数量时
- 需要提高 对象创建的性能时
- 需要隔离对象的创建和使用时
3. 结构
plaintext
+-------------------+ +-------------------+
| Prototype | | Client |
+-------------------+ +-------------------+
| + Clone() |<------>| + Operation() |
+-------------------+ +-------------------+
^
|
+-------------------+
| ConcretePrototype |
+-------------------+
| + Clone() |
+-------------------+
4. 实现步骤
- 定义原型接口(Prototype)
- 实现具体原型类(ConcretePrototype)
- 实现**Clone()**方法
- 客户端通过**Clone()**方法创建对象
5. 效果
好处:
- 提高 对象创建的性能
- 简化 对象创建的过程
- 动态配置对象
- 减少 子类的数量
- 隔离对象的创建和使用
坏处:
- 需要**实现Clone()**方法
- 可能复杂化对象结构
- 需要处理深拷贝和浅拷贝
- 可能违反封装原则
6. 总结
原型模式通过复制 现有对象来创建新对象,特别适用于创建成本高 的对象。它提高 了对象创建的性能 ,但需要注意 深拷贝和浅拷贝的问题。
7. 实际案例 - 图形编辑器
以下是一个C#原型模式实现示例:
csharp
// 原型接口
public interface IGraphic
{
IGraphic Clone();
void Draw();
}
// 具体原型1 - 圆形
public class Circle : IGraphic
{
public int Radius { get; set; }
public string Color { get; set; }
public Circle(int radius, string color)
{
Radius = radius;
Color = color;
}
public IGraphic Clone()
{
return new Circle(Radius, Color);
}
public void Draw()
{
Console.WriteLine($"Drawing Circle: Radius={Radius}, Color={Color}");
}
}
// 具体原型2 - 矩形
public class Rectangle : IGraphic
{
public int Width { get; set; }
public int Height { get; set; }
public string Color { get; set; }
public Rectangle(int width, int height, string color)
{
Width = width;
Height = height;
Color = color;
}
public IGraphic Clone()
{
return new Rectangle(Width, Height, Color);
}
public void Draw()
{
Console.WriteLine($"Drawing Rectangle: Width={Width}, Height={Height}, Color={Color}");
}
}
// 图形管理器
public class GraphicManager
{
private Dictionary<string, IGraphic> prototypes = new Dictionary<string, IGraphic>();
public void RegisterPrototype(string key, IGraphic prototype)
{
prototypes[key] = prototype;
}
public IGraphic CreateGraphic(string key)
{
if (prototypes.ContainsKey(key))
{
return prototypes[key].Clone();
}
throw new ArgumentException($"Prototype {key} not found");
}
}
// 客户端代码
public class Program
{
public static void Main(string[] args)
{
// 创建原型管理器
var manager = new GraphicManager();
// 注册原型
manager.RegisterPrototype("RedCircle", new Circle(10, "Red"));
manager.RegisterPrototype("BlueRectangle", new Rectangle(20, 30, "Blue"));
// 使用原型创建对象
var circle1 = manager.CreateGraphic("RedCircle");
var circle2 = manager.CreateGraphic("RedCircle");
var rectangle = manager.CreateGraphic("BlueRectangle");
// 绘制图形
circle1.Draw();
circle2.Draw();
rectangle.Draw();
// 修改克隆对象
if (circle2 is Circle c)
{
c.Radius = 15;
c.Color = "Green";
c.Draw();
}
}
}
代码注解
- IGraphic:定义图形接口
- Circle:具体原型,实现圆形
- Rectangle:具体原型,实现矩形
- Clone():克隆方法,实现原型模式
- Draw():绘制方法,展示图形
- GraphicManager:图形管理器,管理原型
- RegisterPrototype():注册原型方法
- CreateGraphic():创建图形方法
6. 组合模式(Composite Pattern) ★
1. 定义
组合模式(Composite Pattern)是一种结构型设计模式,它允许你将对象组合成树形结构以表示"部分-整体"的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。
2. 适用场景
- 需要表示对象的"部分-整体"层次结构时
- 希望用户忽略组合对象与单个对象的不同,统一地使用组合结构中的所有对象时
- 需要处理树形结构的数据时
- 需要对树形结构中的节点进行统一操作时
- 需要动态地添加或删除树形结构中的节点时
3. 结构
plaintext
+-------------------+ +-------------------+
| Component | | Leaf |
+-------------------+ +-------------------+
| + Operation() | | + Operation() |
| + Add(Component) | +-------------------+
| + Remove(Component)| ^
| + GetChild(int) | |
+-------------------+ |
^ |
| |
+-------------------+ |
| Composite | |
+-------------------+ |
| + Operation() | |
| + Add(Component) | |
| + Remove(Component)| |
| + GetChild(int) | |
+-------------------+ |
|
|
+-------------------+ |
| Client |---------------+
+-------------------+
4. 实现步骤
- 创建抽象组件接口
- 创建叶子组件类
- 创建组合组件类
- 客户端通过抽象接口操作组件
5. 效果
好处:
- 简化客户端代码
- 更容易添加新类型的组件
- 提供灵活的结构
- 符合开闭原则
- 符合单一职责原则
坏处:
- 设计可能过于通用
- 类型检查可能变得复杂
- 可能违反接口隔离原则
- 增加系统复杂性
6. 总结
组合模式通过将对象组织成树形结构,使得客户端可以统一处理单个对象和组合对象。它特别适用于需要表示"部分-整体"层次结构的场景,但可能会增加系统的复杂性。
7. 实际案例 - 文件系统
以下是一个C#组合模式实现示例:
csharp
// 抽象组件
public abstract class FileSystemComponent
{
public string Name { get; }
protected FileSystemComponent(string name)
{
Name = name;
}
public abstract void Display(int depth);
public abstract long GetSize();
}
// 叶子组件 - 文件
public class File : FileSystemComponent
{
private readonly long _size;
public File(string name, long size) : base(name)
{
_size = size;
}
public override void Display(int depth)
{
Console.WriteLine(new string('-', depth) + Name);
}
public override long GetSize()
{
return _size;
}
}
// 组合组件 - 文件夹
public class Directory : FileSystemComponent
{
private readonly List<FileSystemComponent> _children = new();
public Directory(string name) : base(name) { }
public void Add(FileSystemComponent component)
{
_children.Add(component);
}
public void Remove(FileSystemComponent component)
{
_children.Remove(component);
}
public override void Display(int depth)
{
Console.WriteLine(new string('-', depth) + Name);
foreach (var component in _children)
{
component.Display(depth + 2);
}
}
public override long GetSize()
{
return _children.Sum(component => component.GetSize());
}
}
// 客户端代码
public class Program
{
public static void Main(string[] args)
{
// 创建文件
var file1 = new File("file1.txt", 100);
var file2 = new File("file2.jpg", 200);
var file3 = new File("file3.doc", 150);
// 创建子目录
var subDir = new Directory("Sub Directory");
subDir.Add(file2);
subDir.Add(file3);
// 创建根目录
var rootDir = new Directory("Root Directory");
rootDir.Add(file1);
rootDir.Add(subDir);
// 显示目录结构
Console.WriteLine("File System Structure:");
rootDir.Display(1);
// 计算总大小
Console.WriteLine($"\nTotal Size: {rootDir.GetSize()} bytes");
}
}
7. 适配器模式(Adapter Pattern)
1. 定义
适配器模式(Adapter Pattern)是一种结构型设计模式 ,它允许不兼容的接口 之间能够协同工作 。适配器模式通过包装对象的方式,将一个类的接口转换成客户期望的另一个接口。
2. 适用场景
- 需要使用现有的类 ,但其接口与需求不匹配时
- 需要创建可复用的类 ,这些类需要与不相关或不可预见的类协同工作时
- 需要使用多个现有的子类 ,但通过子类化 每个类来适配它们的接口不现实时
- 需要统一多个不同接口时
- 需要逐步迁移到新系统时
3. 结构
plaintext
+-------------------+ +-------------------+
| Client | | Target |
+-------------------+ +-------------------+
| + Request() |<------>| + Request() |
+-------------------+ +-------------------+
^
|
|
+-------------------+ +-------------------+
| Adapter | | Adaptee |
+-------------------+ +-------------------+
| + Request() | | + SpecificRequest()|
| + Adaptee adaptee |------->| |
+-------------------+ +-------------------+
4. 实现步骤
- 定义目标接口(Target)
- 创建适配器类(Adapter)
- 在适配器类中包装被适配者(Adaptee)
- 客户端通过目标接口调用适配器
5. 效果
好处:
- 解耦客户端与被适配者
- 提高代码复用性
- 增加灵活性
- 符合开闭原则
- 实现接口转换
坏处:
- 增加系统复杂性
- 可能影响性能
- 过度使用可能导致设计混乱
- 增加调试难度
6. 总结
适配器模式通过包装对象 的方式,实现了接口转换 ,使得不兼容的接口 能够协同工作。它特别适用于集成现有代码 或统一多个接口 的场景,但可能会增加系统的复杂性。
7. 实际案例 - 电源适配器
以下是一个C#适配器模式实现示例:
csharp
// 目标接口 - 标准电源接口
public interface IStandardPowerSupply
{
void SupplyPower();
}
// 被适配者 - 美标电源
public class AmericanPowerPlug
{
public void SpecificSupply()
{
Console.WriteLine("Supplying 110V power (American standard)");
}
}
// 适配器 - 美标转国标适配器
public class PowerAdapter : IStandardPowerSupply
{
private readonly AmericanPowerPlug _americanPlug;
public PowerAdapter(AmericanPowerPlug americanPlug)
{
_americanPlug = americanPlug;
}
public void SupplyPower()
{
Console.WriteLine("Converting 110V to 220V...");
_americanPlug.SpecificSupply();
Console.WriteLine("Power conversion complete, now supplying 220V");
}
}
// 客户端代码
public class Program
{
public static void Main(string[] args)
{
// 创建美标电源
var americanPlug = new AmericanPowerPlug();
// 创建适配器
var adapter = new PowerAdapter(americanPlug);
// 使用标准接口供电
Console.WriteLine("Using power adapter:");
adapter.SupplyPower();
}
}
代码注解
- IStandardPowerSupply:定义标准电源接口
- AmericanPowerPlug:被适配者,实现美标电源
- PowerAdapter:适配器类,实现标准接口并包装被适配者
- SupplyPower():适配器方法,实现电压转换
- SpecificSupply():被适配者的特定方法
8. 桥接模式(Bridge Pattern)
1. 定义
桥接模式(Bridge Pattern)是一种结构型设计模式 ,它将抽象部分 与实现部分 分离,使它们可以独立变化 。通过使用组合 而不是继承 ,桥接模式可以避免类的爆炸式增长。
2. 适用场景
- 需要避免 在抽象和实现之间建立永久绑定时
- 抽象和实现都需要通过子类化进行扩展时
- 需要对抽象部分的修改不影响客户端时
- 需要在运行时切换实现时
- 需要共享实现时
3. 结构
plaintext
+-------------------+ +-------------------+
| Abstraction | | Implementor |
+-------------------+ +-------------------+
| + Operation() |<------>| + OperationImpl() |
+-------------------+ +-------------------+
^ ^
| |
+-------------------+ +-------------------+
| RefinedAbstraction| | ConcreteImplementor|
+-------------------+ +-------------------+
| + Operation() | | + OperationImpl() |
+-------------------+ +-------------------+
4. 实现步骤
- 定义抽象接口(Abstraction)
- 定义实现接口(Implementor)
- 创建具体实现类(ConcreteImplementor)
- 创建精化抽象类(RefinedAbstraction)
- 客户端通过抽象接口调用实现
5. 效果
好处:
- 分离抽象和实现
- 提高可扩展性
- 实现运行时绑定
- 避免类的爆炸
- 符合开闭原则
坏处:
- 增加系统复杂性
- 可能影响性能
- 增加设计难度
- 需要更多类
6. 总结
桥接模式通过组合 的方式将抽象 与实现 分离,使得它们可以独立变化 。它特别适用于需要避免继承层次过深 的场景,但可能会增加系统的复杂性。
7. 实际案例 - 图形绘制
以下是一个C#桥接模式实现示例:
csharp
// 实现接口 - 绘图API
public interface IDrawingAPI
{
void DrawCircle(double x, double y, double radius);
}
// 具体实现1 - Windows绘图API
public class WindowsDrawingAPI : IDrawingAPI
{
public void DrawCircle(double x, double y, double radius)
{
Console.WriteLine($"Windows API: Drawing circle at ({x},{y}) with radius {radius}");
}
}
// 具体实现2 - Linux绘图API
public class LinuxDrawingAPI : IDrawingAPI
{
public void DrawCircle(double x, double y, double radius)
{
Console.WriteLine($"Linux API: Drawing circle at ({x},{y}) with radius {radius}");
}
}
// 抽象类 - 形状
public abstract class Shape
{
protected IDrawingAPI drawingAPI;
protected Shape(IDrawingAPI drawingAPI)
{
this.drawingAPI = drawingAPI;
}
public abstract void Draw();
public abstract void ResizeByPercentage(double pct);
}
// 精化抽象类 - 圆形
public class Circle : Shape
{
private double x, y, radius;
public Circle(double x, double y, double radius, IDrawingAPI drawingAPI)
: base(drawingAPI)
{
this.x = x;
this.y = y;
this.radius = radius;
}
public override void Draw()
{
drawingAPI.DrawCircle(x, y, radius);
}
public override void ResizeByPercentage(double pct)
{
radius *= pct;
}
}
// 客户端代码
public class Program
{
public static void Main(string[] args)
{
// 创建不同平台的绘图API
IDrawingAPI windowsAPI = new WindowsDrawingAPI();
IDrawingAPI linuxAPI = new LinuxDrawingAPI();
// 创建圆形并使用不同API绘制
var circle1 = new Circle(1, 2, 3, windowsAPI);
var circle2 = new Circle(5, 7, 11, linuxAPI);
Console.WriteLine("Drawing circles:");
circle1.Draw();
circle2.Draw();
// 调整大小并重新绘制
Console.WriteLine("\nResizing and redrawing circles:");
circle1.ResizeByPercentage(2.5);
circle2.ResizeByPercentage(0.75);
circle1.Draw();
circle2.Draw();
}
}
代码注解
- IDrawingAPI:定义绘图接口
- WindowsDrawingAPI:Windows平台的具体实现
- LinuxDrawingAPI:Linux平台的具体实现
- Shape:抽象类,包含绘图API引用
- Circle:精化抽象类,实现具体形状
- Draw():绘制方法,委托给具体实现
- ResizeByPercentage():调整大小方法
9. 装饰模式(Decorator Pattern)★
1. 定义
装饰模式(Decorator Pattern)是一种结构型设计模式 ,它允许动态地 为对象添加功能 ,而不改变 其结构。装饰模式通过包装对象 的方式,提供了比继承 更灵活的功能扩展方式。
2. 适用场景
- 需要动态地添加或撤销对象的功能时
- 需要扩展 类的功能,但使用继承不切实际时
- 需要组合多个功能时
- 需要避免 使用子类进行功能扩展时
- 需要运行时添加功能时
3. 结构
plaintext
+-------------------+ +-------------------+
| Component | | Decorator |
+-------------------+ +-------------------+
| + Operation() |<------>| + Operation() |
+-------------------+ | + Component component|
^ +-------------------+
| ^
| |
+-------------------+ +-------------------+
| ConcreteComponent | | ConcreteDecorator |
+-------------------+ +-------------------+
| + Operation() | | + Operation() |
+-------------------+ | + AddedBehavior() |
+-------------------+
4. 实现步骤
- 定义组件接口(Component)
- 创建具体组件(ConcreteComponent)
- 创建装饰器基类(Decorator)
- 创建具体装饰器(ConcreteDecorator)
- 客户端通过装饰器调用组件
5. 效果
好处:
- 动态添加功能
- 避免继承爆炸
- 符合开闭原则
- 提高灵活性
- 实现功能组合
坏处:
- 增加系统复杂性
- 可能产生大量小对象
- 增加调试难度
- 可能影响性能
6. 总结
装饰模式通过包装对象 的方式,实现了动态功能扩展 。它特别适用于需要运行时添加功能 的场景,但可能会增加系统的复杂性。
7. 实际案例 - 文本格式化
以下是一个C#装饰模式实现示例:
csharp
// 组件接口 - 文本组件
public interface ITextComponent
{
string FormatText(string text);
}
// 具体组件 - 基础文本
public class PlainText : ITextComponent
{
public string FormatText(string text)
{
return text;
}
}
// 装饰器基类
public abstract class TextDecorator : ITextComponent
{
protected ITextComponent textComponent;
protected TextDecorator(ITextComponent textComponent)
{
this.textComponent = textComponent;
}
public virtual string FormatText(string text)
{
return textComponent.FormatText(text);
}
}
// 具体装饰器1 - 加粗
public class BoldTextDecorator : TextDecorator
{
public BoldTextDecorator(ITextComponent textComponent)
: base(textComponent) { }
public override string FormatText(string text)
{
return $"<b>{base.FormatText(text)}</b>";
}
}
// 具体装饰器2 - 斜体
public class ItalicTextDecorator : TextDecorator
{
public ItalicTextDecorator(ITextComponent textComponent)
: base(textComponent) { }
public override string FormatText(string text)
{
return $"<i>{base.FormatText(text)}</i>";
}
}
// 具体装饰器3 - 下划线
public class UnderlineTextDecorator : TextDecorator
{
public UnderlineTextDecorator(ITextComponent textComponent)
: base(textComponent) { }
public override string FormatText(string text)
{
return $"<u>{base.FormatText(text)}</u>";
}
}
// 客户端代码
public class Program
{
public static void Main(string[] args)
{
// 创建基础文本
ITextComponent text = new PlainText();
// 添加装饰
text = new BoldTextDecorator(text);
text = new ItalicTextDecorator(text);
text = new UnderlineTextDecorator(text);
// 格式化文本
string formattedText = text.FormatText("Hello, Decorator Pattern!");
Console.WriteLine(formattedText);
}
}
代码注解
- ITextComponent:定义文本组件接口
- PlainText:具体组件,实现基础文本
- TextDecorator:装饰器基类,包含组件引用
- BoldTextDecorator:具体装饰器,实现加粗功能
- ItalicTextDecorator:具体装饰器,实现斜体功能
- UnderlineTextDecorator:具体装饰器,实现下划线功能
- FormatText():格式化方法,实现装饰逻辑
10. 外观模式(Facade Pattern)★
1. 定义
外观模式为子系统中的一组接口 提供一个统一的入口 。它定义了一个高层接口,使得这一子系统更加容易使用。
2. 适用场景
- 当需要为复杂的子系统提供一个简单接口时
- 在分层结构的系统中,可以使用外观模式定义每层的入口点
- 当客户端与多个子系统之间存在强耦合时,可以使用外观模式解耦
- 需要简化复杂系统的访问方式时
3. 结构
plaintext
+-------------------+ +-------------------+
| Facade | | SubSystemA |
+-------------------+ +-------------------+
| + Operation() |<------>| + OperationA() |
+-------------------+ +-------------------+
^ ^
| |
+-------------------+ +-------------------+
| Client | | SubSystemB |
+-------------------+ +-------------------+
| + OperationB() |
+-------------------+
4. 实现步骤
- 识别需要简化的复杂子系统
- 创建外观类,提供统一的接口
- 在外观类中封装子系统的调用逻辑
- 客户端通过外观类与子系统交互
- 如果需要扩展功能,可以创建抽象外观类
5. 效果
好处:
- 简化接口:为复杂子系统提供简单易用的接口
- 解耦:降低客户端与子系统的耦合度
- 提高灵活性:可以独立修改子系统而不影响客户端
- 提高安全性:隐藏子系统细节,防止客户端误操作
坏处:
- 违反开闭原则:新增功能可能需要修改外观类
- 性能开销:增加了一层调用,可能影响性能
- 过度使用:可能导致系统过于依赖外观类
6. 总结
外观模式通过提供一个统一的接口 ,简化了复杂系统的使用。它特别适用于需要简化客户端调用的场景,但需要注意不要过度使用,以免造成系统过于依赖外观类。
7. 实际案例 - 文件压缩系统
以下是一个C#外观模式实现示例:
csharp
// 子系统 - 文件读取
public class FileReader
{
public string Read(string fileName)
{
Console.WriteLine($"Reading file: {fileName}");
return $"Content of {fileName}";
}
}
// 子系统 - 数据压缩
public class DataCompressor
{
public string Compress(string data)
{
Console.WriteLine("Compressing data...");
return $"Compressed({data})";
}
}
// 子系统 - 文件写入
public class FileWriter
{
public void Write(string fileName, string data)
{
Console.WriteLine($"Writing data to {fileName}");
}
}
// 外观类
public class FileCompressionFacade
{
private FileReader _reader = new FileReader();
private DataCompressor _compressor = new DataCompressor();
private FileWriter _writer = new FileWriter();
public void CompressFile(string sourceFile, string destFile)
{
// 1. 读取文件
string content = _reader.Read(sourceFile);
// 2. 压缩数据
string compressedData = _compressor.Compress(content);
// 3. 写入文件
_writer.Write(destFile, compressedData);
Console.WriteLine("File compression completed!");
}
}
// 客户端代码
public class Program
{
public static void Main(string[] args)
{
FileCompressionFacade facade = new FileCompressionFacade();
// 使用外观类简化文件压缩过程
facade.CompressFile("source.txt", "compressed.zip");
}
}
11. 享元模式(Flyweight Pattern)
1. 定义
享元模式(Flyweight Pattern)是一种结构型设计模式 ,它通过共享 大量细粒度对象来减少内存使用 。享元模式通过将对象的内部状态 和外部状态分离,使得可以在多个对象之间共享相同的内部状态。
2. 适用场景
- 当应用程序需要创建大量相似对象时
- 当对象的大部分状态 可以外部化时
- 当内存使用是一个关键问题时
- 当需要缓存对象以提高性能时
- 当对象的创建成本很高时
3. 结构
plaintext
+-------------------+ +-------------------+
| Flyweight | | FlyweightFactory|
+-------------------+ +-------------------+
| + Operation() |<------>| + GetFlyweight() |
+-------------------+ +-------------------+
^ ^
| |
+-------------------+ +-------------------+
| ConcreteFlyweight | | Client |
+-------------------+ +-------------------+
| + Operation() | | |
+-------------------+ +-------------------+
4. 实现步骤
- 定义享元接口(Flyweight)
- 创建具体享元类(ConcreteFlyweight)
- 创建享元工厂类(FlyweightFactory)
- 客户端通过享元工厂获取享元对象
- 将外部状态传递给享元对象
5. 效果
好处:
- 减少内存使用:通过共享对象减少内存占用
- 提高性能:减少对象创建和销毁的开销
- 提高可扩展性:可以轻松添加新的享元对象
- 符合单一职责原则:将状态分离到不同的类中
坏处:
- 增加复杂性:需要管理内部状态和外部状态
- 可能影响性能:频繁的外部状态传递可能影响性能
- 增加调试难度:共享对象可能导致调试困难
6. 总结
享元模式通过共享 大量细粒度对象来减少内存使用 。它特别适用于需要创建大量相似对象 的场景,但可能会增加系统的复杂性。
7. 实际案例 - 文本编辑器
以下是一个C#享元模式实现示例:
csharp
// 享元接口 - 字符
public interface ICharacter
{
void Display(int pointSize);
}
// 具体享元类 - 字符
public class Character : ICharacter
{
private char _symbol;
private int _pointSize;
public Character(char symbol)
{
_symbol = symbol;
}
public void Display(int pointSize)
{
_pointSize = pointSize;
Console.WriteLine($"Character: {_symbol}, Point Size: {_pointSize}");
}
}
// 享元工厂类
public class CharacterFactory
{
private Dictionary<char, ICharacter> _characters = new Dictionary<char, ICharacter>();
public ICharacter GetCharacter(char key)
{
if (!_characters.ContainsKey(key))
{
_characters[key] = new Character(key);
}
return _characters[key];
}
}
// 客户端代码
public class Program
{
public static void Main(string[] args)
{
string document = "AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ";
int pointSize = 10;
CharacterFactory factory = new CharacterFactory();
foreach (char c in document)
{
pointSize++;
ICharacter character = factory.GetCharacter(c);
character.Display(pointSize);
}
}
}
12. 代理模式(Proxy Pattern)★
1. 定义
代理模式(Proxy Pattern)是一种结构型设计模式 ,它为其他对象提供一种代理 以控制对这个对象的访问。代理模式通过包装对象 的方式,可以在访问对象时添加额外的功能。
2. 适用场景
- 当需要控制对对象的访问时
- 当需要延迟初始化(懒加载)时
- 当需要访问控制(权限检查)时
- 当需要记录日志 或缓存时
- 当需要远程代理(远程方法调用)时
3. 结构
plaintext
+-------------------+ +-------------------+
| Subject | | RealSubject |
+-------------------+ +-------------------+
| + Request() |<------>| + Request() |
+-------------------+ +-------------------+
^ ^
| |
+-------------------+ |
| Proxy | |
+-------------------+ |
| + Request() | |
| + RealSubject subject|------------>|
+-------------------+
4. 实现步骤
- 定义抽象主题接口(Subject)
- 创建真实主题类(RealSubject)
- 创建代理类(Proxy)
- 客户端通过代理类访问真实主题
5. 效果
好处:
- 控制访问:可以在访问对象时添加额外的功能
- 延迟初始化:可以延迟对象的创建直到真正需要时
- 提高性能:可以通过缓存提高性能
- 符合开闭原则:可以轻松添加新的代理类
坏处:
- 增加复杂性:需要管理代理类和真实主题类
- 可能影响性能:代理类的额外处理可能影响性能
- 增加调试难度:代理类可能导致调试困难
6. 总结
代理模式通过包装对象 的方式,可以在访问对象时添加额外的功能 。它特别适用于需要控制对对象的访问 的场景,但可能会增加系统的复杂性。
7. 实际案例 - 图片加载
以下是一个C#代理模式实现示例:
csharp
// 抽象主题接口 - 图片
public interface IImage
{
void Display();
}
// 真实主题类 - 图片
public class RealImage : IImage
{
private string _fileName;
public RealImage(string fileName)
{
_fileName = fileName;
LoadFromDisk();
}
private void LoadFromDisk()
{
Console.WriteLine($"Loading {_fileName}");
}
public void Display()
{
Console.WriteLine($"Displaying {_fileName}");
}
}
// 代理类 - 图片代理
public class ProxyImage : IImage
{
private RealImage _realImage;
private string _fileName;
public ProxyImage(string fileName)
{
_fileName = fileName;
}
public void Display()
{
if (_realImage == null)
{
_realImage = new RealImage(_fileName);
}
_realImage.Display();
}
}
// 客户端代码
public class Program
{
public static void Main(string[] args)
{
IImage image = new ProxyImage("test.jpg");
// 图片将从磁盘加载
image.Display();
// 图片不需要从磁盘加载
image.Display();
}
}
13. 职责链模式(Chain of Responsibility Pattern)
1. 定义
职责链模式(Chain of Responsibility Pattern)是一种行为型设计模式 ,它允许多个对象都有机会处理请求,从而避免 请求的发送者与接收者之间的耦合。这些对象形成一条链,并沿着这条链传递请求,直到有对象处理它为止。
2. 适用场景
- 需要动态指定处理请求的对象时
- 需要避免 请求发送者与接收者之间的耦合时
- 需要灵活地分配职责时
- 需要按顺序处理请求时
- 需要动态添加或删除处理者时
3. 结构
plaintext
+-------------------+ +-------------------+
| Handler | | Client |
+-------------------+ +-------------------+
| + SetSuccessor() |<------>| + HandleRequest() |
| + HandleRequest() | +-------------------+
+-------------------+
^
|
+-------------------+
| ConcreteHandler |
+-------------------+
| + HandleRequest() |
+-------------------+
4. 实现步骤
- 定义Handler接口
- 实现ConcreteHandler类
- 实现**SetSuccessor()**方法
- 实现**HandleRequest()**方法
- 客户端通过**HandleRequest()**方法发送请求
5. 效果
好处:
- 降低 请求发送者与接收者之间的耦合
- 动态添加或删除处理者
- 灵活地分配职责
- 按顺序处理请求
- 简化 对象的职责
坏处:
- 可能降低 系统的性能
- 可能增加 系统的复杂性
- 可能导致 请求的丢失
- 可能增加 调试的难度
6. 总结
职责链模式通过链式结构 处理请求,降低 了请求发送者与接收者之间的耦合 。它灵活 地分配职责,但需要注意 请求的丢失 和系统的性能。
7. 实际案例 - 审批系统
以下是一个C#职责链模式实现示例:
csharp
// 审批者接口
public interface IApprover
{
void SetNextApprover(IApprover nextApprover);
void ProcessRequest(PurchaseRequest request);
}
// 具体审批者1 - 经理
public class Manager : IApprover
{
private IApprover _nextApprover;
public void SetNextApprover(IApprover nextApprover)
{
_nextApprover = nextApprover;
}
public void ProcessRequest(PurchaseRequest request)
{
if (request.Amount <= 1000)
{
Console.WriteLine($"Manager approved request #{request.Number}");
}
else if (_nextApprover != null)
{
_nextApprover.ProcessRequest(request);
}
}
}
// 具体审批者2 - 总监
public class Director : IApprover
{
private IApprover _nextApprover;
public void SetNextApprover(IApprover nextApprover)
{
_nextApprover = nextApprover;
}
public void ProcessRequest(PurchaseRequest request)
{
if (request.Amount <= 5000)
{
Console.WriteLine($"Director approved request #{request.Number}");
}
else if (_nextApprover != null)
{
_nextApprover.ProcessRequest(request);
}
}
}
// 具体审批者3 - 总裁
public class President : IApprover
{
public void SetNextApprover(IApprover nextApprover)
{
// 总裁是最后一个审批者,没有下一个审批者
}
public void ProcessRequest(PurchaseRequest request)
{
if (request.Amount <= 10000)
{
Console.WriteLine($"President approved request #{request.Number}");
}
else
{
Console.WriteLine($"Request #{request.Number} requires an executive meeting!");
}
}
}
// 采购请求
public class PurchaseRequest
{
public int Number { get; set; }
public double Amount { get; set; }
public string Purpose { get; set; }
public PurchaseRequest(int number, double amount, string purpose)
{
Number = number;
Amount = amount;
Purpose = purpose;
}
}
// 客户端代码
public class Program
{
public static void Main(string[] args)
{
// 创建审批者
var manager = new Manager();
var director = new Director();
var president = new President();
// 设置责任链
manager.SetNextApprover(director);
director.SetNextApprover(president);
// 创建采购请求
var requests = new List<PurchaseRequest>
{
new PurchaseRequest(1, 800, "Office supplies"),
new PurchaseRequest(2, 3500, "Conference equipment"),
new PurchaseRequest(3, 8500, "Server upgrade"),
new PurchaseRequest(4, 12000, "Company retreat")
};
// 处理请求
foreach (var request in requests)
{
manager.ProcessRequest(request);
}
}
}
代码注解
- IApprover:定义审批者接口
- Manager:具体审批者,处理金额小于等于1000的请求
- Director:具体审批者,处理金额小于等于5000的请求
- President:具体审批者,处理金额小于等于10000的请求
- SetNextApprover():设置下一个审批者
- ProcessRequest():处理请求
- PurchaseRequest:采购请求类
- Client:客户端代码,创建审批者和请求
14. 命令模式(Command Pattern)
1. 定义
命令模式(Command Pattern)是一种行为型设计模式 ,它将请求封装为对象 ,从而使你可以用不同的请求对客户进行参数化。命令模式支持撤销操作 ,并可以将命令排队 或记录日志。
2. 适用场景
- 需要将请求调用者 与请求执行者解耦时
- 需要支持撤销/重做操作时
- 需要将操作 作为一等公民(如支持事务)时
- 需要支持宏命令(组合命令)时
- 需要支持命令队列 或日志记录时
3. 结构
plaintext
+-------------------+ +-------------------+
| Invoker | | Command |
+-------------------+ +-------------------+
| + SetCommand() |<------>| + Execute() |
| + ExecuteCommand()| | + Undo() |
+-------------------+ +-------------------+
^
|
|
+-------------------+ |
| Receiver | |
+-------------------+ |
| + Action() |<---------------+
+-------------------+
4. 实现步骤
- 定义命令接口(Command)
- 创建具体命令类(ConcreteCommand)
- 创建接收者类(Receiver)
- 创建调用者类(Invoker)
- 客户端通过调用者执行命令
5. 效果
好处:
- 解耦调用者与执行者
- 支持撤销/重做操作
- 支持组合命令
- 支持命令队列
- 符合开闭原则
坏处:
- 增加系统复杂性
- 可能产生大量命令类
- 增加调试难度
- 可能影响性能
6. 总结
命令模式通过将请求封装为对象 ,实现了调用者与执行者的解耦。它特别适用于需要支持撤销/重做 或命令队列 的场景,但可能会增加系统的复杂性。
7. 实际案例 - 文本编辑器
以下是一个C#命令模式实现示例:
csharp
// 接收者 - 文档
public class Document
{
public string Content { get; private set; } = string.Empty;
public void Write(string text)
{
Content += text;
}
public void Erase(int length)
{
if (length > Content.Length)
length = Content.Length;
Content = Content.Substring(0, Content.Length - length);
}
public void Print()
{
Console.WriteLine($"Document Content: {Content}");
}
}
// 命令接口
public interface ICommand
{
void Execute();
void Undo();
}
// 具体命令 - 写入命令
public class WriteCommand : ICommand
{
private readonly Document _document;
private readonly string _text;
public WriteCommand(Document document, string text)
{
_document = document;
_text = text;
}
public void Execute()
{
_document.Write(_text);
}
public void Undo()
{
_document.Erase(_text.Length);
}
}
// 具体命令 - 删除命令
public class EraseCommand : ICommand
{
private readonly Document _document;
private readonly int _length;
private string _erasedText;
public EraseCommand(Document document, int length)
{
_document = document;
_length = length;
}
public void Execute()
{
_erasedText = _document.Content.Substring(_document.Content.Length - _length);
_document.Erase(_length);
}
public void Undo()
{
_document.Write(_erasedText);
}
}
// 调用者 - 编辑器
public class Editor
{
private readonly Stack<ICommand> _commandHistory = new();
public void ExecuteCommand(ICommand command)
{
command.Execute();
_commandHistory.Push(command);
}
public void UndoLastCommand()
{
if (_commandHistory.Count > 0)
{
var lastCommand = _commandHistory.Pop();
lastCommand.Undo();
}
}
}
// 客户端代码
public class Program
{
public static void Main(string[] args)
{
var document = new Document();
var editor = new Editor();
// 执行命令
editor.ExecuteCommand(new WriteCommand(document, "Hello "));
editor.ExecuteCommand(new WriteCommand(document, "World!"));
document.Print();
// 撤销命令
editor.UndoLastCommand();
document.Print();
// 执行删除命令
editor.ExecuteCommand(new EraseCommand(document, 3));
document.Print();
// 撤销删除
editor.UndoLastCommand();
document.Print();
}
}
15. 解释器模式(Interpreter Pattern)
1. 定义
解释器模式(Interpreter Pattern)是一种行为型设计模式 ,它定义了一个语言的语法表示 ,并使用一个解释器 来解释语言中的句子。解释器模式通常用于解析 和执行特定领域的语言。
2. 适用场景
- 当需要解释 一种特定领域语言时
- 当语言的语法相对简单时
- 当效率不是关键问题时
- 当需要扩展语言时
- 当需要解析表达式时
3. 结构
plaintext
+-------------------+ +-------------------+
| Context | | AbstractExpression|
+-------------------+ +-------------------+
| |<------>| + Interpret() |
+-------------------+ +-------------------+
^
|
|
+-------------------+ |
| TerminalExpression| |
+-------------------+ |
| + Interpret() | |
+-------------------+ |
|
+----------------------+ |
| NonterminalExpression| |
+----------------------+ |
| + Interpret() |-------------|
+----------------------+
4. 实现步骤
- 定义抽象表达式接口(AbstractExpression)
- 创建终结符表达式类(TerminalExpression)
- 创建非终结符表达式类(NonterminalExpression)
- 创建上下文类(Context)
- 客户端通过解释器解释表达式
5. 效果
好处:
- 易于扩展语法
- 易于实现简单语法
- 符合单一职责原则
- 符合开闭原则
- 灵活的语法表示
坏处:
- 效率较低
- 难以维护复杂语法
- 增加系统复杂性
- 难以调试
6. 总结
解释器模式通过定义语言的语法表示 ,并使用解释器 来解释语言中的句子。它特别适用于解析简单语法 的场景,但对于复杂语法可能效率较低。
7. 实际案例 - 布尔表达式解释器
以下是一个C#解释器模式实现示例:
csharp
// 抽象表达式接口
public interface IExpression
{
bool Interpret(Dictionary<string, bool> context);
}
// 终结符表达式 - 变量
public class VariableExpression : IExpression
{
private readonly string _name;
public VariableExpression(string name)
{
_name = name;
}
public bool Interpret(Dictionary<string, bool> context)
{
return context[_name];
}
}
// 非终结符表达式 - 与运算
public class AndExpression : IExpression
{
private readonly IExpression _expr1;
private readonly IExpression _expr2;
public AndExpression(IExpression expr1, IExpression expr2)
{
_expr1 = expr1;
_expr2 = expr2;
}
public bool Interpret(Dictionary<string, bool> context)
{
return _expr1.Interpret(context) && _expr2.Interpret(context);
}
}
// 非终结符表达式 - 或运算
public class OrExpression : IExpression
{
private readonly IExpression _expr1;
private readonly IExpression _expr2;
public OrExpression(IExpression expr1, IExpression expr2)
{
_expr1 = expr1;
_expr2 = expr2;
}
public bool Interpret(Dictionary<string, bool> context)
{
return _expr1.Interpret(context) || _expr2.Interpret(context);
}
}
// 非终结符表达式 - 非运算
public class NotExpression : IExpression
{
private readonly IExpression _expr;
public NotExpression(IExpression expr)
{
_expr = expr;
}
public bool Interpret(Dictionary<string, bool> context)
{
return !_expr.Interpret(context);
}
}
// 客户端代码
public class Program
{
public static void Main(string[] args)
{
// 创建上下文
var context = new Dictionary<string, bool>
{
{ "A", true },
{ "B", false },
{ "C", true }
};
// 创建表达式:A AND (B OR C)
var expression = new AndExpression(
new VariableExpression("A"),
new OrExpression(
new VariableExpression("B"),
new VariableExpression("C")
)
);
// 解释表达式
bool result = expression.Interpret(context);
Console.WriteLine($"A AND (B OR C) = {result}");
// 创建表达式:NOT A OR (B AND C)
var expression2 = new OrExpression(
new NotExpression(new VariableExpression("A")),
new AndExpression(
new VariableExpression("B"),
new VariableExpression("C")
)
);
// 解释表达式
bool result2 = expression2.Interpret(context);
Console.WriteLine($"NOT A OR (B AND C) = {result2}");
}
}
16. ### 迭代器模式详解
1. 定义
迭代器模式(Iterator Pattern)是一种行为型设计模式 ,它提供一种方法顺序访问 一个聚合对象中的各个元素,而又不暴露 该对象的内部表示 。迭代器模式将遍历 和聚合 分离,使得聚合对象可以独立于遍历算法。
2. 适用场景
- 需要统一 遍历不同的聚合结构时
- 需要隐藏 聚合对象的内部结构时
- 需要支持多种遍历方式时
- 需要简化 聚合对象的接口时
- 需要并行遍历多个聚合对象时
3. 结构
plaintext
+-------------------+ +-------------------+
| Aggregate | | Iterator |
+-------------------+ +-------------------+
| + CreateIterator()|<------>| + First() |
+-------------------+ | + Next() |
^ | + IsDone() |
| | + CurrentItem() |
+-------------------+ +-------------------+
| ConcreteAggregate | | ConcreteIterator |
+-------------------+ +-------------------+
| + CreateIterator()| | + First() |
+-------------------+ | + Next() |
| + IsDone() |
| + CurrentItem() |
+-------------------+
4. 实现步骤
- 定义Iterator接口
- 定义Aggregate接口
- 实现ConcreteIterator类
- 实现ConcreteAggregate类
- 客户端通过Iterator 遍历Aggregate
5. 效果
好处:
- 简化 聚合对象的接口
- 支持多种遍历方式
- 隐藏 聚合对象的内部结构
- 统一 遍历不同的聚合结构
- 支持并行遍历
坏处:
- 可能增加 系统的复杂性
- 可能降低 系统的性能
- 可能增加 调试的难度
- 可能导致 代码的冗余
6. 总结
迭代器模式通过分离 遍历和聚合,简化 了聚合对象的接口 。它支持 多种遍历方式,但需要注意 系统的性能 和复杂性。
7. 实际案例 - 图书管理系统
以下是一个C#迭代器模式实现示例:
csharp
// 迭代器接口
public interface IIterator<T>
{
T First();
T Next();
bool IsDone();
T CurrentItem();
}
// 聚合接口
public interface IAggregate<T>
{
IIterator<T> CreateIterator();
}
// 具体聚合 - 图书集合
public class BookCollection : IAggregate<Book>
{
private List<Book> _books = new List<Book>();
public void Add(Book book)
{
_books.Add(book);
}
public int Count => _books.Count;
public Book this[int index]
{
get => _books[index];
set => _books[index] = value;
}
public IIterator<Book> CreateIterator()
{
return new BookIterator(this);
}
}
// 具体迭代器 - 图书迭代器
public class BookIterator : IIterator<Book>
{
private BookCollection _collection;
private int _current = 0;
public BookIterator(BookCollection collection)
{
_collection = collection;
}
public Book First()
{
_current = 0;
return _collection[_current];
}
public Book Next()
{
_current++;
if (!IsDone())
{
return _collection[_current];
}
return null;
}
public bool IsDone()
{
return _current >= _collection.Count;
}
public Book CurrentItem()
{
return _collection[_current];
}
}
// 图书类
public class Book
{
public string Title { get; set; }
public string Author { get; set; }
public int Year { get; set; }
public Book(string title, string author, int year)
{
Title = title;
Author = author;
Year = year;
}
public override string ToString()
{
return $"{Title} by {Author} ({Year})";
}
}
// 客户端代码
public class Program
{
public static void Main(string[] args)
{
// 创建图书集合
var collection = new BookCollection();
collection.Add(new Book("Design Patterns", "Erich Gamma", 1994));
collection.Add(new Book("Clean Code", "Robert C. Martin", 2008));
collection.Add(new Book("Refactoring", "Martin Fowler", 1999));
// 创建迭代器
var iterator = collection.CreateIterator();
// 遍历图书集合
Console.WriteLine("Books in collection:");
for (var book = iterator.First(); !iterator.IsDone(); book = iterator.Next())
{
Console.WriteLine(book);
}
}
}
代码注解
- IIterator:定义迭代器接口
- IAggregate:定义聚合接口
- BookCollection:具体聚合,实现图书集合
- BookIterator:具体迭代器,实现图书迭代器
- Book:图书类
- CreateIterator():创建迭代器方法
- First():获取第一个元素
- Next():获取下一个元素
- IsDone():判断是否遍历完成
- CurrentItem():获取当前元素
17. 中介者模式
1. 定义
中介者模式(Mediator Pattern)是一种行为型设计模式 ,它封装 了一组对象之间的交互 方式,使得这些对象不需要显式地相互引用 。中介者模式通过集中控制 对象之间的通信 ,来降低 对象之间的耦合度。
2. 适用场景
- 系统中对象之间存在复杂的引用关系时
- 需要简化 对象之间的交互时
- 需要集中控制 对象之间的通信时
- 需要动态 改变对象之间的交互方式时
- 需要限制 对象之间的直接通信时
3. 结构
plaintext
+-------------------+ +-------------------+
| Mediator | | Colleague |
+-------------------+ +-------------------+
| + Notify() |<------>| + Send() |
+-------------------+ | + Receive() |
^ +-------------------+
| ^
+-------------------+ |
| ConcreteMediator | |
+-------------------+ |
| + Notify() | |
+-------------------+ |
| |
+-------------------+ +-------------------+
| ConcreteColleague | | ConcreteColleague |
+-------------------+ +-------------------+
| + Send() | | + Send() |
| + Receive() | | + Receive() |
+-------------------+ +-------------------+
4. 实现步骤
- 定义Mediator接口
- 定义Colleague接口
- 实现ConcreteMediator类
- 实现ConcreteColleague类
- 客户端通过Mediator 进行对象之间的通信
5. 效果
好处:
- 降低 对象之间的耦合度
- 简化 对象之间的交互
- 集中控制 对象之间的通信
- 支持 对象之间的松耦合
- 支持 对象之间的动态交互
坏处:
- 可能增加 系统的复杂性
- 可能降低 系统的性能
- 可能增加 调试的难度
- 可能导致 中介者的过度复杂
6. 总结
中介者模式通过集中控制 对象之间的通信 ,降低 了对象之间的耦合度 。它简化 了对象之间的交互 ,但需要注意 中介者的复杂性。
7. 实际案例 - 聊天室系统
以下是一个C#中介者模式实现示例:
csharp
// 中介者接口
public interface IChatRoomMediator
{
void SendMessage(string message, User user);
void AddUser(User user);
}
// 具体中介者 - 聊天室
public class ChatRoom : IChatRoomMediator
{
private List<User> _users = new List<User>();
public void AddUser(User user)
{
_users.Add(user);
}
public void SendMessage(string message, User user)
{
foreach (var u in _users)
{
// 不发送消息给自己
if (u != user)
{
u.Receive(message);
}
}
}
}
// 同事类 - 用户
public abstract class User
{
protected IChatRoomMediator _mediator;
public string Name { get; }
public User(string name, IChatRoomMediator mediator)
{
Name = name;
_mediator = mediator;
}
public abstract void Send(string message);
public abstract void Receive(string message);
}
// 具体同事类 - 聊天用户
public class ChatUser : User
{
public ChatUser(string name, IChatRoomMediator mediator)
: base(name, mediator) { }
public override void Send(string message)
{
Console.WriteLine($"{Name} sends: {message}");
_mediator.SendMessage(message, this);
}
public override void Receive(string message)
{
Console.WriteLine($"{Name} receives: {message}");
}
}
// 客户端代码
public class Program
{
public static void Main(string[] args)
{
// 创建中介者
var chatRoom = new ChatRoom();
// 创建用户
var john = new ChatUser("John", chatRoom);
var jane = new ChatUser("Jane", chatRoom);
var bob = new ChatUser("Bob", chatRoom);
// 添加用户到聊天室
chatRoom.AddUser(john);
chatRoom.AddUser(jane);
chatRoom.AddUser(bob);
// 用户发送消息
john.Send("Hi everyone!");
jane.Send("Hello John!");
bob.Send("Hey guys, what's up?");
}
}
代码注解
- IChatRoomMediator:定义中介者接口
- ChatRoom:具体中介者,实现聊天室
- User:同事类,定义用户
- ChatUser:具体同事类,实现聊天用户
- Send():发送消息方法
- Receive():接收消息方法
- AddUser():添加用户方法
- SendMessage():发送消息给所有用户方法
18. 备忘录模式#
1. 定义
备忘录模式(Memento Pattern)是一种行为型设计模式 ,它允许 在不破坏封装性的前提下,捕获 并保存 一个对象的内部状态 ,以便在需要时恢复该状态。
2. 适用场景
- 需要保存 和恢复 对象的历史状态时
- 需要实现撤销/重做功能时
- 需要保护 对象的封装性时
- 需要隔离 状态保存 和恢复 的细节时
- 需要支持多级撤销操作时
3. 结构
plaintext
+-------------------+ +-------------------+ +-------------------+
| Originator | | Memento | | Caretaker |
+-------------------+ +-------------------+ +-------------------+
| + State | | + State | | + Memento |
| + CreateMemento() |<------>| + GetState() |<------>| + SaveMemento() |
| + SetMemento() | +-------------------+ | + RestoreMemento()|
+-------------------+ +-------------------+
4. 实现步骤
- 定义Originator类
- 定义Memento类
- 定义Caretaker类
- Originator 创建Memento
- Caretaker 保存和恢复Memento
5. 效果
好处:
- 保护 对象的封装性
- 简化 状态保存 和恢复 的实现
- 支持撤销/重做功能
- 支持多级撤销操作
- 隔离 状态保存 和恢复 的细节
坏处:
- 可能增加 系统的复杂性
- 可能消耗 大量内存
- 可能降低 系统的性能
- 可能增加 调试的难度
- 可能导致 状态保存 和恢复 的不一致
6. 总结
备忘录模式通过保存 和恢复 对象的内部状态 ,支持 撤销/重做功能。它保护 了对象的封装性 ,但需要注意 内存消耗 和性能问题。
7. 实际案例 - 文本编辑器
以下是一个C#备忘录模式实现示例:
csharp
// 备忘录类
public class TextMemento
{
public string Text { get; }
public TextMemento(string text)
{
Text = text;
}
}
// 原发器类 - 文本编辑器
public class TextEditor
{
private string _text;
public void SetText(string text)
{
_text = text;
}
public string GetText()
{
return _text;
}
// 创建备忘录
public TextMemento CreateMemento()
{
return new TextMemento(_text);
}
// 恢复备忘录
public void SetMemento(TextMemento memento)
{
_text = memento.Text;
}
}
// 管理者类
public class TextEditorHistory
{
private Stack<TextMemento> _history = new Stack<TextMemento>();
public void Save(TextEditor editor)
{
_history.Push(editor.CreateMemento());
}
public void Undo(TextEditor editor)
{
if (_history.Count > 0)
{
editor.SetMemento(_history.Pop());
}
}
}
// 客户端代码
public class Program
{
public static void Main(string[] args)
{
var editor = new TextEditor();
var history = new TextEditorHistory();
// 编辑文本
editor.SetText("First version");
history.Save(editor);
editor.SetText("Second version");
history.Save(editor);
editor.SetText("Third version");
// 显示当前文本
Console.WriteLine("Current text: " + editor.GetText());
// 撤销
history.Undo(editor);
Console.WriteLine("After undo: " + editor.GetText());
// 再次撤销
history.Undo(editor);
Console.WriteLine("After second undo: " + editor.GetText());
}
}
代码注解
- TextMemento:备忘录类,保存文本状态
- TextEditor:原发器类,实现文本编辑功能
- TextEditorHistory:管理者类,保存和恢复备忘录
- CreateMemento():创建备忘录方法
- SetMemento():恢复备忘录方法
- Save():保存备忘录方法
- Undo():撤销操作方法
- SetText():设置文本方法
- GetText():获取文本方法
19. 观察者模式### 观察者模式详解
1. 定义
观察者模式(Observer Pattern)是一种行为型设计模式 ,它定义了对象之间 的一对多依赖关系 ,使得当一个对象改变状态 时,所有依赖它的对象 都会自动收到通知 并更新。
2. 适用场景
- 需要维护 对象之间的一致性时
- 需要实现 事件处理系统时
- 需要支持 广播通信时
- 需要解耦 对象之间的依赖关系时
- 需要实现 发布-订阅机制时
3. 结构
plaintext
+-------------------+ +-------------------+
| Subject | | Observer |
+-------------------+ +-------------------+
| + Attach() |<------>| + Update() |
| + Detach() | +-------------------+
| + Notify() | ^
+-------------------+ |
^ |
+-------------------+ +-------------------+
| ConcreteSubject | | ConcreteObserver |
+-------------------+ +-------------------+
| + State | | + Update() |
| + GetState() | +-------------------+
| + SetState() |
+-------------------+
4. 实现步骤
- 定义Subject接口
- 定义Observer接口
- 实现ConcreteSubject类
- 实现ConcreteObserver类
- Subject 维护Observer列表
- Subject 状态改变时通知 所有Observer
5. 效果
好处:
- 支持 对象之间的松耦合
- 支持 广播通信
- 支持 动态添加 和删除观察者
- 支持 对象之间的一致性
- 支持 发布-订阅机制
坏处:
- 可能增加 系统的复杂性
- 可能降低 系统的性能
- 可能导致 内存泄漏
- 可能增加 调试的难度
- 可能导致 通知顺序 的不确定性
6. 总结
观察者模式通过定义 对象之间的一对多依赖关系 ,支持 对象之间的松耦合 。它支持 广播通信 ,但需要注意 性能问题 和通知顺序。
7. 实际案例 - 股票价格通知系统
以下是一个C#观察者模式实现示例:
csharp
// 观察者接口
public interface IStockObserver
{
void Update(string stockSymbol, decimal price);
}
// 具体观察者 - 投资者
public class Investor : IStockObserver
{
public string Name { get; }
public Investor(string name)
{
Name = name;
}
public void Update(string stockSymbol, decimal price)
{
Console.WriteLine($"{Name} received update: {stockSymbol} is now at {price:C}");
}
}
// 主题接口
public interface IStockSubject
{
void Attach(IStockObserver observer);
void Detach(IStockObserver observer);
void Notify();
}
// 具体主题 - 股票
public class Stock : IStockSubject
{
private string _symbol;
private decimal _price;
private List<IStockObserver> _observers = new List<IStockObserver>();
public Stock(string symbol, decimal price)
{
_symbol = symbol;
_price = price;
}
public decimal Price
{
get => _price;
set
{
if (_price != value)
{
_price = value;
Notify();
}
}
}
public void Attach(IStockObserver observer)
{
_observers.Add(observer);
}
public void Detach(IStockObserver observer)
{
_observers.Remove(observer);
}
public void Notify()
{
foreach (var observer in _observers)
{
observer.Update(_symbol, _price);
}
}
}
// 客户端代码
public class Program
{
public static void Main(string[] args)
{
// 创建股票
var appleStock = new Stock("AAPL", 150.00m);
// 创建投资者
var john = new Investor("John");
var jane = new Investor("Jane");
// 订阅股票
appleStock.Attach(john);
appleStock.Attach(jane);
// 更新股票价格
appleStock.Price = 155.00m;
appleStock.Price = 160.00m;
// John取消订阅
appleStock.Detach(john);
// 再次更新股票价格
appleStock.Price = 165.00m;
}
}
代码注解
- IStockObserver:观察者接口,定义更新方法
- Investor:具体观察者,实现投资者
- IStockSubject:主题接口,定义附加、分离和通知方法
- Stock:具体主题,实现股票
- Attach():附加观察者方法
- Detach():分离观察者方法
- Notify():通知观察者方法
- Update():更新观察者方法
- Price:股票价格属性
20. 状态模式### 状态模式详解
1. 定义
状态模式(State Pattern)是一种行为型设计模式 ,它允许 一个对象在内部状态改变 时改变 它的行为 ,使得对象看起来 像是修改 了它的类。
2. 适用场景
- 一个对象的行为 取决于它的状态 ,并且它必须在运行时 根据状态改变 它的行为
- 需要消除 大量的条件语句时
- 需要封装 与特定状态 相关的行为时
- 需要实现 状态转换时
- 需要支持 多态行为时
3. 结构
plaintext
+-------------------+ +-------------------+
| Context | | State |
+-------------------+ +-------------------+
| + State |<------>| + Handle() |
| + Request() | +-------------------+
+-------------------+ ^
^ |
+-------------------+ +-------------------+
| ConcreteContext | | ConcreteStateA |
+-------------------+ +-------------------+
| + Request() | | + Handle() |
+-------------------+ +-------------------+
^
+-------------------+ |
| ConcreteStateB | |
+-------------------+ |
| + Handle() | |
+-------------------+------------+
4. 实现步骤
- 定义State接口
- 定义ConcreteState类
- 定义Context类
- Context 维护State引用
- Context 委托State处理请求
- State 处理请求并可能转换状态
5. 效果
好处:
- 封装 了与特定状态 相关的行为
- 消除 了大量的条件语句
- 支持 状态转换
- 支持 多态行为
- 简化 了Context 类的实现
坏处:
- 可能增加 系统的复杂性
- 可能增加 类的数量
- 可能降低 系统的性能
- 可能增加 调试的难度
- 可能导致 状态转换 的不确定性
6. 总结
状态模式通过封装 与特定状态 相关的行为 ,支持 状态转换 。它消除 了大量的条件语句 ,但需要注意 类的数量 和性能问题。
7. 实际案例 - 订单状态系统
以下是一个C#状态模式实现示例:
csharp
// 状态接口
public interface IOrderState
{
void Handle(OrderContext context);
}
// 具体状态 - 新建订单
public class NewOrderState : IOrderState
{
public void Handle(OrderContext context)
{
Console.WriteLine("Order is in NEW state. Processing...");
context.State = new ProcessingOrderState();
}
}
// 具体状态 - 处理中订单
public class ProcessingOrderState : IOrderState
{
public void Handle(OrderContext context)
{
Console.WriteLine("Order is in PROCESSING state. Shipping...");
context.State = new ShippedOrderState();
}
}
// 具体状态 - 已发货订单
public class ShippedOrderState : IOrderState
{
public void Handle(OrderContext context)
{
Console.WriteLine("Order is in SHIPPED state. Delivering...");
context.State = new DeliveredOrderState();
}
}
// 具体状态 - 已交付订单
public class DeliveredOrderState : IOrderState
{
public void Handle(OrderContext context)
{
Console.WriteLine("Order is in DELIVERED state. Completed.");
context.State = null;
}
}
// 上下文类
public class OrderContext
{
public IOrderState State { get; set; }
public OrderContext()
{
State = new NewOrderState();
}
public void Process()
{
State?.Handle(this);
}
}
// 客户端代码
public class Program
{
public static void Main(string[] args)
{
var order = new OrderContext();
// 处理订单
order.Process(); // NEW -> PROCESSING
order.Process(); // PROCESSING -> SHIPPED
order.Process(); // SHIPPED -> DELIVERED
order.Process(); // DELIVERED -> Completed
}
}
代码注解
- IOrderState:状态接口,定义处理方法
- NewOrderState:具体状态,实现新建订单状态
- ProcessingOrderState:具体状态,实现处理中订单状态
- ShippedOrderState:具体状态,实现已发货订单状态
- DeliveredOrderState:具体状态,实现已交付订单状态
- OrderContext:上下文类,维护状态引用
- State:状态属性
- Process():处理方法
- Handle():状态处理方法
21. 策略模式
1. 定义
策略模式(Strategy Pattern)是一种行为型设计模式 ,它定义 了一系列算法 ,并将每个算法封装 起来,使它们可以互相替换 。策略模式让算法的变化 独立于使用它的客户。
2. 适用场景
- 一个系统需要动态 地在几种算法中选择一种时
- 需要消除 大量的条件语句时
- 需要封装 与算法 相关的行为时
- 需要支持 算法的扩展时
- 需要支持 算法的复用时
3. 结构
plaintext
+-------------------+ +-------------------+
| Context | | Strategy |
+-------------------+ +-------------------+
| + Strategy |<------>| + Execute() |
| + Execute() | +-------------------+
+-------------------+ ^
^ |
+-------------------+ +-------------------+
| ConcreteContext | | ConcreteStrategyA |
+-------------------+ +-------------------+
| + Execute() | | + Execute() |
+-------------------+ +-------------------+
^
+-------------------+ |
| ConcreteStrategyB | |
+-------------------+ |
| + Execute() | |
+-------------------+------------+
4. 实现步骤
- 定义Strategy接口
- 定义ConcreteStrategy类
- 定义Context类
- Context 维护Strategy引用
- Context 委托Strategy执行算法
- Strategy执行算法
5. 效果
好处:
- 封装 了与算法 相关的行为
- 消除 了大量的条件语句
- 支持 算法的扩展
- 支持 算法的复用
- 简化 了Context 类的实现
坏处:
- 可能增加 系统的复杂性
- 可能增加 类的数量
- 可能降低 系统的性能
- 可能增加 调试的难度
- 可能导致 算法的不确定性
6. 总结
策略模式通过封装 与算法 相关的行为 ,支持 算法的扩展 和复用 。它消除 了大量的条件语句 ,但需要注意 类的数量 和性能问题。
7. 实际案例 - 支付系统
以下是一个C#策略模式实现示例:
csharp
// 策略接口
public interface IPaymentStrategy
{
void Pay(decimal amount);
}
// 具体策略 - 信用卡支付
public class CreditCardPayment : IPaymentStrategy
{
public void Pay(decimal amount)
{
Console.WriteLine($"Paid {amount} via Credit Card.");
}
}
// 具体策略 - 支付宝支付
public class AlipayPayment : IPaymentStrategy
{
public void Pay(decimal amount)
{
Console.WriteLine($"Paid {amount} via Alipay.");
}
}
// 具体策略 - 微信支付
public class WechatPayment : IPaymentStrategy
{
public void Pay(decimal amount)
{
Console.WriteLine($"Paid {amount} via Wechat.");
}
}
// 上下文类
public class PaymentContext
{
private IPaymentStrategy _strategy;
public PaymentContext(IPaymentStrategy strategy)
{
_strategy = strategy;
}
public void SetStrategy(IPaymentStrategy strategy)
{
_strategy = strategy;
}
public void ExecutePayment(decimal amount)
{
_strategy.Pay(amount);
}
}
// 客户端代码
public class Program
{
public static void Main(string[] args)
{
var context = new PaymentContext(new CreditCardPayment());
context.ExecutePayment(100.00m); // 使用信用卡支付
context.SetStrategy(new AlipayPayment());
context.ExecutePayment(200.00m); // 使用支付宝支付
context.SetStrategy(new WechatPayment());
context.ExecutePayment(300.00m); // 使用微信支付
}
}
代码注解
- IPaymentStrategy:策略接口,定义支付方法
- CreditCardPayment:具体策略,实现信用卡支付
- AlipayPayment:具体策略,实现支付宝支付
- WechatPayment:具体策略,实现微信支付
- PaymentContext:上下文类,维护策略引用
- _strategy:策略属性
- SetStrategy():设置策略方法
- ExecutePayment():执行支付方法
- Pay():支付方法
22. 模版方法模式
1. 定义
模板方法模式(Template Method Pattern)是一种行为型设计模式 ,它定义 了一个算法的骨架 ,并允许 子类为算法的某些步骤 提供实现 。模板方法模式让子类重新定义 算法的某些步骤 ,而不改变 算法的结构。
2. 适用场景
- 需要固定 算法的结构 ,但允许 算法的某些步骤变化时
- 需要消除 大量的重复代码时
- 需要封装 与算法 相关的行为时
- 需要支持 算法的扩展时
- 需要支持 算法的复用时
3. 结构
plaintext
+-------------------+ +-------------------+
| AbstractClass | | ConcreteClass |
+-------------------+ +-------------------+
| + TemplateMethod()| | + PrimitiveOp1() |
| + PrimitiveOp1() |<------>| + PrimitiveOp2() |
| + PrimitiveOp2() | +-------------------+
+-------------------+ ^
^ |
+-------------------+ +-------------------+
| ConcreteClassA | | ConcreteClassB |
+-------------------+ +-------------------+
| + PrimitiveOp1() | | + PrimitiveOp1() |
| + PrimitiveOp2() | | + PrimitiveOp2() |
+-------------------+ +-------------------+
4. 实现步骤
- 定义AbstractClass
- 定义TemplateMethod
- 定义PrimitiveOp1 和PrimitiveOp2
- 定义ConcreteClass
- ConcreteClass 实现PrimitiveOp1 和PrimitiveOp2
- AbstractClass 调用TemplateMethod
5. 效果
好处:
- 封装 了与算法 相关的行为
- 消除 了大量的重复代码
- 支持 算法的扩展
- 支持 算法的复用
- 简化 了AbstractClass 类的实现
坏处:
- 可能增加 系统的复杂性
- 可能增加 类的数量
- 可能降低 系统的性能
- 可能增加 调试的难度
- 可能导致 算法的不确定性
6. 总结
模板方法模式通过封装 与算法 相关的行为 ,支持 算法的扩展 和复用 。它消除 了大量的重复代码 ,但需要注意 类的数量 和性能问题。
7. 实际案例 - 数据导出系统
以下是一个C#模板方法模式实现示例:
csharp
// 抽象类
public abstract class DataExporter
{
// 模板方法
public void Export()
{
ReadData();
ProcessData();
WriteData();
}
// 基本操作 - 读取数据
protected abstract void ReadData();
// 基本操作 - 处理数据
protected abstract void ProcessData();
// 基本操作 - 写入数据
protected virtual void WriteData()
{
Console.WriteLine("Writing data to file...");
}
}
// 具体类 - Excel导出
public class ExcelExporter : DataExporter
{
protected override void ReadData()
{
Console.WriteLine("Reading data from database for Excel...");
}
protected override void ProcessData()
{
Console.WriteLine("Processing data for Excel...");
}
protected override void WriteData()
{
base.WriteData();
Console.WriteLine("Writing data to Excel file...");
}
}
// 具体类 - CSV导出
public class CsvExporter : DataExporter
{
protected override void ReadData()
{
Console.WriteLine("Reading data from database for CSV...");
}
protected override void ProcessData()
{
Console.WriteLine("Processing data for CSV...");
}
protected override void WriteData()
{
base.WriteData();
Console.WriteLine("Writing data to CSV file...");
}
}
// 客户端代码
public class Program
{
public static void Main(string[] args)
{
DataExporter exporter = new ExcelExporter();
exporter.Export(); // 导出Excel
exporter = new CsvExporter();
exporter.Export(); // 导出CSV
}
}
代码注解
- DataExporter:抽象类,定义模板方法
- Export():模板方法
- ReadData():基本操作,读取数据
- ProcessData():基本操作,处理数据
- WriteData():基本操作,写入数据
- ExcelExporter:具体类,实现Excel导出
- CsvExporter:具体类,实现CSV导出
- ReadData():具体操作,读取数据
- ProcessData():具体操作,处理数据
- WriteData():具体操作,写入数据
23. 访问者模式
1. 定义
访问者模式(Visitor Pattern)是一种行为型设计模式 ,它定义 了一个操作 ,该操作作用于 一组对象 ,并允许 在不改变 这些对象的类 的情况下定义 新的操作 。访问者模式将数据结构 与数据操作分离。
2. 适用场景
- 一个对象结构 包含多个类 ,且需要执行 不同的操作时
- 需要添加 新的操作 ,但不改变 对象的类时
- 需要封装 与对象结构 相关的行为时
- 需要支持 操作的扩展时
- 需要支持 操作的复用时
3. 结构
plaintext
+-------------------+ +-------------------+
| Visitor | | Element |
+-------------------+ +-------------------+
| + VisitA() |<------>| + Accept() |
| + VisitB() | +-------------------+
+-------------------+ ^
^ |
+-------------------+ +-------------------+
| ConcreteVisitor | | ConcreteElementA |
+-------------------+ +-------------------+
| + VisitA() | | + Accept() |
| + VisitB() | | + OperationA() |
+-------------------+ +-------------------+
^
+-------------------+ |
| ConcreteElementB | |
+-------------------+ |
| + Accept() | |
| + OperationB() | |
+-------------------+------------+
4. 实现步骤
- 定义Visitor接口
- 定义ConcreteVisitor类
- 定义Element接口
- 定义ConcreteElement类
- Element 维护Visitor引用
- Element 委托Visitor执行操作
- Visitor执行操作
5. 效果
好处:
- 封装 了与对象结构 相关的行为
- 消除 了大量的条件语句
- 支持 操作的扩展
- 支持 操作的复用
- 简化 了Element 类的实现
坏处:
- 可能增加 系统的复杂性
- 可能增加 类的数量
- 可能降低 系统的性能
- 可能增加 调试的难度
- 可能导致 操作的不确定性
6. 总结
访问者模式通过封装 与对象结构 相关的行为 ,支持 操作的扩展 和复用 。它消除 了大量的条件语句 ,但需要注意 类的数量 和性能问题。
7. 实际案例 - 文档处理系统
以下是一个C#访问者模式实现示例:
csharp
// 访问者接口
public interface IVisitor
{
void Visit(TextElement text);
void Visit(ImageElement image);
}
// 具体访问者 - 导出访问者
public class ExportVisitor : IVisitor
{
public void Visit(TextElement text)
{
Console.WriteLine($"Exporting text: {text.Content}");
}
public void Visit(ImageElement image)
{
Console.WriteLine($"Exporting image: {image.FileName}");
}
}
// 具体访问者 - 打印访问者
public class PrintVisitor : IVisitor
{
public void Visit(TextElement text)
{
Console.WriteLine($"Printing text: {text.Content}");
}
public void Visit(ImageElement image)
{
Console.WriteLine($"Printing image: {image.FileName}");
}
}
// 元素接口
public interface IElement
{
void Accept(IVisitor visitor);
}
// 具体元素 - 文本元素
public class TextElement : IElement
{
public string Content { get; set; }
public TextElement(string content)
{
Content = content;
}
public void Accept(IVisitor visitor)
{
visitor.Visit(this);
}
}
// 具体元素 - 图片元素
public class ImageElement : IElement
{
public string FileName { get; set; }
public ImageElement(string fileName)
{
FileName = fileName;
}
public void Accept(IVisitor visitor)
{
visitor.Visit(this);
}
}
// 对象结构
public class Document
{
private List<IElement> _elements = new List<IElement>();
public void AddElement(IElement element)
{
_elements.Add(element);
}
public void Accept(IVisitor visitor)
{
foreach (var element in _elements)
{
element.Accept(visitor);
}
}
}
// 客户端代码
public class Program
{
public static void Main(string[] args)
{
var document = new Document();
document.AddElement(new TextElement("Hello World"));
document.AddElement(new ImageElement("image.png"));
var exportVisitor = new ExportVisitor();
document.Accept(exportVisitor); // 导出文档
var printVisitor = new PrintVisitor();
document.Accept(printVisitor); // 打印文档
}
}
代码注解
- IVisitor:访问者接口,定义访问方法
- ExportVisitor:具体访问者,实现导出操作
- PrintVisitor:具体访问者,实现打印操作
- IElement:元素接口,定义接受方法
- TextElement:具体元素,实现文本元素
- ImageElement:具体元素,实现图片元素
- Accept():接受方法
- Visit():访问方法
- Document:对象结构,维护元素列表
- AddElement():添加元素方法
- Accept():接受访问者方法