CSharp-设计模式

杜绝屎山,从我做起!

资源

  • 设计模式 | 菜鸟教程 这个教程使用 Java 写的,但是我并没有系统性地学过 Java,真是太不幸运了!所以我决定用 C# 重写一遍。

C#

配置

众所周知,VSC 是世界上最好的 IDE……

VSC 中安装完 C#C# Extensions 插件后,在一个空项目中创建脚手架:

shell
dotnet new console

编写完代码后按 F5 进入调试模式。

C# 一些特性

  • abstract:用于在基类中定义没有实现的方法,要求子类必须实现它。

  • override:用于在子类中重写基类中已经实现的方法,提供不同的实现。

  • interface:是一种定义契约的机制,它指定了实现类必须遵循的一组方法、属性、事件和索引器的签名,但不提供具体的实现。

面向对象编程

封装、继承和多态是面向对象编程(OOP)的三大核心概念,通常用于设计和实现更灵活、可维护的代码。

封装(Encapsulation)

  • 封装是将数据(属性)和操作数据的方法(函数)捆绑在一起,并对外隐藏内部实现细节。外界只能通过公共接口访问对象的状态,而不能直接修改对象的内部数据。
  • 目的是保护对象的内部数据不被外界随意修改,确保对象的状态始终合法。
  • 例如,定义一个类 Car,其中有一个私有属性 speed,并通过公共方法 accelerate()brake() 来操作该属性。
csharp
public class Car
{
    private int speed;  // 私有属性
    
    public void Accelerate()
    {
        speed += 10;  // 修改属性的方法
    }
    
    public int GetSpeed()
    {
        return speed;  // 获取属性的方法
    }
}

继承(Inheritance)

  • 继承是从现有类创建新类的机制,新类称为子类,现有类称为父类。子类继承了父类的属性和方法,可以增加新的功能或者重写父类的功能。
  • 继承有助于代码的复用,避免重复定义相同的功能。
  • 例如,Car 类可以继承自 Vehicle 类,Vehicle 定义了一些通用的属性和方法,而 Car 可以扩展或修改这些功能。
csharp
public class Vehicle
{
    public int speed;
    public void Move() { Console.WriteLine("Vehicle is moving"); }
}
 
public class Car : Vehicle  // 继承自 Vehicle
{
    public void Horn() { Console.WriteLine("Car is honking"); }
}

多态(Polymorphism)

  • 多态是指相同的操作作用于不同的对象上时,表现出不同的行为。多态分为编译时多态(方法重载)和运行时多态(方法重写)。
  • 通过多态,子类可以替代父类,执行不同的实现,从而提高灵活性和可扩展性。
  • 例如,Car 类和 Truck 类可以都有一个 Move() 方法,但它们可以有不同的实现。
csharp
public class Vehicle
{
    public virtual void Move()
    {
        Console.WriteLine("Vehicle is moving");
    }
}
 
public class Car : Vehicle
{
    public override void Move()
    {
        Console.WriteLine("Car is moving");
    }
}
 
public class Truck : Vehicle
{
    public override void Move()
    {
        Console.WriteLine("Truck is moving");
    }
}
 
// 使用多态
Vehicle myVehicle = new Car();
myVehicle.Move();  // 输出: Car is moving

总结

  • 封装保护对象的内部数据,通过提供公共接口来操作数据。
  • 继承允许新类复用现有类的属性和方法,促进代码复用。
  • 多态使得相同的操作在不同的对象上产生不同的行为,提高代码的灵活性。

正文

设计模式的类型

根据设计模式的参考书 Design Patterns - Elements of Reusable Object-Oriented Software(中文译名:设计模式 - 可复用的面向对象软件元素) 中所提到的,总共有 23 种设计模式。这些模式可以分为三大类:

  • 创建型模式(Creational Patterns)
    • 工厂模式(Factory Pattern)
    • 抽象工厂模式(Abstract Factory Pattern)
    • 单例模式(Singleton Pattern)
    • 建造者模式(Builder Pattern)
    • 原型模式(Prototype Pattern)

注意

这些设计模式提供了一种在创建对象的同时隐藏创建逻辑的方式,而不是使用 new 运算符直接实例化对象。这使得程序在判断针对某个给定实例需要创建哪些对象时更加灵活。

  • 结构型模式(Structural Patterns)
    • 适配器模式(Adapter Pattern)
    • 桥接模式(Bridge Pattern)
    • 过滤器模式(Filter、Criteria Pattern)
    • 组合模式(Composite Pattern)
    • 装饰器模式(Decorator Pattern)
    • 外观模式(Facade Pattern)
    • 享元模式(Flyweight Pattern)
    • 代理模式(Proxy Pattern)

注意

这些模式关注对象之间的组合和关系,旨在解决如何构建灵活且可复用的类和对象结构。

  • 行为型模式(Behavioral Patterns)
    • 责任链模式(Chain of Responsibility Pattern)
    • 命令模式(Command Pattern)
    • 解释器模式(Interpreter Pattern)
    • 迭代器模式(Iterator Pattern)
    • 中介者模式(Mediator Pattern)
    • 备忘录模式(Memento Pattern)
    • 观察者模式(Observer Pattern)
    • 状态模式(State Pattern)
    • 空对象模式(Null Object Pattern)
    • 策略模式(Strategy Pattern)
    • 模板模式(Template Pattern)
    • 访问者模式(Visitor Pattern)

注意

这些模式关注对象之间的通信和交互,旨在解决对象之间的责任分配和算法的封装。

设计模式的六大原则

1. 单一职责原则(SRP)

注意

单一职责原则要求一个类只负责一个功能。如果一个类承担了多个职责,它应该被拆分为多个类。

2. 开放-封闭原则(OCP)

注意

开放-封闭原则要求对扩展开放对修改封闭。我们可以通过继承或者接口来实现这个原则。

Mermaid
Loading diagram…

{% tabs OCP %}

下面的例子违反了 OCP 原则:

C#
// 违反开放封闭原则
public class AreaCalculator
{
 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;
 }
}

修改后:

C#
// 改正后的例子
public interface IShape
{
	double CalculateArea();
}
 
public class Circle : IShape
{
    public double Radius { get; set; }
    public double CalculateArea()
    {
        return Math.PI * Radius * Radius;
    }
}
 
public class Rectangle : IShape
{
    public double Width { get; set; }
    public double Height { get; set; }
    public double CalculateArea()
    {
        return Width * Height;
    }
}
 
public class AreaCalculator
{
    public double CalculateArea(IShape shape)
    {
        return shape.CalculateArea();
    }
}

这样,新增形状(如三角形)时只需实现 IShape 接口,无需修改 AreaCalculator

使用:

C#
Circle circle = new Circle();
circle.Radius = 5;
Rectangle rectangle = new Rectangle();
rectangle.Width = 10;
rectangle.Height = 20;
AreaCalculator calculator = new AreaCalculator();
Console.WriteLine("Circle area: " + calculator.CalculateArea(circle));
Console.WriteLine("Rectangle area: " + calculator.CalculateArea(rectangle));
plaintext
Circle area: 78.53981633974483
Rectangle area: 200

{% endtabs %}

3. 里氏替换原则(LSP)

注意

子类应该能够替代父类,而不会影响程序的正确性。

Mermaid
Loading diagram…

{% tabs LSP %}

C#
// 违反里氏替换原则
public class Rectangle
{
    public virtual double Width { get; set; }
    public virtual double Height { get; set; }
    public double Area()
    {
        return Width * Height;
    }
}
 
public class Square : Rectangle
{
    public override double Width
    {
        set { base.Width = base.Height = value; }
    }
 
    public override double Height
    {
        set { base.Width = base.Height = value; }
    }
}
C#
// 改正后的例子
public abstract class Shape
{
    public abstract double Area();
}
 
public class Rectangle : Shape
{
    public double Width { get; set; }
    public double Height { get; set; }
 
    public override double Area()
    {
        return Width * Height;
    }
}
 
public class Square : Shape
{
    public double Side { get; set; }
 
    public override double Area()
    {
        return Side * Side;
    }
}

通过抽象类 Shape,矩形和正方形成为独立的类,避免了继承引发的问题。

{% endtabs %}

4. 接口隔离原则(ISP)

注意

接口应该只包含客户端需要的方法,不应该强迫实现不必要的功能。

Mermaid
Loading diagram…

{% tabs ISP %}

C#
// 违反接口隔离原则
public interface IPrinter
{
    void Print();
    void Scan();
    void Fax();
}
 
public class BasicPrinter : IPrinter
{
    public void Print()
    {
        Console.WriteLine("Printing...");
    }
 
    public void Scan()
    {
        throw new NotImplementedException(); // BasicPrinter 不支持扫描
    }
 
    public void Fax()
    {
        throw new NotImplementedException(); // BasicPrinter 不支持传真
    }
}
C#
// 改正后的例子
public interface IPrintable
{
    void Print();
}
 
public interface IScannable
{
    void Scan();
}
 
public interface IFaxable
{
    void Fax();
}
 
public class BasicPrinter : IPrintable
{
    public void Print()
    {
        Console.WriteLine("Printing...");
    }
}
 
public class AdvancedPrinter : IPrintable, IScannable, IFaxable
{
    public void Print()
    {
        Console.WriteLine("Printing...");
    }
 
    public void Scan()
    {
        Console.WriteLine("Scanning...");
    }
 
    public void Fax()
    {
        Console.WriteLine("Faxing...");
    }
}

{% endtabs %}

5. 依赖倒转原则(DIP)

注意

高层模块不应依赖低层模块,两者都应依赖抽象。

Mermaid
Loading diagram…

{% tabs DIP %}

C#
// 违反依赖倒转原则
public class FileReader
{
    public string Read()
    {
        return "Reading from file.";
    }
}
 
public class Application
{
    private FileReader _fileReader;
 
    public Application()
    {
        _fileReader = new FileReader();
    }
 
    public void Start()
    {
        Console.WriteLine(_fileReader.Read());
    }
}
Mermaid
Loading diagram…
C#
// 改正后的例子
public interface IReader
{
    string Read();
}
 
public class FileReader : IReader
{
    public string Read()
    {
        return "Reading from file.";
    }
}
 
public class DatabaseReader : IReader
{
    public string Read()
    {
        return "Reading from database.";
    }
}
 
public class Application
{
    private readonly IReader _reader;
 
    public Application(IReader reader)
    {
        _reader = reader;
    }
 
    public void Start()
    {
        Console.WriteLine(_reader.Read());
    }
}
C#
// 使用
IReader reader = new FileReader();
Application app = new Application(reader);
app.Start();

{% endtabs %}

6. 合成复用原则(CRP)

注意

优先使用组合而不是继承。

Mermaid
Loading diagram…

{% tabs DIP %}

C#
// 违反合成复用原则
public class Engine
{
    public void Start()
    {
        Console.WriteLine("Engine starts");
    }
}
 
public class Car : Engine
{
    public void Drive()
    {
        Console.WriteLine("Car is driving");
    }
}
C#
// 改正后的例子
public class Engine
{
    public void Start()
    {
        Console.WriteLine("Engine starts");
    }
}
 
public class Car
{
    private readonly Engine _engine;
 
    public Car(Engine engine)
    {
        _engine = engine;
    }
 
    public void Start()
    {
        _engine.Start();
    }
 
    public void Drive()
    {
        Console.WriteLine("Car is driving");
    }
}
C#
// 使用
Engine engine = new Engine();
Car car = new Car(engine);
car.Start();
car.Drive();

{% endtabs %}

结构型模式(5)

工厂模式(Factory Pattern)

注意

工厂模式(Factory Pattern)提供了一种创建对象的方式,使得创建对象的过程与使用对象的过程分离。

应用实例

  1. 汽车制造:你需要一辆汽车,只需从工厂提货,而不需要关心汽车的制造过程及其内部实现。
  2. Hibernate:更换数据库时,只需更改方言(Dialect)和数据库驱动(Driver),即可实现对不同数据库的切换。

优点:

  1. 调用者只需要知道对象的名称即可创建对象。
  2. 扩展性高,如果需要增加新产品,只需扩展一个工厂类即可。
  3. 屏蔽了产品的具体实现,调用者只关心产品的接口。

缺点:

  1. 每次增加一个产品时,都需要增加一个具体类和对应的工厂,使系统中类的数量成倍增加,增加了系统的复杂度和具体类的依赖。
Mermaid
Loading diagram…

说明

  1. 接口 Shape:定义了一个 Draw() 方法,所有实现该接口的类都需要提供 Draw() 方法的实现。
  2. RectangleSquareCircle:分别是 Shape 接口的实现类,每个类都实现了 Draw() 方法,输出各自的形状信息。
  3. ShapeFactory:工厂类,根据传入的 shapeType 字符串创建并返回相应的形状对象(RectangleSquareCircle)。
  4. FactoryPattenDemo:演示类,包含 Main 方法,创建 ShapeFactory 实例并使用其方法生成形状对象,最后调用 Draw() 方法输出信息。

类之间的关系

  • Shape <|.. Rectangle, Shape <|.. Square, Shape <|.. Circle 表示 RectangleSquareCircle 都实现了 Shape 接口。
  • ShapeFactory --> Shape 表示 ShapeFactory 依赖于 Shape 类型,并通过 GetShape 方法返回相应的实现类。

{% tabs FactoryPattern %}

C#
public interface Shape {
    void Draw();
}
C#
public class Rectangle : Shape {
    public void Draw() {
        Console.WriteLine("Inside Rectangle::Draw() method.");
    }
}
 
public class Square : Shape {
    public void Draw() {
        Console.WriteLine("Inside Square::Draw() method.");
    }
}
 
public class Circle : Shape {
    public void Draw() {
        Console.WriteLine("Inside Circle::Draw() method.");
    }
}
c#
public class ShapeFactory {
    public Shape GetShape(string shapeType) {
        if (shapeType.ToLower() == "rectangle") {
            return new Rectangle();
        }
        else if (shapeType.ToLower() == "square") {
            return new Square();
        }
        else if (shapeType.ToLower() == "circle") {
            return new Circle();
        }
        else {
            return null;
        }
    }    
}
C#
public static void Main(string[] args) {
    ShapeFactory shapeFactory = new ShapeFactory();
    Shape shape1 = shapeFactory.GetShape("Rectangle");
    shape1.Draw();
    Shape shape2 = shapeFactory.GetShape("Square");
    shape2.Draw();
    Shape shape3 = shapeFactory.GetShape("Circle");
    shape3.Draw();
}
Inside Rectangle::Draw() method.
Inside Square::Draw() method.
Inside Circle::Draw() method.

{% endtabs %}

抽象工厂模式(Abstract Factory Pattern)

注意

抽象工厂模式(Abstract Factory Pattern)是围绕一个超级工厂创建其他工厂。该超级工厂又称为其他工厂的工厂。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

应用实例

  1. 汽车制造:你需要一辆汽车,只需从工厂提货,而不需要关心汽车的制造过程及其内部实现。
  2. Hibernate:更换数据库时,只需更改方言(Dialect)和数据库驱动(Driver),即可实现对不同数据库的切换。

优点

  1. 调用者只需要知道对象的名称即可创建对象。
  2. 扩展性高,如果需要增加新产品,只需扩展一个工厂类即可。
  3. 屏蔽了产品的具体实现,调用者只关心产品的接口。

缺点

每次增加一个产品时,都需要增加一个具体类和对应的工厂,使系统中类的数量成倍增加,增加了系统的复杂度和具体类的依赖。(emmm 所以我觉得这个有点蠢)

Mermaid
Loading diagram…

说明

  1. ShapeColor 接口
    • Shape 接口定义了 Draw() 方法,所有实现该接口的类需要提供该方法的具体实现。
    • Color 接口定义了 Fill() 方法,所有实现该接口的类需要提供该方法的具体实现。
  2. RectangleSquareCircle
    • 这些类实现了 Shape 接口,并提供了 Draw() 方法的具体实现。
  3. RedGreenBlue
    • 这些类实现了 Color 接口,并提供了 Fill() 方法的具体实现。
  4. AbstractFactory 抽象类
    • 定义了两个抽象方法 GetShape()GetColor(),分别用于获取形状和颜色对象。
  5. ShapeFactoryColorFactory
    • 这些类继承自 AbstractFactory,并分别实现了 GetShape()GetColor() 方法。ShapeFactory 负责创建形状对象,ColorFactory 负责创建颜色对象。
  6. FactoryProducer
    • 这是一个工厂生产者类,根据传入的选择字符串返回一个相应的工厂实例(ShapeFactoryColorFactory)。

类之间的关系

  • Shape <|.. Rectangle, Shape <|.. Square, Shape <|.. Circle 表示 RectangleSquareCircle 都实现了 Shape 接口。
  • Color <|.. Red, Color <|.. Green, Color <|.. Blue 表示 RedGreenBlue 都实现了 Color 接口。
  • AbstractFactory <|-- ShapeFactoryAbstractFactory <|-- ColorFactory 表示 ShapeFactoryColorFactory 类继承自 AbstractFactory
  • FactoryProducer --> AbstractFactory 表示 FactoryProducer 类依赖于 AbstractFactory,并通过 GetFactory() 方法返回一个相应的工厂实例。

{% tabs AbstractFactoryPattern %}

C#
public interface Shape
{
    void Draw();
}
C#
public class Rectangle : Shape
{
    public void Draw()
    {
        Console.WriteLine("Inside Rectangle::Draw() method.");
    }
}
 
public class Square : Shape
{
    public void Draw()
    {
        Console.WriteLine("Inside Square::Draw() method.");
    }
}
 
public class Circle : Shape
{
    public void Draw()
    {
        Console.WriteLine("Inside Circle::Draw() method.");
    }
}
C#
public interface Color
{
    void Fill();
}
C#
public class Red : Color
{
    public void Fill()
    {
        Console.WriteLine("Inside Red::Fill() method.");
    }
}
 
public class Green : Color
{
    public void Fill()
    {
        Console.WriteLine("Inside Green::Fill() method.");
    }
}
 
public class Blue : Color
{
    public void Fill()
    {
        Console.WriteLine("Inside Blue::Fill() method.");
    }
}
C#
public abstract class AbstractFactory {
    public abstract Shape GetShape(string shapeType);
    public abstract Color GetColor(string colorType);
}
C#
public class ShapeFactory : AbstractFactory
{
    public override Shape GetShape(string shapeType)
    {
        if (shapeType.Equals("Rectangle"))
        {
            return new Rectangle();
        }
        else if (shapeType.Equals("Square"))
        {
            return new Square();
        }
        else if (shapeType.Equals("Circle"))
        {
            return new Circle();
        }
        return null;
    }
 
    public override Color GetColor(string colorType)
    {
        return null;
    }
}
c#
public class ColorFactory : AbstractFactory {
    public override Shape GetShape(string shapeType)
    {
        return null;
    }
 
    public override Color GetColor(string colorType)
    {
        if (colorType.Equals("Red"))
        {
            return new Red();
        }
        else if (colorType.Equals("Green"))
        {
            return new Green();
        }
        else if (colorType.Equals("Blue"))
        {
            return new Blue();
        }
        return null;
    }
}
C#
public class FactoryProducer
{
    public static AbstractFactory GetFactory(string choice)
    {
        if (choice.Equals("Shape"))
        {
            return new ShapeFactory();
        }
        else if (choice.Equals("Color"))
        {
            return new ColorFactory();
        }
        return null;
    }
}
C#
public static void Main(string[] args)
{
 
    AbstractFactory shapeFactory = FactoryProducer.GetFactory("Shape");
 
    Shape shape1 = shapeFactory.GetShape("Circle");
    shape1.Draw();
    Shape shape2 = shapeFactory.GetShape("Rectangle");
    shape2.Draw();
    Shape shape3 = shapeFactory.GetShape("Square");
    shape3.Draw();
 
    AbstractFactory colorFactory = FactoryProducer.GetFactory("Color");
 
    Color color1 = colorFactory.GetColor("Red");
    color1.Fill();
    Color color2 = colorFactory.GetColor("Green");
    color2.Fill();
    Color color3 = colorFactory.GetColor("Blue");
    color3.Fill();
}

{% endtabs %}

单例模式(Singleton Pattern)

注意

单例模式是一种创建型设计模式,它确保一个类只有一个实例,并提供了一个全局访问点来访问该实例。

注意:

  • 单例类只能有一个实例。
  • 单例类必须自己创建自己的唯一实例。
  • 单例类必须给所有其他对象提供这一实例。

应用实例

  • 一个班级只有一个班主任。
  • Windows 在多进程多线程环境下操作文件时,避免多个进程或线程同时操作一个文件,需要通过唯一实例进行处理。
  • 设备管理器设计为单例模式,例如电脑有两台打印机,避免同时打印同一个文件。

优点

  • 内存中只有一个实例,减少内存开销,尤其是频繁创建和销毁实例时(如管理学院首页页面缓存)。
  • 避免资源的多重占用(如写文件操作)。

缺点

  • 没有接口,不能继承。
  • 与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心实例化方式。
Mermaid
Loading diagram…

{% tabs SingletonPattern %}

C#
public class SingleObject
{
    // 创建 SingleObject 的一个对象
    public static SingleObject instance = new SingleObject();
    // 让构造函数为 private,这样该类就不会被实例化
    private SingleObject() { }
    // 获取唯一可用的对象
    public static SingleObject getInstance()
    {
        return instance;
    }
 
    public void ShowMessage()
    {
        Console.WriteLine("Hello World!");
    }
}
C#
public static void Main(string[] args)
{
    SingleObject obj = SingleObject.getInstance();
    obj.ShowMessage();
}
Hello World!

{% endtabs %}

建造者模式(Builder Pattern)

注意

建造者模式是一种创建型设计模式,它的主要目的是将一个复杂对象的构建过程与其表示相分离,从而可以创建具有不同表示形式的对象。

优点

  • 分离构建过程和表示,使得构建过程更加灵活,可以构建不同的表示。
  • 可以更好地控制构建过程,隐藏具体构建细节。
  • 代码复用性高,可以在不同的构建过程中重复使用相同的建造者。

缺点

  • 如果产品的属性较少,建造者模式可能会导致代码冗余。
  • 增加了系统的类和对象数量。
Mermaid
Loading diagram…

说明

  1. Item 接口:定义了商品的基本方法,包括 Name()(名称)、Packing()(包装方式)和 Price()(价格)。
  2. Packing 接口:定义了 Pack() 方法,用于返回包装的形式(如 WrapperBottle)。
  3. WrapperBottle:分别是 Packing 接口的实现类,提供具体的包装方式。
  4. BurgerColdDrink 抽象类:分别是 Item 的具体实现类,其中:
    • Burger 类定义了返回 Wrapper 作为包装方式,Name()Price() 由子类实现。
    • ColdDrink 类定义了返回 Bottle 作为包装方式,Name()Price() 由子类实现。
  5. VegBurgerChickenBurger:分别是 Burger 的具体实现类,实现了 Name()Price() 方法。
  6. CokePepsi:分别是 ColdDrink 的具体实现类,实现了 Name()Price() 方法。
  7. Meal:用于保存多个 Item(如 VegBurgerCoke 等),并提供 AddItem() 方法添加商品,GetCost() 方法计算总价,ShowItems() 方法展示所有商品的名称、包装和价格。
  8. MealBuilder:提供了两种餐点的构建方法:PrepareVegMeal()PrepareNonVegMeal(),分别用于构建素食餐和非素食餐。

类之间的关系

  • Item 接口由 BurgerColdDrink 抽象类实现。
  • Packing 接口由 WrapperBottle 类实现。
  • VegBurgerChickenBurger 类继承自 Burger,而 CokePepsi 类继承自 ColdDrink
  • Meal 类包含多个 Item 对象,并提供方法来展示和计算餐点的总价。
  • MealBuilder 类负责创建具体的 Meal 实例。

{% tabs BuilderPattern %}

C#
public interface Item
{
    public string Name();
    public Packing Packing();
    public float Price();
}
C#
public interface Packing
{
    public string Pack();
}
C#
public class Wrapper : Packing
{
    public string Pack()
    {
        return "Wrapper";
    }
}
C#
public class Bottle : Packing
{
    public string Pack()
    {
        return "Bottle";
    }
}
C#
public abstract class Burger : Item
{
    public abstract string Name();
 
    public Packing Packing()
    {
        return new Wrapper();
    }
 
    public abstract float Price();
}
C#
public abstract class ColdDrink : Item
{
    public abstract string Name();
 
    public Packing Packing()
    {
        return new Bottle();
    }
 
    public abstract float Price();
}
C#
public class VegBurger : Burger
{
    public override string Name()
    {
        return "Veg Burger";
    }
 
    public override float Price()
    {
        return 25.0f;
    }
}
C#
public class ChickenBurger : Burger
{
    public override string Name()
    {
        return "Chicken Burger";
    }
 
    public override float Price()
    {
        return 50.5f;
    }
}
C#
public class Coke : ColdDrink
{
    public override float Price()
    {
        return 30.0f;
    }
 
    public override string Name()
    {
        return "Coke";
    }
}
C#
public class Pepsi : ColdDrink
{
    public override float Price()
    {
        return 35.0f;
    }
 
    public override string Name()
    {
        return "Pepsi";
    }
}
 

创建一个 Meal 类,带有上面定义的 Item 对象。

C#
public class Meal {
    private List<Item> items = new List<Item>();
 
    public void AddItem(Item item) {
        items.Add(item);
    }
 
    public float GetCost() {
        float cost = 0;
        foreach (Item item in items) {
            cost += item.Price();
        }
        return cost;
    }
 
    public void ShowItems() {
        foreach (Item item in items) {
            Console.WriteLine(item.Name() + " - " + item.Packing().Pack() + " - " + item.Price());
        }
    }
}

创建一个 MealBuilder 类,实际的 builder 类负责创建 Meal 对象。

C#
public class MealBuilder {
    public Meal PrepareVegMeal() {
        Meal meal = new Meal();
        meal.AddItem(new VegBurger());
        meal.AddItem(new Coke());
        return meal;
    }
 
    public Meal PrepareNonVegMeal() {
        Meal meal = new Meal();
        meal.AddItem(new ChickenBurger());
        meal.AddItem(new Pepsi());
        return meal;
    }
}
C#
public static void Main(string[] args)
{
    MealBuilder mealBuilder = new MealBuilder();
    Meal vegMeal = mealBuilder.PrepareVegMeal();
    Console.WriteLine("Veg Meal");
    vegMeal.ShowItems();
    Console.WriteLine("Cost of Veg Meal: " + vegMeal.GetCost() + "\n");
 
    Meal nonVegMeal = mealBuilder.PrepareNonVegMeal();
    Console.WriteLine("Non-Veg Meal");
    nonVegMeal.ShowItems();
    Console.WriteLine("Cost of Non-Veg Meal: " + nonVegMeal.GetCost());
}

{% endtabs %}

原型模式(Prototype Pattern)

注意

原型模式(Prototype Pattern)是用于创建重复的对象,同时又能保证性能。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式之一。

它允许对象通过克隆的方式来创建副本,而不是通过构造器来实例化。

优点

  • 性能提高
  • 避免构造函数的约束

缺点

  • 配备克隆方法需要全面考虑类的功能,对已有类可能较难实现,特别是处理不支持串行化的间接对象或含有循环结构的引用时。
  • 必须实现 Cloneable 接口。
Mermaid
Loading diagram…
  • Shape (抽象类):所有形状(如 RectangleSquareCircle)的基类,包含了 ICloneable 接口的实现,用于对象克隆。

  • Rectangle、Square、Circle:具体的形状类,继承自 Shape 类并实现 Draw() 方法。

  • ShapeCache:一个缓存类,用来存储和克隆不同的形状对象。

{% tabs PrototypePattern %}

C#
public abstract class Shape : ICloneable
{
    private string id;
    protected string type;
    public abstract void Draw();
    public string GetType()
    {
        return type;
    }
    public string GetId()
    {
        return id;
    }
    public void SetId(string id)
    {
        this.id = id;
    }
    public object Clone()
    {
        object clone = null;
        try
        {
            clone = MemberwiseClone();
        }
        catch (Exception e)
        {
            Console.WriteLine(e.Message);
        }
        return clone;
    }
}
C#
public class Rectangle : Shape
{
    public Rectangle()
    {
        type = "Rectangle";
    }
    public override void Draw()
    {
        Console.WriteLine("Inside Rectangle::Draw() method.");
    }
}
 
public class Square : Shape
{
    public Square()
    {
        type = "Square";
    }
    public override void Draw()
    {
        Console.WriteLine("Inside Square::Draw() method.");
    }
}
 
public class Circle : Shape
{
    public Circle()
    {
        type = "Circle";
    }
    public override void Draw()
    {
        Console.WriteLine("Inside Circle::Draw() method.");
    }
}
C#
public class ShapeCache
{
    private static Dictionary<string, Shape> shapeMap = new Dictionary<string, Shape>();
 
    public static Shape GetShape(string shapeId)
    {
        // 从字典中获取缓存的形状
        Shape cachedShape = shapeMap.GetValueOrDefault(shapeId);
        if (cachedShape != null)
        {
            // 克隆并返回
            return (Shape)cachedShape.Clone();
        }
        return null;
    }
 
    // 模拟从数据库查询并加载形状
    public static void LoadCache()
    {
        // 创建并添加形状
        Circle circle = new Circle();
        circle.SetId("1");
        shapeMap[circle.GetId()] = circle;
 
        Square square = new Square();
        square.SetId("2");
        shapeMap[square.GetId()] = square;
 
        Rectangle rectangle = new Rectangle();
        rectangle.SetId("3");
        shapeMap[rectangle.GetId()] = rectangle;
    }
}
C#
public static void Main(string[] args)
{
    ShapeCache.LoadCache();
    Shape shape1 = ShapeCache.GetShape("1");
    shape1.Draw();
 
    Shape shape2 = ShapeCache.GetShape("2");
    shape2.Draw();
 
    Shape shape3 = ShapeCache.GetShape("3");
    shape3.Draw();
}

{% endtabs %}

结构型模式(8)

适配器模式(Adapter Pattern)

注意

适配器模式(Adapter Pattern)充当两个不兼容接口之间的桥梁,属于结构型设计模式。它通过一个中间件(适配器)将一个类的接口转换成客户期望的另一个接口,使原本不能一起工作的类能够协同工作

优点

  • 促进了类之间的协同工作,即使它们没有直接的关联。
  • 提高了类的复用性。
  • 增加了类的透明度。
  • 提供了良好的灵活性。

缺点

  • 过度使用适配器可能导致系统结构混乱,难以理解和维护。
  • 在 Java 中,由于只能继承一个类,因此只能适配一个类,且目标类必须是抽象的(C++ 可以多继承,C# 可以用接口来实现多继承)。

结构

适配器模式包含以下几个主要角色:

  • 目标接口(Target):定义客户需要的接口。
  • 适配者类(Adaptee):定义一个已经存在的接口,这个接口需要适配。
  • 适配器类(Adapter):实现目标接口,并通过组合或继承的方式调用适配者类中的方法,从而实现目标接口。
Mermaid
Loading diagram…
  • MediaPlayer 接口:定义了一个播放音频文件的 Play() 方法。

  • AdvancedMediaPlayer 接口:定义了播放 vlcmp4 格式音频文件的方法。

  • VlcPlayerMp4Player:实现了 AdvancedMediaPlayer 接口,分别可以播放 vlcmp4 格式的文件。

  • MediaAdapter:适配器类,能将 MediaPlayer 接口的请求转换为 AdvancedMediaPlayer 接口的方法调用,从而使得 AudioPlayer 可以播放 vlcmp4 格式的文件。

  • AudioPlayer:客户端类,负责播放 mp3 文件,且通过 MediaAdapter 支持播放 vlcmp4 格式文件。

{% tabs PrototypePattern %}

C#
public interface MediaPlayer
{
    public void Play(string audioType, string fileName);
}
 
public interface AdvancedMediaPlayer
{
    public void playVlc(string fileName);
    public void playMp4(string fileName);
}
C#
public class VlcPlayer : AdvancedMediaPlayer
{
    public void playVlc(string fileName)
    {
        Console.WriteLine("Playing VLC file: " + fileName);
    }
 
    public void playMp4(string fileName)
    {
        Console.WriteLine("Cannot play mp4 file with VLC player");
    }
}
 
public class Mp4Player : AdvancedMediaPlayer
{
    public void playVlc(string fileName)
    {
        Console.WriteLine("Cannot play vlc file with MP4 player");
    }
 
    public void playMp4(string fileName)
    {
        Console.WriteLine("Playing MP4 file: " + fileName);
    }
}
C#
public class MediaAdapter : MediaPlayer
{
    private AdvancedMediaPlayer advancedMediaPlayer;
 
    public MediaAdapter(string audioType)
    {
        if (audioType.Equals("vlc"))
        {
            advancedMediaPlayer = new VlcPlayer();
        }
        else if (audioType.Equals("mp4"))
        {
            advancedMediaPlayer = new Mp4Player();
        }
    }
 
    public void Play(string audioType, string fileName)
    {
        if (audioType.Equals("vlc"))
        {
            advancedMediaPlayer.playVlc(fileName);
        }
        else if (audioType.Equals("mp4"))
        {
            advancedMediaPlayer.playMp4(fileName);
        }
    }
}
C#
public class AudioPlayer : MediaPlayer
{
    private MediaPlayer mediaPlayer;
 
    public void Play(string audioType, string fileName)
    {
        if (audioType.Equals("mp3"))
        {
            Console.WriteLine("Playing MP3 file: " + fileName);
        }
        else if (audioType.Equals("vlc") || audioType.Equals("mp4"))
        {
            mediaPlayer = new MediaAdapter(audioType);
            mediaPlayer.Play(audioType, fileName);
        }
        else
        {
            Console.WriteLine("Invalid audio type");
        }
    }
}
C#
public static void Main(string[] args)
{
    AudioPlayer audioPlayer = new AudioPlayer();
 
    audioPlayer.Play("mp3", "beyond the horizon.mp3");
    audioPlayer.Play("mp4", "alone.mp4");
    audioPlayer.Play("vlc", "far far away.vlc");
    audioPlayer.Play("avi", "mind me.avi");
}
Playing MP3 file: beyond the horizon.mp3
Playing MP4 file: alone.mp4
Playing VLC file: far far away.vlc
Invalid audio type

{% endtabs %}

桥接模式(Bridge Pattern)

注意

桥接(Bridge)是用于把抽象化与实现化解耦,使得二者可以独立变化。

关键代码

  • 抽象类:定义一个抽象类,作为系统的一部分。
  • 实现类:定义一个或多个实现类,与抽象类通过聚合(而非继承)关联。
Mermaid
Loading diagram…
  • DrawAPI 接口:定义了具体绘制图形的方法(如绘制圆形)。它是实现部分的接口。

  • RedCircleGreenCircle:这两个类实现了 DrawAPI 接口,分别提供绘制红色和绿色圆形的具体实现。

  • Shape 抽象类:通过持有一个 DrawAPI 对象,它将绘制的具体实现推迟到子类中,从而实现了绘制的抽象和实现分离。

  • Circle:具体的形状类,继承自 Shape,并通过 DrawAPI 来决定如何绘制圆形。

{% tabs BridgePattern %}

C#
public interface DrawAPI {
    public void DrawCircle(int radius, int x, int y);
}
C#
public class RedCircle : DrawAPI {
    public void DrawCircle(int radius, int x, int y) {
        Console.WriteLine("Drawing a red circle of radius " + radius + " at (" + x + ", " + y + ")");
    }
}
 
public class GreenCircle : DrawAPI {
    public void DrawCircle(int radius, int x, int y) {
        Console.WriteLine("Drawing a green circle of radius " + radius + " at (" + x + ", " + y + ")");
    }
}
C#
public abstract class Shape {
    protected DrawAPI drawAPI;
    public Shape(DrawAPI drawAPI) {
        this.drawAPI = drawAPI;
    }
    public abstract void Draw();
}
C#
public class Circle : Shape {
    private int radius;
    private int x;
    private int y;
    public Circle(DrawAPI drawAPI, int radius, int x, int y) : base(drawAPI) {
        this.radius = radius;
        this.x = x;
        this.y = y;
    }
    public override void Draw() {
        drawAPI.DrawCircle(radius, x, y);
    }
} 
C#
public static void Main(string[] args)
{
    Shape redCircle = new Circle(new RedCircle(), 5, 10, 20);
    Shape greenCircle = new Circle(new GreenCircle(), 10, 30, 40);
    redCircle.Draw();
    greenCircle.Draw();
}
C#
Drawing a red circle of radius 5 at (10, 20)
Drawing a green circle of radius 10 at (30, 40)

{% endtabs %}

过滤器模式(Filter Pattern)

注意

过滤器模式(Filter Pattern)或标准模式(Criteria Pattern)是一种设计模式,这种模式允许开发人员使用不同的标准来过滤一组对象,通过逻辑运算以解耦的方式把它们连接起来。这种类型的设计模式属于结构型模式,它结合多个标准来获得单一标准。

Mermaid
Loading diagram…
  • Person:表示一个人的基本信息,包括姓名、性别和婚姻状况。提供了 getter 方法来获取这些信息。

  • Criteria 接口:定义了筛选方法 MeetCriteria(),所有具体的标准类都实现这个接口。

  • CriteriaMaleCriteriaFemaleCriteriaSingle:这些是具体的标准类,根据性别或婚姻状况来筛选 Person 对象。

  • AndCriteriaOrCriteria:这两种类通过组合已有的标准(即通过 "与" 或 "或" 逻辑)来定义更复杂的筛选条件。

{% tabs FilterPattern %}

C#
public class Person {
    private string name;
    private string gender;
    private string maritalStatus;  // 婚姻状况
    public Person(string name, string gender, string maritalStatus) {
        this.name = name;
        this.gender = gender;
        this.maritalStatus = maritalStatus;
    }
    public string GetName() {
        return name;
    }
    public string GetGender() {
        return gender;
    }
    public string GetMaritalStatus() {
        return maritalStatus;
    }
}
C#
public interface Criteria  // 标准
{
    public List<Person> MeetCriteria(List<Person> persons);
}
C#
public class CriteriaMale : Criteria
{
    public List<Person> MeetCriteria(List<Person> persons)
    {
        List<Person> malePersons = new List<Person>();
        foreach (Person person in persons)
        {
            if (person.GetGender() == "Male")
            {
                malePersons.Add(person);
            }
        }
        return malePersons;
    }
}
 
public class CriteriaFemale : Criteria
{
    public List<Person> MeetCriteria(List<Person> persons)
    {
        List<Person> femalePersons = new List<Person>();
        foreach (Person person in persons)
        {
            if (person.GetGender() == "Female")
            {
                femalePersons.Add(person);
            }
        }
        return femalePersons;
    }
}
 
public class CriteriaSingle : Criteria
{
    public List<Person> MeetCriteria(List<Person> persons)
    {
        List<Person> singlePersons = new List<Person>();
        foreach (Person person in persons)
        {
            if (person.GetMaritalStatus() == "Single")
            {
                singlePersons.Add(person);
            }
        }
        return singlePersons;
    }
}
 
public class AndCriteria : Criteria
{
    private Criteria criteria;
    private Criteria Othercriteria;
    public AndCriteria(Criteria criteria, Criteria Othercriteria)
    {
        this.criteria = criteria;
        this.Othercriteria = Othercriteria;
    }
    public List<Person> MeetCriteria(List<Person> persons)
    {
        List<Person> result = criteria.MeetCriteria(persons);
        result = Othercriteria.MeetCriteria(result);
        return result;
    }
}
 
public class OrCriteria : Criteria
{
    private Criteria criteria;
    private Criteria Othercriteria;
    public OrCriteria(Criteria criteria, Criteria Othercriteria)
    {
        this.criteria = criteria;
        this.Othercriteria = Othercriteria;
    }
    public List<Person> MeetCriteria(List<Person> persons)
    {
        List<Person> result = criteria.MeetCriteria(persons);
        List<Person> otherResult = Othercriteria.MeetCriteria(persons);
        foreach (Person person in otherResult)
        {
            if (!result.Contains(person))
            {
                result.Add(person);
            }
        }
        return result;
    }
}
C#
public static void Main(string[] args)
{
    List<Person> persons =
    [
        new Person("John", "Male", "Single"),
        new Person("Jane", "Female", "Married"),
        new Person("Bob", "Male", "Single"),
        new Person("Alice", "Female", "Married"),
        new Person("Tom", "Male", "Single"),
        new Person("Lily", "Female", "Single"),
    ];
 
    // 创建标准
    Criteria criteriaMale = new CriteriaMale();
    Criteria criteriaFemale = new CriteriaFemale();
    Criteria criteriaSingle = new CriteriaSingle();
 
    // 使用与条件
    Criteria criteriaAndMale = new AndCriteria(criteriaMale, criteriaSingle);
    Criteria criteriaAndFemale = new AndCriteria(criteriaFemale, criteriaSingle);
 
    // 使用或条件
    Criteria criteriaOrMale = new OrCriteria(criteriaMale, criteriaSingle);
    Criteria criteriaOrFemale = new OrCriteria(criteriaFemale, criteriaSingle);
 
    // 输出结果
    List<Person> maleSinglePersons = criteriaAndMale.MeetCriteria(persons);
    List<Person> femaleSinglePersons = criteriaAndFemale.MeetCriteria(persons);
    List<Person> maleOrSinglePersons = criteriaOrMale.MeetCriteria(persons);
    List<Person> femaleOrSinglePersons = criteriaOrFemale.MeetCriteria(persons);
 
    Console.WriteLine("Male and Single: " + maleSinglePersons.Count);
    Console.WriteLine("Female and Single: " + femaleSinglePersons.Count);
    Console.WriteLine("Male or Single: " + maleOrSinglePersons.Count);
    Console.WriteLine("Female or Single: " + femaleOrSinglePersons.Count);
}
Male and Single: 3
Female and Single: 1
Male or Single: 4
Female or Single: 6

{% endtabs %}

组合模式(Composite Pattern)

注意

组合模式(Composite Pattern),又叫部分整体模式,是用于把一组相似的对象当作一个单一的对象。组合模式依据树形结构来组合对象,用来表示部分以及整体层次。这种类型的设计模式属于结构型模式,它创建了对象组的树形结构。

Mermaid
Loading diagram…
Mermaid
Loading diagram…

{% tabs FilterPattern %}

C#
public class Employee
{
    private string name;
    private string dept;
    private int salary;
    private List<Employee> subordinates;
 
    public Employee(string name, string dept, int salary)
    {
        this.name = name;
        this.dept = dept;
        this.salary = salary;
        this.subordinates = new List<Employee>();
    }
 
    public void Add(Employee emp)
    {
        subordinates.Add(emp);
    }
 
    public void Remove(Employee emp)
    {
        subordinates.Remove(emp);
    }
 
    public List<Employee> GetSubordinates()
    {
        return subordinates;
    }
 
    public string toString()
    {
        return "Name: " + name + ", Department: " + dept + ", Salary: " + salary;
    }
 
}
C#
public static void Main(string[] args)
{
    Employee CEO = new Employee("John Doe", "CEO", 50000);  // 首席执行官
    Employee headSales = new Employee("Jane Smith", "Head Sales", 40000);  // 销售总监
    Employee headMarketing = new Employee("Bob Johnson", "Head Marketing", 30000);  // 市场总监
 
    Employee clerk1 = new Employee("Sarah Lee", "Marketing", 25000);  // 市场员工
    Employee clerk2 = new Employee("Tom Johnson", "Sales", 25000);
 
    Employee salesExecutive1 = new Employee("David Kim", "Sales", 20000);  // 销售员工
    Employee salesExecutive2 = new Employee("Karen Lee", "Sales", 20000);
 
    CEO.Add(headSales);
    CEO.Add(headMarketing);
 
    headSales.Add(salesExecutive1);
    headSales.Add(salesExecutive2);
 
    headMarketing.Add(clerk1);
    headMarketing.Add(clerk2);
 
    Console.WriteLine(CEO.toString());
    foreach (Employee emp in CEO.GetSubordinates())
    {
        Console.WriteLine(emp.toString());
        foreach (Employee subemp in emp.GetSubordinates())
        {
            Console.WriteLine(subemp.toString());
        }
    }
}

{% endtabs %}

装饰器模式(Decorator Pattern)

注意

装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装。

Mermaid
Loading diagram…

{% tabs DecoratorPattern %}

C#
public interface Shape {
    void Draw();
}
C#
public class Rectangle : Shape {
    public void Draw() {
        Console.WriteLine("Rectangle.Draw()");
    }
}
 
public class Circle : Shape {
    public void Draw() {
        Console.WriteLine("Circle.Draw()");
    }
}
C#
public abstract class ShapeDecorator : Shape {
    protected Shape decoratedShape;
 
    public ShapeDecorator(Shape decoratedShape) {
        this.decoratedShape = decoratedShape;
    }
 
    public void Draw() {
        decoratedShape.Draw();
    }
}
C#
public class RedShapeDecorator : ShapeDecorator {
    public RedShapeDecorator(Shape decoratedShape) : base(decoratedShape) {
    }
 
    public void Draw() {
        base.Draw();
        Console.WriteLine("RedShapeDecorator.Draw()");
    }
}
C#
public static void Main(string[] args)
{
    Shape circle = new Circle();
    ShapeDecorator redCircle = new RedShapeDecorator(new Circle());
    ShapeDecorator redRectangle = new RedShapeDecorator(new Rectangle());
    circle.Draw();
    redCircle.Draw();
    redRectangle.Draw();
}
Circle.Draw()
Circle.Draw()
Rectangle.Draw()

{% endtabs %}

外观模式(Facade Pattern)

注意

外观模式(Facade Pattern)隐藏系统的复杂性,并向客户端提供了一个客户端可以访问系统的接口。这种类型的设计模式属于结构型模式,它向现有的系统添加一个接口,来隐藏系统的复杂性。

应用实例

通过外观模式,可以简化对表示层、业务逻辑层和数据访问层的访问。

Mermaid
Loading diagram…

{% tabs FacadePattern %}

C#
public interface Shape
{
    void Draw();
}
C#
public class Square : Shape
{
    public void Draw()
    {
        Console.WriteLine("Square is being drawn");
    }
}
 
public class Circle : Shape
{
    public void Draw()
    {
        Console.WriteLine("Circle is being drawn");
    }
}
C#
public class ShapeMaker
{
    private Shape circle;
    private Shape rectangle;
    private Shape square;
 
    public ShapeMaker()
    {
        circle = new Circle();
        rectangle = new Rectangle();
        square = new Square();
    }
 
    public void DrawCircle()
    {
        circle.Draw();
    }
 
    public void DrawRectangle()
    {
        rectangle.Draw();
    }
 
    public void DrawSquare()
    {
        square.Draw();
    }
}
C#
public static void Main(string[] args)
{
    ShapeMaker shapeMaker = new ShapeMaker();
 
    shapeMaker.DrawCircle();
    shapeMaker.DrawRectangle();
    shapeMaker.DrawSquare();
}

{% endtabs %}

享元模式(Flyweight Pattern)

注意

享元模式(Flyweight Pattern)主要用于减少创建对象的数量,以减少内存占用和提高性能。这种类型的设计模式属于结构型模式,它提供了减少对象数量从而改善应用所需的对象结构的方式。

Mermaid
Loading diagram…

{% tabs FlyweightPattern %}

C#
public interface Shape
{
    void Draw();
}
C#
public class Circle : Shape
{
    private string color;
    private int x;
    private int y;
    private int radius;
 
    public Circle(string color)
    {
        this.color = color;
    }
 
    public void SetX(int x)
    {
        this.x = x;
    }
 
    public void SetY(int y)
    {
        this.y = y;
    }
 
    public void SetRadius(int radius)
    {
        this.radius = radius;
    }
 
    public void Draw()
    {
        Console.WriteLine("Drawing Circle with color " + color + " at (" + x + ", " + y + ") with radius " + radius);
    }
}
C#
public class ShapeFactory
{
    private static readonly Dictionary<string, Shape> circleMap = new Dictionary<string, Shape>();
 
    public static Shape GetCircle(string color)
    {
        // 尝试从字典中获取已存在的圆形
        Circle circle = (Circle)circleMap.GetValueOrDefault(color);
 
        if (circle == null)
        {
            // 如果没有找到,创建新的圆形,并添加到字典中
            circle = new Circle(color);
            circleMap[color] = circle;
            Console.WriteLine("Creating circle of color : " + color);
        }
 
        return circle;
    }
}
C#
private static readonly string[] colors = { "Red", "Green", "Blue", "White", "Black" };
public static void Main(string[] args)
{
    for (int i = 0; i < 20; i++) {
        Circle circle = (Circle)ShapeFactory.GetCircle(GetRandColor());
        circle.SetX(GetRandX());
        circle.SetY(GetRandY());
        circle.SetRadius(50);
        circle.Draw();
    }
}
 
private static string GetRandColor()
{
    Random rand = new Random();
    return colors[rand.Next(colors.Length)];
}
 
private static int GetRandX()
{
    Random rand = new Random();
    return (int)rand.Next(100);
}
 
private static int GetRandY()
{
    Random rand = new Random();
    return (int)rand.Next(100);
}

{% endtabs %}

代理模式(Proxy Pattern)

注意

在代理模式(Proxy Pattern)中,一个类代表另一个类的功能,这种类型的设计模式属于结构型模式。

代理模式通过引入一个代理对象来控制对原对象的访问。代理对象在客户端和目标对象之间充当中介,负责将客户端的请求转发给目标对象,同时可以在转发请求前后进行额外的处理。

Mermaid
Loading diagram…

{% tabs ProxyPattern %}

C#
public interface Image {
    void Display();
}
C#
public class RealImage : Image {
    private string fileName;
    public RealImage(string fileName) {
        this.fileName = fileName;
        LoadFromDisk(fileName);
    }
    public void Display() {
        Console.WriteLine("Displaying image: " + fileName);
    }
 
    private void LoadFromDisk(string fileName) {
        Console.WriteLine("Loading image from disk: " + fileName);
    }
}
 
public class ProxyImage : Image {
    private string fileName;
    private RealImage realImage;
    public ProxyImage(string fileName) {
        this.fileName = fileName;
    }
    public void Display() {
        if (realImage == null) {
            realImage = new RealImage(fileName);
        }
        realImage.Display();
    }
}
C#
public static void Main() {
    Image image = new ProxyImage("image.jpg");
    // 图像将从磁盘加载
    image.Display();
    // 图像不需要从磁盘加载
    image.Display();
}
Loading image from disk: image.jpg
Displaying image: image.jpg
Displaying image: image.jpg

{% endtabs %}

行为型模式(12)

责任链模式(Chain of Responsibility Pattern)

注意

责任链模式(Chain of Responsibility Pattern)为请求创建了一个接收者对象的链。这种模式给予请求的类型,对请求的发送者和接收者进行解耦。这种类型的设计模式属于行为型模式。

意图

允许将请求沿着处理者链传递,直到请求被处理为止。每个处理对象(日志记录器)只关注处理自己能够处理的日志级别,无法处理的请求则传递给下一个处理对象。

Mermaid
Loading diagram…

{% tabs Chain %}

C#
public abstract class AbstractLogger {
    public static int INFO = 1;
    public static int ERROR = 2;
    public static int DEBUG = 3;
    protected int level;
    protected AbstractLogger nextLogger;
    public void SetNextLogger(AbstractLogger nextLogger) {
        this.nextLogger = nextLogger;
    }
    public void LogMessage(int level, string message) {
        if (this.level <= level) {
            Write(message);
        }
        if (nextLogger != null) {
            nextLogger.LogMessage(level, message);
        }
    }
    protected abstract void Write(string message);
}
C#
public class ConsoleLogger : AbstractLogger {
    public ConsoleLogger(int level) {
        this.level = level;
    }
    protected override void Write(string message) {
        Console.WriteLine("Standard Console::Logger: " + message);
    }
}
 
public class ErrorLogger : AbstractLogger { 
    public ErrorLogger(int level) {
        this.level = level;
    }
    protected override void Write(string message) {
        Console.Error.WriteLine("Error Console::Logger: " + message);
    }
}
 
public class FileLogger : AbstractLogger {
    public FileLogger(int level) {
        this.level = level;
    }
    protected override void Write(string message) {
        Console.WriteLine("File::Logger: " + message);
    }
}
C#
private static AbstractLogger GetChainOfLoggers() {
    AbstractLogger errorLogger = new ErrorLogger(AbstractLogger.ERROR);
    AbstractLogger fileLogger = new FileLogger(AbstractLogger.DEBUG);
    AbstractLogger consoleLogger = new ConsoleLogger(AbstractLogger.INFO);
 
    errorLogger.SetNextLogger(fileLogger);
    fileLogger.SetNextLogger(consoleLogger);
    return errorLogger;
}
 
public static void Main() {
    AbstractLogger loggerChain = GetChainOfLoggers();
    loggerChain.LogMessage(AbstractLogger.INFO, "This is an info message");
    loggerChain.LogMessage(AbstractLogger.DEBUG, "This is a debug message");
    loggerChain.LogMessage(AbstractLogger.ERROR, "This is an error message");
}
Standard Console::Logger: This is an info message
Error Console::Logger: This is a debug message
File::Logger: This is a debug message
Standard Console::Logger: This is a debug message
Error Console::Logger: This is an error message
Standard Console::Logger: This is an error message

{% endtabs %}

Mermaid
Loading diagram…

命令模式(Command Pattern)

注意

命令模式(Command Pattern)是一种数据驱动的设计模式,它属于行为型模式。

命令模式将一个请求封装为一个对象,从而使你可以用不同的请求对客户进行参数化,对请求排队或记录请求日志,以及支持可撤销的操作。

Mermaid
Loading diagram…

{% tabs CommandPattern %}

C#
public interface Order {
    void exeute();
}
C#
public class Stock {
    private string name = "ABC";
    private int quantity = 100;
    public void Buy() {
        Console.WriteLine("Stock [ Name: " + name + " Quantity: " + quantity + " ] bought");
    }
    public void Sell() {
        Console.WriteLine("Stock [ Name: " + name + " Quantity: " + quantity + " ] sold");
    }
}
C#
public class BuyStock : Order {
    private Stock abcStock;
    public BuyStock(Stock abcStock) {
        this.abcStock = abcStock;
    }
    public void exeute() {
        abcStock.Buy();
    }
}
C#
public class SellStock : Order {
    private Stock abcStock;
    public SellStock(Stock abcStock) {
        this.abcStock = abcStock;
    }
    public void exeute() {
        abcStock.Sell();
    }
}
 
C#
public class Broker {
    private List<Order> orderList = new List<Order>();
    public void TakeOrder(Order order) {
        orderList.Add(order);
    }
    public void PlaceOrder() {
        foreach (Order order in orderList) {
            order.exeute();
        }
        orderList.Clear();
    }
}
  • 创建具体命令对象 (BuyStockSellStock)。

  • 将命令传递给 BrokerTakeOrder 方法

  • 调用 BrokerPlaceOrder 方法,按序执行命令,完成股票的买卖操作。

C#
public static void Main() {
    Stock abcStock = new Stock();
    BuyStock buyStockOrder = new BuyStock(abcStock);
    SellStock sellStockOrder = new SellStock(abcStock);
    Broker broker = new Broker();
    broker.TakeOrder(buyStockOrder);
    broker.TakeOrder(sellStockOrder);
    broker.PlaceOrder();
}
Stock [ Name: ABC Quantity: 100 ] bought
Stock [ Name: ABC Quantity: 100 ] sold

{% endtabs %}

解释器模式(Interpreter Pattern)

注意

解释器模式(Interpreter Pattern)提供了评估语言的语法或表达式的方式,它属于行为型模式。

解释器模式给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。

这种模式被用在 SQL 解析、符号处理引擎等。

Mermaid
Loading diagram…

{% tabs InterpreterPattern %}

C#
public interface Expression {
    public bool Interpret(string context);
}
C#
public class TerminalExpression : Expression {
    private string data;
    public TerminalExpression(string data) {
        this.data = data;
    }
    public bool Interpret(string context) {
        return context.Contains(data);
    }
}
public class OrExpression : Expression {
    private Expression exp1, exp2;
    public OrExpression(Expression exp1, Expression exp2) {
        this.exp1 = exp1;
        this.exp2 = exp2;
    }
    public bool Interpret(string context) {
        return exp1.Interpret(context) || exp2.Interpret(context);
    }
}
public class AndExpression : Expression {
    private Expression exp1, exp2;
    public AndExpression(Expression exp1, Expression exp2) {
        this.exp1 = exp1;
        this.exp2 = exp2;
    }
    public bool Interpret(string context) {
        return exp1.Interpret(context) && exp2.Interpret(context);
    }
}
C#
public static Expression GetMaleExpression() { 
    // 规则:Robert 和 John 是男性
    Expression robert = new TerminalExpression("Robert");
    Expression john = new TerminalExpression("John");
    return new OrExpression(robert, john);
}
public static Expression GetMarriedWomanExpression() {
    //规则:Jane 是一个已婚的女性
    Expression jane = new TerminalExpression("Jane");
    Expression married = new TerminalExpression("married");
    return new AndExpression(jane, married);
}
public static void Main() {
    Expression isMale = GetMaleExpression();
    Expression isMarriedWoman = GetMarriedWomanExpression();
 
    Console.WriteLine("John is male: " + isMale.Interpret("John")); // Output: True
    Console.WriteLine("Jane is married woman: " + isMarriedWoman.Interpret("Jane is married")); // Output: True
}
John is male: True
Jane is married woman: True

{% endtabs %}

迭代器模式(Iterator Pattern)

注意

迭代器模式提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露其内部的表示。

Mermaid
Loading diagram…

{% tabs IteratorPattern %}

C#
public interface Iterator
{
    public bool HasNext();
    public object Next();
}
public interface Container
{
    public Iterator GetIterator();
}
C#
public class NameRepository : Container
{
    public string[] names = { "John", "Mary", "Peter", "David" };
    public Iterator GetIterator()
    {
        return new NameIterator(this);
    }
    private class NameIterator : Iterator
    {
        private int index = 0;
        private NameRepository repository;
        public NameIterator(NameRepository repository)
        {
            this.repository = repository; // 初始化引用
        }
        public bool HasNext()
        {
            return index < repository.names.Length;
        }
        public object Next()
        {
            if (HasNext())
            {
                return repository.names[index++];
            }
            return null;
        }
    }
C#
public static void Main() {
    NameRepository repository = new NameRepository();
    Iterator iterator = repository.GetIterator();
    while (iterator.HasNext())
    {
        Console.WriteLine("Name: " + iterator.Next());
    }
}
Name: John
Name: Mary
Name: Peter
Name: David

{% endtabs %}

中介者模式(Mediator Pattern)

注意

中介者模式(Mediator Pattern)是用来降低多个对象和类之间的通信复杂性,属于行为型模式。

中介者模式定义了一个中介对象来封装一系列对象之间的交互。中介者使各对象之间不需要显式地相互引用,从而使其耦合松散,且可以独立地改变它们之间的交互。

MVC 框架:控制器(C)作为模型(M)和视图(V)的中介者。

Mermaid
Loading diagram…

{% tabs MediatorPattern %}

C#
public class ChatRoom {
    public static void ShowMessage(User user, string message) {
        Console.WriteLine(user.GetName() + ": " + message);
    }
}
C#
public class User {
    private string name;
    public string GetName() {
        return name;
    }
    public void SetName(string name) {
        this.name = name;
    }
    public void SendMessage(string message) {
        ChatRoom.ShowMessage(this, message);
    }
    public User(string name) { 
        this.name = name;
    }
}
C#
public static void Main()
{
    User robert = new User("Robert");
    User john = new User("John");
    robert.SendMessage("Hello, John!");
    john.SendMessage("Hi, Robert!");
}
Robert: Hello, John!
John: Hi, Robert!

{% endtabs %}

备忘录模式(Memento Pattern)

注意

备忘录模式(Memento Pattern)保存一个对象的某个状态,以便在适当的时候恢复对象,备忘录模式属于行为型模式。

备忘录模式允许在不破坏封装性的前提下,捕获和恢复对象的内部状态。

应用实例

  • 游戏存档:保存游戏进度,允许玩家加载之前的存档。
  • Windows 中的 Ctrl + Z:实现撤销操作。
Mermaid
Loading diagram…

{% tabs MementoPattern %}

C#
public class Memento  // 备忘录
{
    private string state;
    public Memento(string state)
    {
        this.state = state;
    }
    public string GetState()
    {
        return state;
    }
}
C#
public class Originator  // 发起人
{
    private string state;
    public void SetState(string state)
    {
        this.state = state;
    }
    public string GetState()
    {
        return state;
    }
    public Memento CreateMemento()
    {
        return new Memento(state);
    }
    public void SetMemento(Memento memento)
    {
        state = memento.GetState();
    }
}
C#
public class Caretaker  // 管理者
{
    private List<Memento> mementosList = new List<Memento>();
    public void Add(Memento state)
    {
        mementosList.Add(state);
    }
    public Memento Get(int index)
    {
        return mementosList[index];
    }
}
C#
public static void Main()
{
    Originator originator = new Originator();
    Caretaker caretaker = new Caretaker();
 
    originator.SetState("State 1");
    originator.SetState("State 2");
    caretaker.Add(originator.CreateMemento());  // 存档
    originator.SetState("State 3");
    caretaker.Add(originator.CreateMemento());
    originator.SetState("State 4");
    caretaker.Add(originator.CreateMemento());
 
 
    Console.WriteLine("Current state: " + originator.GetState());
    originator.SetMemento(caretaker.Get(0));
    Console.WriteLine("First state: " + originator.GetState());
    originator.SetMemento(caretaker.Get(1));
    Console.WriteLine("Second state: " + originator.GetState());
}
C#
Current state: State 4
First state: State 2
Second state: State 3

{% endtabs %}

观察者模式(Observer Pattern)

注意

观察者模式是一种行为型设计模式,它定义了一种一对多的依赖关系,当一个对象的状态发生改变时,其所有依赖者都会收到通知并自动更新。

当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)。比如,当一个对象被修改时,则会自动通知依赖它的对象。观察者模式属于行为型模式。

Mermaid
Loading diagram…

{% tabs ObserverPattern %}

C#
public class Subject
{
    private List<Observer> observers = new List<Observer>();
    private int state;
    public int GetState()
    {
        return state;
    }
    public void SetState(int state)
    {
        this.state = state;
        NotifyObservers();
    }
    public void Attach(Observer observer)
    {
        observers.Add(observer);
    }
    public void Detach(Observer observer)
    {
        observers.Remove(observer);
    }
    private void NotifyObservers()
    {
        foreach (Observer observer in observers)
        {
            observer.Update();
        }
    }
}
C#
public abstract class Observer
{
    protected Subject subject;
    public abstract void Update();
}
C#
public class BinaryObserver : Observer
{
    public BinaryObserver(Subject subject)
    {
        this.subject = subject;
        this.subject.Attach(this);
    }
    public override void Update()
    {
        Console.WriteLine("Binary string: " + Convert.ToString(subject.GetState(), 2));
    }
}
public class OctalObserver : Observer
{
    public OctalObserver(Subject subject)
    {
        this.subject = subject;
        this.subject.Attach(this);
    }
    public override void Update()
    {
        Console.WriteLine("Octal string: " + Convert.ToString(subject.GetState(), 8));
    }
}
public class HexObserver : Observer
{
    public HexObserver(Subject subject)
    {
        this.subject = subject;
        this.subject.Attach(this);
    }
    public override void Update()
    {
        Console.WriteLine("Hexadecimal string: " + Convert.ToString(subject.GetState(), 16));
    }
}
C#
public static void Main()
{
    Subject subject = new Subject();
    BinaryObserver binaryObserver = new BinaryObserver(subject);
    OctalObserver octalObserver = new OctalObserver(subject);
    HexObserver hexObserver = new HexObserver(subject);
    subject.SetState(10);
    subject.SetState(15);
}

{% endtabs %}

状态模式(State Pattern)

注意

在状态模式(State Pattern)中,类的行为是基于它的状态改变的,这种类型的设计模式属于行为型模式。

在状态模式中,我们创建表示各种状态的对象和一个行为随着状态对象改变而改变的 context 对象。

Mermaid
Loading diagram…

{% tabs StatePattern %}

C#
public interface State
{
    public void DoAction(Context context);
}
C#
public class StartState : State
{
    public void DoAction(Context context)
    {
        Console.WriteLine("Starting the application");
        context.SetState(this);
    }
    public override string ToString() {
        return "Start State";
    }
}
public class StopState : State {
    public void DoAction(Context context) {
        Console.WriteLine("Stopping the application");
        context.SetState(this);
    }
    public override string ToString() {
        return "Stop State";
    }
}
C#
public class Context
{
    private State state;
    public Context()
    {
        state = null;
    }
    public void SetState(State state)
    {
        this.state = state;
    }
    public State GetState()
    {
        return state;
    }
}
C#
public static void Main()
{
    Context context = new Context();
    StartState startState = new StartState();
    startState.DoAction(context);
 
    Console.WriteLine("Current state: " + context.GetState());
 
    StopState stopState = new StopState();
    stopState.DoAction(context);
 
    Console.WriteLine("Current state: " + context.GetState());
}
Starting the application
Current state: Start State
Stopping the application
Current state: Stop State

{% endtabs %}

空对象模式(Null Object Pattern)

注意

在空对象模式(Null Object Pattern)中,一个空对象取代 NULL 对象实例的检查。Null 对象不是检查空值,而是反应一个不做任何动作的关系。这样的 Null 对象也可以在数据不可用的时候提供默认的行为。

在空对象模式中,我们创建一个指定各种要执行的操作的抽象类和扩展该类的实体类,还创建一个未对该类做任何实现的空对象类,该空对象类将无缝地使用在需要检查空值的地方。

使用一个空对象代替 null 值,这个空对象实现了相同的接口,但对请求不做任何操作或提供默认操作。

Mermaid
Loading diagram…

{% tabs NullObjectPattern %}

C#
public abstract class AbstractCustomer {
    protected string name;
    public abstract bool IsNil();
    public abstract string GetName();
}
C#
public class RealCustomer : AbstractCustomer {
    public RealCustomer(string name) {
        this.name = name;
    }
    public override bool IsNil() {
        return false;
    }
    public override string GetName() {
        return name;
    }
}
 
public class NilCustomer : AbstractCustomer {
    public override bool IsNil() {
        return true;
    }
    public override string GetName() {
        return "Not Available in Customer Database";
    }
}
C#
public class CustomerFactory {
    public static readonly string[] names = { "John", "Mary", "Tom", "David" };
    public static AbstractCustomer CreateCustomer(string name) {
        if (Array.IndexOf(names, name) >= 0) {
            return new RealCustomer(name);
        } else {
            return new NilCustomer();
        }
    }
}
C#
public static void Main()
{
    AbstractCustomer customer1 = CustomerFactory.CreateCustomer("John");
    AbstractCustomer customer2 = CustomerFactory.CreateCustomer("Peter");
    AbstractCustomer customer3 = CustomerFactory.CreateCustomer("Mary");
    AbstractCustomer customer4 = CustomerFactory.CreateCustomer("Tom");
 
    Console.WriteLine("Customers:");
    Console.WriteLine(customer1.GetName());
    Console.WriteLine(customer2.GetName());
    Console.WriteLine(customer3.GetName());
    Console.WriteLine(customer4.GetName());
}
Customers:
John
Not Available in Customer Database
Mary
Tom

{% endtabs %}

策略模式(Strategy Pattern)

注意

在策略模式(Strategy Pattern)中一个类的行为或其算法可以在运行时更改。这种类型的设计模式属于行为型模式。

**意图:**将每个算法封装起来,使它们可以互换使用。

Mermaid
Loading diagram…

{% tabs StrategyPattern %}

C#
public interface Strategy {
    public int DoOperation(int num1, int num2);
}
C#
public class OperationAdd : Strategy {
    public int DoOperation(int num1, int num2) {
        return num1 + num2;
    }
}
public class OperationSubtract : Strategy {
    public int DoOperation(int num1, int num2) {
        return num1 - num2;
    }
}
public class OperationMultiply : Strategy {
    public int DoOperation(int num1, int num2) {
        return num1 * num2;
    }
}
C#
public class Context {
    private Strategy strategy;
    public Context(Strategy strategy) {
        this.strategy = strategy;
    }
    public int DoOperation(int num1, int num2) {
        return strategy.DoOperation(num1, num2);
    }
}
C#
public static void Main()
{
    Context context = new Context(new OperationAdd());
    Console.WriteLine("10 + 5 = " + context.DoOperation(10, 5));
 
    context = new Context(new OperationSubtract());
    Console.WriteLine("10 - 5 = " + context.DoOperation(10, 5));
 
    context = new Context(new OperationMultiply());
    Console.WriteLine("10 * 5 = " + context.DoOperation(10, 5));
}

{% endtabs %}

模板模式(Template Pattern)

注意

模板模式(Template Pattern)中,一个抽象类公开定义了执行它的方法的方式/模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行。这种类型的设计模式属于行为型模式。

Mermaid
Loading diagram…

{% tabs StrategyPattern %}

C#
public abstract class Game {
    public abstract void Initialize();
    public abstract void StartPlay();
    public abstract void EndPlay();
    public void Play() {
        Initialize();
        StartPlay();
        EndPlay();
    }
}
C#
public class Cricket : Game {
    public override void Initialize() {
        Console.WriteLine("Cricket game initialized.");
    }
    public override void StartPlay() {
        Console.WriteLine("Cricket game started.");
    }
    public override void EndPlay() {
        Console.WriteLine("Cricket game ended.");
    }
}
 
public class Football : Game {
    public override void Initialize() {
        Console.WriteLine("Football game initialized.");
    }
    public override void StartPlay() {
        Console.WriteLine("Football game started.");
    }
    public override void EndPlay() {
        Console.WriteLine("Football game ended.");
    }
}
C#
public static void Main()
{
    Game game = new Cricket();
    game.Play();
    game = new Football();
    game.Play();
}
Cricket game initialized.
Cricket game started.
Cricket game ended.
Football game initialized.
Football game started.
Football game ended.

{% endtabs %}

访问者模式(Visitor Pattern)

注意

在访问者模式(Visitor Pattern)中,我们使用了一个访问者类,它改变了元素类的执行算法。

Mermaid
Loading diagram…

{% tabs VisitorPattern %}

C#
public interface ComputerPart {
    public void Accept(ComputerPartVisitor computerPartVisitor);
}
C#
public class Keyboard : ComputerPart {
    public void Accept(ComputerPartVisitor computerPartVisitor) {
        computerPartVisitor.Visit(this);
    }
}
public class Mouse : ComputerPart {
    public void Accept(ComputerPartVisitor computerPartVisitor) {
        computerPartVisitor.Visit(this);
    }
}
public class Monitor : ComputerPart {
    public void Accept(ComputerPartVisitor computerPartVisitor) {
        computerPartVisitor.Visit(this);
    }
}
public class Computer : ComputerPart {
    ComputerPart[] parts;
    public Computer() {
        parts = [new Keyboard(), new Mouse(), new Monitor()];
    }
    public void Accept(ComputerPartVisitor computerPartVisitor) {
        foreach (ComputerPart part in parts) {
            part.Accept(computerPartVisitor);
        }
    }
}
C#
public interface ComputerPartVisitor {
    public void Visit(Computer computer);
    public void Visit(Keyboard keyboard);
    public void Visit(Mouse mouse);
    public void Visit(Monitor monitor);
}
C#
public class ComputerPartDisplayVisitor : ComputerPartVisitor {
    public void Visit(Computer computer) {
        Console.WriteLine("Displaying Computer");
    }
    public void Visit(Keyboard keyboard) {
        Console.WriteLine("Displaying Keyboard");
    }
    public void Visit(Mouse mouse) {
        Console.WriteLine("Displaying Mouse");
    }
    public void Visit(Monitor monitor) {
        Console.WriteLine("Displaying Monitor");
    }
}   
C#
public static void Main()
{
    ComputerPart computer = new Computer();
    computer.Accept(new ComputerPartDisplayVisitor());
}
Displaying Keyboard
Displaying Mouse
Displaying Monitor

{% endtabs %}