Implement 23 common design patterns using C#

Implement 23 common design patterns using C#

These patterns are best practices for solving common object-oriented design problems.

最后更新 6/8/2023 9:51 PM
Token
预计阅读 48 分钟
分类
.NET
标签
.NET C#

Design patterns are generally divided into three main categories:

  • creational patterns

  • structural model

  • Behavioral patterns.

These patterns are best practices for solving common object-oriented design problems.

以下是 23 种常见的设计模式并且提供c#代码案例

Creative model:

1. Singleton model (Singleton)

public sealed class Singleton
{
    //创建一个只读的静态Singleton实例
    private static readonly Singleton instance = new Singleton();

    // 记录Singleton的创建次数
    private static int instanceCounter = 0;

    // 单例实例的公共访问点
    public static Singleton Instance
    {
        get
        {
            return instance;
        }
    }

    // 私有构造函数
    private Singleton()
    {
        instanceCounter++;
        Console.WriteLine("Instances Created " + instanceCounter);
    }

    // 在此处添加其他的Singleton类方法
    public void LogMessage(string message)
    {
        Console.WriteLine("Message: " + message);
    }
}

在这个例子中,我们有一个名为Singleton的类,它有一个私有的构造函数和一个静态的只读属性Instance,用于访问Singleton类的唯一实例。我们还有一个LogMessage方法,用于模拟Singleton类的某个行为。

以下是一个使用这个Singleton类的控制台应用程序:

class Program
{
    static void Main(string[] args)
    {
        Singleton fromEmployee = Singleton.Instance;
        fromEmployee.LogMessage("Message from Employee");

        Singleton fromBoss = Singleton.Instance;
        fromBoss.LogMessage("Message from Boss");
        Console.ReadLine();
    }
}

2. Factory Method Model

The factory method pattern is a creative design pattern that provides an interface to create objects, but allows subclasses to decide which class to instantiate. The factory method departs the instantiation of classes to subclasses.

Here is a simple example of a factory method pattern implemented using C#:

// 抽象产品
public interface IProduct
{
    string Operation();
}

// 具体产品A
public class ProductA : IProduct
{
    public string Operation()
    {
        return "{Result of ProductA}";
    }
}

// 具体产品B
public class ProductB : IProduct
{
    public string Operation()
    {
        return "{Result of ProductB}";
    }
}

// 抽象创建者
public abstract class Creator
{
    public abstract IProduct FactoryMethod();
}

// 具体创建者A
public class CreatorA : Creator
{
    public override IProduct FactoryMethod()
    {
        return new ProductA();
    }
}

// 具体创建者B
public class CreatorB : Creator
{
    public override IProduct FactoryMethod()
    {
        return new ProductB();
    }
}

以上代码中定义了两个产品ProductAProductB,这两个产品都实现了IProduct接口。接着我们有两个 Creator 类,CreatorACreatorB,它们都继承自抽象基类CreatorCreatorA工厂创建ProductACreatorB工厂创建ProductB

Here is an example of using these factories and products:

class Program
{
    static void Main(string[] args)
    {
        // 创建工厂对象
        Creator creatorA = new CreatorA();
        Creator creatorB = new CreatorB();

        // 通过工厂方法创建产品对象
        IProduct productA = creatorA.FactoryMethod();
        IProduct productB = creatorB.FactoryMethod();

        // 打印结果
        Console.WriteLine("ProductA says: " + productA.Operation());
        Console.WriteLine("ProductB says: " + productB.Operation());

        Console.ReadLine();
    }
}

当你运行这个程序时,它会显示出ProductAProductBOperation方法返回的结果。这说明我们已经成功地使用工厂方法模式创建了产品实例。每个工厂类决定了它创建哪个产品的实例。这种方式使得客户端代码不需要直接实例化产品类,而只需要依赖工厂接口,增加了程序的灵活性。

3. Abstract Factory Model

The abstract factory pattern is a creative design pattern that provides an interface for creating a series of related or dependent objects without specifying concrete classes for those objects. In this pattern, clients use classes through their abstract interfaces, allowing the pattern to replace implementation classes without affecting the client.

Here is a simple C#implementation of the abstract factory pattern:

// 抽象产品:动物
public interface IAnimal
{
    string Speak();
}

// 具体产品:狗
public class Dog : IAnimal
{
    public string Speak()
    {
        return "Bark Bark";
    }
}

// 具体产品:猫
public class Cat : IAnimal
{
    public string Speak()
    {
        return "Meow Meow";
    }
}

// 抽象工厂
public abstract class IAnimalFactory
{
    public abstract IAnimal CreateAnimal();
}

// 具体工厂:狗工厂
public class DogFactory : IAnimalFactory
{
    public override IAnimal CreateAnimal()
    {
        return new Dog();
    }
}

// 具体工厂:猫工厂
public class CatFactory : IAnimalFactory
{
    public override IAnimal CreateAnimal()
    {
        return new Cat();
    }
}

以上代码定义了两种动物DogCat,它们都实现了IAnimal接口。然后我们有两个工厂类,DogFactoryCatFactory,它们都继承自IAnimalFactoryDogFactory生产Dog,而CatFactory生产Cat

Here is an example of using these factories and products:

class Program
{
    static void Main(string[] args)
    {
        // 创建工厂
        IAnimalFactory dogFactory = new DogFactory();
        IAnimalFactory catFactory = new CatFactory();

        // 使用工厂创建产品
        IAnimal dog = dogFactory.CreateAnimal();
        IAnimal cat = catFactory.CreateAnimal();

        // 打印结果
        Console.WriteLine("Dog says: " + dog.Speak());
        Console.WriteLine("Cat says: " + cat.Speak());

        Console.ReadLine();
    }
}

When you run this program, the results of Dog and Cat's Speak methods will print out, which shows that we have successfully created product instances using the abstract factory pattern. This approach allows client code to not need to directly instantiate product classes, but only needs to rely on the factory interface, increasing the flexibility and extensibility of the program.

4. Builder Mode

The builder pattern is a creative design pattern that provides an interface to create objects, but allows different products to be created using the same build process.

Here is a simple example of implementing the builder pattern in C#:

// 产品
public class Car
{
    public string Engine { get; set; }
    public string Wheels { get; set; }
    public string Doors { get; set; }
}

// 建造者抽象类
public abstract class CarBuilder
{
    protected Car car;

    public void CreateNewCar()
    {
        car = new Car();
    }

    public Car GetCar()
    {
        return car;
    }

    public abstract void SetEngine();
    public abstract void SetWheels();
    public abstract void SetDoors();
}

// 具体建造者
public class FerrariBuilder : CarBuilder
{
    public override void SetEngine()
    {
        car.Engine = "V8";
    }

    public override void SetWheels()
    {
        car.Wheels = "18 inch";
    }

    public override void SetDoors()
    {
        car.Doors = "2";
    }
}

// 指挥者
public class Director
{
    public Car Construct(CarBuilder carBuilder)
    {
        carBuilder.CreateNewCar();
        carBuilder.SetEngine();
        carBuilder.SetWheels();
        carBuilder.SetDoors();
        return carBuilder.GetCar();
    }
}

以上代码中,Car是我们要创建的产品,CarBuilder是抽象的建造者,定义了制造一个产品所需要的各个步骤,FerrariBuilder是具体的建造者,实现了CarBuilder定义的所有步骤,Director是指挥者,它告诉建造者应该按照什么顺序去执行哪些步骤。

Here is an example of using this builder pattern:

class Program
{
    static void Main(string[] args)
    {
        Director director = new Director();
        CarBuilder builder = new FerrariBuilder();
        Car ferrari = director.Construct(builder);

        Console.WriteLine($"Engine: {ferrari.Engine}, Wheels: {ferrari.Wheels}, Doors: {ferrari.Doors}");
        Console.ReadLine();
    }
}

当你运行这个程序时,会看到我们已经成功地创建了一个Car实例,它的各个部分是按照FerrariBuilder所定义的方式创建的。这说明我们使用建造者模式成功地将一个复杂对象的构造过程解耦,使得同样的构造过程可以创建不同的表示。

5. Prototype

A prototype pattern is a creative design pattern that implements a prototype interface that is used to create a clone of the current object. This pattern is adopted when the cost of directly creating objects is high. For example, an object needs to be created after a costly database operation.

Here is a simple example of implementing a prototype pattern in C#:

// 抽象原型
public interface IPrototype
{
    IPrototype Clone();
}

// 具体原型
public class ConcretePrototype : IPrototype
{
    public string Name { get; set; }
    public int Value { get; set; }

    public IPrototype Clone()
    {
        // 实现深拷贝
        return (ConcretePrototype)this.MemberwiseClone(); // Clones the concrete object.
    }
}

以上代码定义了一个ConcretePrototype类,它实现了IPrototype接口。接口定义了一个Clone方法,用于复制对象。在ConcretePrototype类中,我们使用了MemberwiseClone方法来创建一个新的克隆对象。

The following is an example of using the prototype pattern:

class Program
{
    static void Main(string[] args)
    {
        ConcretePrototype prototype = new ConcretePrototype();
        prototype.Name = "Original";
        prototype.Value = 10;

        Console.WriteLine("Original instance: " + prototype.Name + ", " + prototype.Value);

        ConcretePrototype clone = (ConcretePrototype)prototype.Clone();
        Console.WriteLine("Cloned instance: " + clone.Name + ", " + clone.Value);

        Console.ReadLine();
    }
}

在这个例子中,我们创建了一个ConcretePrototype对象,并为其属性赋值,然后我们调用Clone方法创建了一个新的ConcretePrototype对象。当我们运行这个程序时,会看到原始对象和克隆对象的属性是相同的,这表明我们已经成功地克隆了一个对象。

The implementation process is as follows:

  1. Create a concrete prototype object and assign values to its properties.
  2. 调用原型对象的Clone方法,创建一个新的对象,该对象的属性与原型对象的属性相同。
  3. Print the properties of the prototype object and the clone object and verify that they are the same.

Structural model:

1. Adapter Mode

The goal of the Adapter Pattern is to transform the interface of one class into another that the client expects. Adapters allow classes that would otherwise not work together due to incompatible interfaces to work together. Here is an example of an adapter pattern implemented using C#:

在这个例子中,我将创建一个ITarget接口和一个Adaptee类。然后我将创建一个适配器Adapter,它会实现ITarget接口并使用Adaptee类的方法来满足ITarget的要求。

// 目标接口,或者客户端期望的接口。
public interface ITarget
{
    string GetRequest();
}

// 需要适配的类。
public class Adaptee
{
    public string GetSpecificRequest()
    {
        return "Specific request.";
    }
}

// 适配器类,它通过在内部封装一个Adaptee对象来满足ITarget的接口。
public class Adapter : ITarget
{
    private readonly Adaptee _adaptee;

    public Adapter(Adaptee adaptee)
    {
        this._adaptee = adaptee;
    }

    public string GetRequest()
    {
        return $"This is '{this._adaptee.GetSpecificRequest()}'";
    }
}

// 客户端代码,它与所有符合ITarget接口的对象兼容。
public class Client
{
    public void MakeRequest(ITarget target)
    {
        Console.WriteLine(target.GetRequest());
    }
}

The implementation process is as follows:

class Program
{
    static void Main(string[] args)
    {
        Adaptee adaptee = new Adaptee();
        ITarget target = new Adapter(adaptee);
        Client client = new Client();

        // 由于适配器可以使得Adaptee与Client兼容,所以我们可以调用Client的MakeRequest方法
        client.MakeRequest(target);

        // 等待用户输入,以防止控制台程序立即退出。
        Console.ReadKey();
    }
}

When the above code is run, the following output will be printed on the console:

This is 'Specific request.'

此例中,Adapter是将Adaptee适配到ITarget接口的类。当客户端(在这种情况下是Client类)调用AdapterGetRequest方法时,Adapter会将这个请求转发给AdapteeGetSpecificRequest方法。

在此实例中,我们通过使用Adapter类,让原本接口不兼容的Client类和Adaptee类能够顺利地一起工作。

2. Bridge Mode (Bridge)

The bridging pattern is a structural design pattern used to separate the abstract part from its implementation part so that they can both be independently changed.

Here is a simple example of implementing the bridging pattern in C#:

// 实现类接口
public interface IImplementor
{
    void OperationImp();
}

// 具体实现类A
public class ConcreteImplementorA : IImplementor
{
    public void OperationImp()
    {
        Console.WriteLine("Concrete Implementor A");
    }
}

// 具体实现类B
public class ConcreteImplementorB : IImplementor
{
    public void OperationImp()
    {
        Console.WriteLine("Concrete Implementor B");
    }
}

// 抽象类
public abstract class Abstraction
{
    protected IImplementor implementor;

    public Abstraction(IImplementor implementor)
    {
        this.implementor = implementor;
    }

    public virtual void Operation()
    {
        implementor.OperationImp();
    }
}

// 扩充的抽象类
public class RefinedAbstraction : Abstraction
{
    public RefinedAbstraction(IImplementor implementor) : base(implementor) { }

    public override void Operation()
    {
        Console.WriteLine("Refined Abstraction is calling implementor's method:");
        base.Operation();
    }
}

在这个代码中,Abstraction是抽象类,它有一个IImplementor接口的实例,通过这个实例调用实现类的方法。RefinedAbstraction是扩充的抽象类,它继承自AbstractionConcreteImplementorAConcreteImplementorB是实现类,它们实现了IImplementor接口。

The following is an example of using this pattern:

class Program
{
    static void Main(string[] args)
    {
        IImplementor implementorA = new ConcreteImplementorA();
        Abstraction abstractionA = new RefinedAbstraction(implementorA);
        abstractionA.Operation();

        IImplementor implementorB = new ConcreteImplementorB();
        Abstraction abstractionB = new RefinedAbstraction(implementorB);
        abstractionB.Operation();

        Console.ReadLine();
    }
}

在这个例子中,我们创建了两个实现类的实例,然后创建了两个抽象类的实例,每个抽象类的实例都有一个实现类的实例。当我们调用抽象类的Operation方法时,它会调用实现类的OperationImp方法。

The implementation process is as follows:

  1. Create an instance of the implementation class.
  2. Create an instance of an abstract class that has an instance of an implementation class.
  3. 调用抽象类的Operation方法,该方法会调用实现类的OperationImp方法。

3. Composite model

Composite pattern is a structural design pattern that allows you to group objects into a tree structure and use them as if you were using individual objects. The main purpose of this pattern is to provide consistency between individual objects and composite objects.

Here is a simple example of implementing composition patterns in C#:

// 抽象组件类
public abstract class Component
{
    protected string name;

    public Component(string name)
    {
        this.name = name;
    }

    public abstract void Add(Component c);
    public abstract void Remove(Component c);
    public abstract void Display(int depth);
}

// 叶节点类
public class Leaf : Component
{
    public Leaf(string name) : base(name) { }

    public override void Add(Component c)
    {
        Console.WriteLine("Cannot add to a leaf");
    }

    public override void Remove(Component c)
    {
        Console.WriteLine("Cannot remove from a leaf");
    }

    public override void Display(int depth)
    {
        Console.WriteLine(new String('-', depth) + name);
    }
}

// 构件容器类
public class Composite : Component
{
    private List<Component> _children = new List<Component>();

    public Composite(string name) : base(name) { }

    public override void Add(Component component)
    {
        _children.Add(component);
    }

    public override void Remove(Component component)
    {
        _children.Remove(component);
    }

    public override void Display(int depth)
    {
        Console.WriteLine(new String('-', depth) + name);

        // 显示每个节点的子节点
        foreach (Component component in _children)
        {
            component.Display(depth + 2);
        }
    }
}

在这个代码中,Component是组件抽象类,它有一个名字,并定义了添加、删除和显示操作。Leaf是叶子节点,它实现了Component的操作。Composite是组件容器,它可以添加、删除和显示其子节点。

The following is an example of using this pattern:

class Program
{
    static void Main(string[] args)
    {
        Composite root = new Composite("root");
        root.Add(new Leaf("Leaf A"));
        root.Add(new Leaf("Leaf B"));

        Composite comp = new Composite("Composite X");
        comp.Add(new Leaf("Leaf XA"));
        comp.Add(new Leaf("Leaf XB"));

        root.Add(comp);

        Composite comp2 = new Composite("Composite XY");
        comp2.Add(new Leaf("Leaf XYA"));
        comp2.Add(new Leaf("Leaf XYB"));

        comp.Add(comp2);

        root.Add(new Leaf("Leaf C"));

        // 在组合中添加和删除
        Leaf leaf = new Leaf("Leaf D");
        root.Add(leaf);
        root.Remove(leaf);

        // 显示树形结构
        root.Display(1);

        Console.ReadLine();
    }
}

In this example, we create a root node and add two leaf nodes to it. Then we created a composite node and added two leaf nodes to it, and then we added the composite node to the root node. We also added another composite node to the composite node. Finally, we added and deleted a leaf node from the root node, and then showed the structure of the tree.

The implementation process is as follows:

  1. Create combinations and leaf objects.
  2. 通过调用组合对象的Add方法将叶子对象和其他组合对象添加到组合对象中。
  3. 通过调用组合对象的Remove方法将叶子对象从组合对象中移除。
  4. 调用组合对象的Display方法显示组合对象的结构。

4. Decorator

Decoration pattern is a structural design pattern that allows functionality to be dynamically added to objects at run time, which provides a more flexible solution than inheritance.

Here is a simple example of implementing decoration mode in C#:

// 抽象组件
public abstract class Component
{
    public abstract string Operation();
}

// 具体组件
public class ConcreteComponent : Component
{
    public override string Operation()
    {
        return "ConcreteComponent";
    }
}

// 抽象装饰器
public abstract class Decorator : Component
{
    protected Component component;

    public Decorator(Component component)
    {
        this.component = component;
    }

    public override string Operation()
    {
        if (component != null)
        {
            return component.Operation();
        }
        else
        {
            return string.Empty;
        }
    }
}

// 具体装饰器A
public class ConcreteDecoratorA : Decorator
{
    public ConcreteDecoratorA(Component comp) : base(comp) { }

    public override string Operation()
    {
        return $"ConcreteDecoratorA({base.Operation()})";
    }
}

// 具体装饰器B
public class ConcreteDecoratorB : Decorator
{
    public ConcreteDecoratorB(Component comp) : base(comp) { }

    public override string Operation()
    {
        return $"ConcreteDecoratorB({base.Operation()})";
    }
}

在这个代码中,Component是一个抽象组件,它定义了一个Operation方法。ConcreteComponent是具体组件,它实现了ComponentOperation方法。Decorator是一个抽象装饰器,它包含一个Component对象,并重写了Operation方法。ConcreteDecoratorAConcreteDecoratorB是具体的装饰器,它们继承了Decorator并重写了Operation方法,以添加新的功能。

The following is an example of using this pattern:

class Program
{
    static void Main(string[] args)
    {
        // 基本组件
        Component component = new ConcreteComponent();
        Console.WriteLine("Basic Component: " + component.Operation());

        // 装饰后的组件
        Component decoratorA = new ConcreteDecoratorA(component);
        Console.WriteLine("A Decorated: " + decoratorA.Operation());

        Component decoratorB = new ConcreteDecoratorB(decoratorA);
        Console.WriteLine("B Decorated: " + decoratorB.Operation());

        Console.ReadLine();
    }
}

在这个例子中,我们首先创建了一个ConcreteComponent对象,并调用它的Operation方法。然后我们创建了一个ConcreteDecoratorA对象,它装饰了ConcreteComponent,并调用它的Operation方法。最后,我们创建了一个ConcreteDecoratorB对象,它装饰了ConcreteDecoratorA,并调用它的Operation方法。这样,我们就可以在运行时动态地添加功能。

The implementation process is as follows:

  1. Create a concrete component object and invoke its action.
  2. Create a decorator object that decorates a specific component and invokes its actions. In an operation, the decorator first calls the operation of a specific component, and then performs additional operations.
  3. Create another decorator object, decorate the previous decorator, and invoke its action. In an operation, this decorator first calls the operation of the previous decorator, and then performs additional operations.

5. Appearance model (Facade)

A facade pattern is a structural design pattern that provides a unified interface to access a set of interfaces in a subsystem. The appearance pattern defines a high-level interface that makes the subsystem easier to use.

Here is a simple example of implementing appearance patterns in C#:

// 子系统A
public class SubSystemA
{
    public string OperationA()
    {
        return "SubSystemA, OperationA\n";
    }
}

// 子系统B
public class SubSystemB
{
    public string OperationB()
    {
        return "SubSystemB, OperationB\n";
    }
}

// 子系统C
public class SubSystemC
{
    public string OperationC()
    {
        return "SubSystemC, OperationC\n";
    }
}

// 外观类
public class Facade
{
    private SubSystemA a = new SubSystemA();
    private SubSystemB b = new SubSystemB();
    private SubSystemC c = new SubSystemC();

    public string OperationWrapper()
    {
        string result = "Facade initializes subsystems:\n";
        result += a.OperationA();
        result += b.OperationB();
        result += c.OperationC();
        return result;
    }
}

在这个代码中,SubSystemASubSystemBSubSystemC都是子系统,每个子系统都有一个操作。Facade是一个外观类,它封装了对子系统的操作,提供了一个统一的接口。

The following is an example of using this pattern:

class Program
{
    static void Main(string[] args)
    {
        Facade facade = new Facade();
        Console.WriteLine(facade.OperationWrapper());

        Console.ReadLine();
    }
}

在这个例子中,我们创建了一个Facade对象,并调用了它的OperationWrapper方法。这个方法封装了对子系统的操作,使得客户端可以不直接操作子系统,而是通过外观类操作子系统。

The implementation process is as follows:

  1. Create an appearance object.

  2. Indirectly manipulate the subsystem by calling methods on the facade object.

  3. The operation of the subsystem is encapsulated in the method of the facade object, and the client does not need to directly operate the subsystem.

6. Flyweight model

Flyweight Pattern is a structural design pattern that is mainly used to reduce the number of objects created to reduce memory consumption and improve performance. This type of design pattern is a structural pattern that provides a way to reduce the number of objects and thereby improve the object structure required by the application.

The following is a simple example of implementing the benefit model in C#:

// 享元类
public class Flyweight
{
    private string intrinsicState;

    // 构造函数
    public Flyweight(string intrinsicState)
    {
        this.intrinsicState = intrinsicState;
    }

    // 业务方法
    public void Operation(string extrinsicState)
    {
        Console.WriteLine($"Intrinsic State = {intrinsicState}, Extrinsic State = {extrinsicState}");
    }
}

// 享元工厂类
public class FlyweightFactory
{
    private Dictionary<string, Flyweight> flyweights = new Dictionary<string, Flyweight>();

    public Flyweight GetFlyweight(string key)
    {
        if (!flyweights.ContainsKey(key))
        {
            flyweights[key] = new Flyweight(key);
        }

        return flyweights[key];
    }

    public int GetFlyweightCount()
    {
        return flyweights.Count;
    }
}

在这个代码中,Flyweight是享元类,它有一个内在状态intrinsicState,这个状态是不变的。FlyweightFactory是享元工厂类,它维护了一个享元对象的集合。

The following is an example of using this pattern:

class Program
{
    static void Main(string[] args)
    {
        FlyweightFactory factory = new FlyweightFactory();

        Flyweight flyweightA = factory.GetFlyweight("A");
        flyweightA.Operation("A operation");

        Flyweight flyweightB = factory.GetFlyweight("B");
        flyweightB.Operation("B operation");

        Flyweight flyweightC = factory.GetFlyweight("A");
        flyweightC.Operation("C operation");

        Console.WriteLine($"Total Flyweights: {factory.GetFlyweightCount()}");

        Console.ReadLine();
    }
}

在这个例子中,我们创建了一个FlyweightFactory对象,并通过它创建了两个享元对象。注意,当我们试图创建第三个享元对象时,工厂实际上返回了第一个享元对象的引用,因为这两个对象的内在状态是相同的。

The implementation process is as follows:

  1. Create an Enjoyment Factory object.
  2. Get the objects through the Yuan-Enjoyment Factory. If the object already exists, the existing object is returned; otherwise, a new object is created.
  3. Performs the operation on the participant object.
  4. Displays the current number of shared objects.

7. Proxy mode

The proxy pattern is a structural design pattern that provides one object in place of another to control access to it. Proxy objects can act as intermediaries between clients and target objects and add other functions.

The following is a simple example of implementing the proxy pattern in C#:

// 抽象主题接口
public interface ISubject
{
    void Request();
}

// 真实主题
public class RealSubject : ISubject
{
    public void Request()
    {
        Console.WriteLine("RealSubject: Handling Request.");
    }
}

// 代理
public class Proxy : ISubject
{
    private RealSubject _realSubject;

    public Proxy(RealSubject realSubject)
    {
        this._realSubject = realSubject;
    }

    public void Request()
    {
        if (this.CheckAccess())
        {
            this._realSubject.Request();
            this.LogAccess();
        }
    }

    public bool CheckAccess()
    {
        // 检查是否有权限访问
        Console.WriteLine("Proxy: Checking access prior to firing a real request.");
        return true;
    }

    public void LogAccess()
    {
        // 记录请求
        Console.WriteLine("Proxy: Logging the time of request.");
    }
}

在这个代码中,ISubject是一个接口,定义了Request方法。RealSubject是实现了ISubject接口的类,Proxy是代理类,它也实现了ISubject接口,并持有一个RealSubject对象的引用。

The following is an example of using this pattern:

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("Client: Executing the client code with a real subject:");
        RealSubject realSubject = new RealSubject();
        realSubject.Request();

        Console.WriteLine();

        Console.WriteLine("Client: Executing the same client code with a proxy:");
        Proxy proxy = new Proxy(realSubject);
        proxy.Request();

        Console.ReadLine();
    }
}

在这个例子中,我们首先直接调用了RealSubjectRequest方法,然后我们通过代理调用了相同的方法。注意,在通过代理调用Request方法时,代理还执行了其他的操作,如检查访问权限和记录日志。

The implementation process is as follows:

  1. 创建一个真实主题对象,并直接调用其Request方法。
  2. Create a proxy object that contains a reference to the real theme.
  3. 通过代理对象调用Request方法。在这个方法中,代理首先检查访问权限,然后调用真实主题的Request方法,最后记录日志。

Behavioral model:

1. Chain of Responsibility Model

The Chain of Responsibility Pattern is a behavioral design pattern that creates a chain of receiving objects for requests. This pattern gives the requesting object more freedom and allows the request to pass along the link until an object handles it.

在这个例子中,我将创建一个Handler抽象类和两个具体的处理器ConcreteHandler1ConcreteHandler2。每个处理器都检查它是否可以处理请求,如果可以,它就处理该请求。如果不行,它就将请求传递给链中的下一个处理器。

public abstract class Handler
{
    protected Handler successor;

    public void SetSuccessor(Handler successor)
    {
        this.successor = successor;
    }

    public abstract void HandleRequest(int request);
}

public class ConcreteHandler1 : Handler
{
    public override void HandleRequest(int request)
    {
        if (request >= 0 && request < 10)
        {
            Console.WriteLine($"{this.GetType().Name} handled request {request}");
        }
        else if (successor != null)
        {
            successor.HandleRequest(request);
        }
    }
}

public class ConcreteHandler2 : Handler
{
    public override void HandleRequest(int request)
    {
        if (request >= 10 && request < 20)
        {
            Console.WriteLine($"{this.GetType().Name} handled request {request}");
        }
        else if (successor != null)
        {
            successor.HandleRequest(request);
        }
    }
}

The implementation process is as follows:

class Program
{
    static void Main(string[] args)
    {
        // 设置责任链
        Handler h1 = new ConcreteHandler1();
        Handler h2 = new ConcreteHandler2();

        h1.SetSuccessor(h2);

        // 生成并处理请求
        int[] requests = { 2, 5, 14, 22, 18, 3, 27, 20 };

        foreach (int request in requests)
        {
            h1.HandleRequest(request);
        }

        Console.ReadKey();
    }
}

When the above code is run, the following output will be printed on the console:

ConcreteHandler1 handled request 2
ConcreteHandler1 handled request 5
ConcreteHandler2 handled request 14
ConcreteHandler2 handled request 18
ConcreteHandler1 handled request 3

在这个例子中,ConcreteHandler1ConcreteHandler2都是处理器,它们决定是否处理传入的请求。如果ConcreteHandler1无法处理请求,它将请求传递给ConcreteHandler2。如果ConcreteHandler2也无法处理请求,那么请求就会被忽略。

This example shows the core idea of the chain of responsibility model, which is to create a chain of objects to process a request. In this chain, each processor determines whether to process the request. If it cannot process the request, it passes the request to the next processor in the chain.

2. Command Mode (Command)

Command Pattern is a data-driven design pattern that belongs to a behavioral pattern. In command mode, requests are encapsulated in objects as an operation or behavior. These requests are sent to the calling object, which finds the appropriate object that can process the command and delivers the command directly to the corresponding object, which executes these commands.

Here is a simple example of implementing command patterns in C#:

// 命令接口
public interface ICommand
{
    void Execute();
}

// 具体命令类
public class ConcreteCommand : ICommand
{
    private Receiver receiver;

    public ConcreteCommand(Receiver receiver)
    {
        this.receiver = receiver;
    }

    public void Execute()
    {
        receiver.Action();
    }
}

// 接收者类
public class Receiver
{
    public void Action()
    {
        Console.WriteLine("Receiver performs an action");
    }
}

// 调用者或发送者类
public class Invoker
{
    private ICommand command;

    public void SetCommand(ICommand command)
    {
        this.command = command;
    }

    public void ExecuteCommand()
    {
        command.Execute();
    }
}

在这个代码中,ICommand是命令接口,定义了Execute方法。ConcreteCommand是具体的命令类,它实现了ICommand接口,并持有一个Receiver对象的引用。Invoker是调用者或发送者类,它持有一个ICommand对象的引用,并可以通过SetCommand方法设置命令,通过ExecuteCommand方法执行命令。

The following is an example of using this pattern:

class Program
{
    static void Main(string[] args)
    {
        Receiver receiver = new Receiver();
        ICommand command = new ConcreteCommand(receiver);
        Invoker invoker = new Invoker();

        invoker.SetCommand(command);
        invoker.ExecuteCommand();

        Console.ReadLine();
    }
}

在这个例子中,我们创建了一个Receiver对象、一个ConcreteCommand对象和一个Invoker对象。然后我们通过InvokerSetCommand方法设置了命令,并通过ExecuteCommand方法执行了命令。

The implementation process is as follows:

  1. Create a recipient object.
  2. Create a concrete command object and pass the recipient object to it.
  3. Create a caller or sender object.
  4. 通过调用者对象的SetCommand方法设置命令。
  5. 通过调用者对象的ExecuteCommand方法执行命令。

3. Interpreter pattern (Interpreter)

The Interpreter Pattern is a behavioral design pattern used to address the needs of some fixed syntax formats. It defines how syntax is represented and parsed in the language.

Here is a simple example of implementing the interpreter pattern in C#:

// 抽象表达式
public interface IExpression
{
    bool Interpret(string context);
}

// 终结符表达式
public class TerminalExpression : IExpression
{
    private string data;

    public TerminalExpression(string data)
    {
        this.data = data;
    }

    public bool Interpret(string context)
    {
        if (context.Contains(data))
        {
            return true;
        }

        return false;
    }
}

// 非终结符表达式
public class OrExpression : IExpression
{
    private IExpression expr1 = null;
    private IExpression expr2 = null;

    public OrExpression(IExpression expr1, IExpression expr2)
    {
        this.expr1 = expr1;
        this.expr2 = expr2;
    }

    public bool Interpret(string context)
    {
        return expr1.Interpret(context) || expr2.Interpret(context);
    }
}

在这个代码中,IExpression是抽象表达式,定义了Interpret方法。TerminalExpression是终结符表达式,它实现了IExpression接口。OrExpression是非终结符表达式,它也实现了IExpression接口。

The following is an example of using this pattern:

class Program
{
    static void Main(string[] args)
    {
        IExpression isMale = GetMaleExpression();
        IExpression isMarriedWoman = GetMarriedWomanExpression();

        Console.WriteLine($"John is male? {isMale.Interpret("John")}");
        Console.WriteLine($"Julie is a married women? {isMarriedWoman.Interpret("Married Julie")}");

        Console.ReadLine();
    }

    // 规则:Robert 和 John 是男性
    public static IExpression GetMaleExpression()
    {
        IExpression robert = new TerminalExpression("Robert");
        IExpression john = new TerminalExpression("John");
        return new OrExpression(robert, john);
    }

    // 规则:Julie 是一个已婚的女性
    public static IExpression GetMarriedWomanExpression()
    {
        IExpression julie = new TerminalExpression("Julie");
        IExpression married = new TerminalExpression("Married");
        return new OrExpression(julie, married);
    }
}

In this example, we define two rules,"Robert and John are men" and "Julie is a married woman." We then created two expression objects that represented each of the two rules, and used these two objects to parse the input.

The implementation process is as follows:

  1. Create terminal expression objects and non-terminal expression objects to represent rules.
  2. 调用表达式对象的Interpret方法,解析输入的字符串。
  3. Output the parsing result.

4. Iterator mode

The Iterator Pattern is a behavioral design pattern that provides a way to access elements of an object without exposing the internal representation of the object. Here is a simple example of implementing the iterator pattern in C#:

// 抽象聚合类
public interface IAggregate
{
    IIterator CreateIterator();
    void Add(string item);
    int Count { get; }
    string this[int index] { get; set; }
}

// 具体聚合类
public class ConcreteAggregate : IAggregate
{
    private List<string> items = new List<string>();

    public IIterator CreateIterator()
    {
        return new ConcreteIterator(this);
    }

    public int Count
    {
        get { return items.Count; }
    }

    public string this[int index]
    {
        get { return items[index]; }
        set { items.Insert(index, value); }
    }

    public void Add(string item)
    {
        items.Add(item);
    }
}

// 抽象迭代器
public interface IIterator
{
    string First();
    string Next();
    bool IsDone { get; }
    string CurrentItem { get; }
}

// 具体迭代器
public class ConcreteIterator : IIterator
{
    private ConcreteAggregate aggregate;
    private int current = 0;

    public ConcreteIterator(ConcreteAggregate aggregate)
    {
        this.aggregate = aggregate;
    }

    public string First()
    {
        return aggregate[0];
    }

    public string Next()
    {
        string ret = null;
        if (current < aggregate.Count - 1)
        {
            ret = aggregate[++current];
        }

        return ret;
    }

    public string CurrentItem
    {
        get { return aggregate[current]; }
    }

    public bool IsDone
    {
        get { return current >= aggregate.Count; }
    }
}

在这个代码中,IAggregate是抽象聚合类,定义了CreateIterator等方法,ConcreteAggregate是具体聚合类,实现了IAggregate接口。IIterator是抽象迭代器,定义了FirstNext等方法,ConcreteIterator是具体迭代器,实现了IIterator接口。

The following is an example of using this pattern:

class Program
{
    static void Main(string[] args)
    {
        IAggregate aggregate = new ConcreteAggregate();
        aggregate.Add("Item A");
        aggregate.Add("Item B");
        aggregate.Add("Item C");
        aggregate.Add("Item D");

        IIterator iterator = aggregate.CreateIterator();

        Console.WriteLine("Iterating over collection:");

        string item = iterator.First();
        while (item != null)
        {
            Console.WriteLine(item);
            item = iterator.Next();
        }

        Console.ReadLine();
    }
}

在这个例子中,我们创建了一个ConcreteAggregate对象,并添加了几个元素。然后我们通过CreateIterator方法创建了一个迭代器,并使用这个迭代器遍历了集合中的所有元素。

The implementation process is as follows:

  1. Create an aggregate object and add some elements.
  2. 通过聚合对象的CreateIterator方法创建一个迭代器。
  3. 通过迭代器的First方法获取第一个元素,然后通过Next方法获取后续的元素,直到获取不到元素为止。

5. Mediator Model

The mediator pattern is a behavioral design pattern that allows you to reduce complex communication between a set of objects. It provides a mediator object that is responsible for communicating between objects in the group, rather than communicating directly with those objects.

First, let's define a mediator interface and a specific mediator:

// Mediator 接口声明了与组件交互的方法。
public interface IMediator
{
    void Notify(object sender, string ev);
}

// 具体 Mediators 实现协作行为,它负责协调多个组件。
public class ConcreteMediator : IMediator
{
    private Component1 _component1;
    private Component2 _component2;

    public ConcreteMediator(Component1 component1, Component2 component2)
    {
        _component1 = component1;
        _component1.SetMediator(this);
        _component2 = component2;
        _component2.SetMediator(this);
    }

    public void Notify(object sender, string ev)
    {
        if (ev == "A")
        {
            Console.WriteLine("Mediator reacts on A and triggers following operations:");
            this._component2.DoC();
        }
        if (ev == "D")
        {
            Console.WriteLine("Mediator reacts on D and triggers following operations:");
            this._component1.DoB();
            this._component2.DoC();
        }
    }
}

Next, we define a basic component class and two specific components:

public abstract class BaseComponent
{
    protected IMediator _mediator;

    public BaseComponent(IMediator mediator = null)
    {
        _mediator = mediator;
    }

    public void SetMediator(IMediator mediator)
    {
        this._mediator = mediator;
    }
}

// 具体 Components 实现各种功能。它们不依赖于其他组件。
// 它们也不依赖于任何具体 Mediator 类。
public class Component1 : BaseComponent
{
    public void DoA()
    {
        Console.WriteLine("Component 1 does A.");
        this._mediator.Notify(this, "A");
    }

    public void DoB()
    {
        Console.WriteLine("Component 1 does B.");
        this._mediator.Notify(this, "B");
    }
}

public class Component2 : BaseComponent
{
    public void DoC()
    {
        Console.WriteLine("Component 2 does C.");
        this._mediator.Notify(this, "C");
    }

    public void DoD()
    {
        Console.WriteLine("Component 2 does D.");
        this._mediator.Notify(this, "D");
    }
}

Finally, let's create a client-side code:

class Program
{
    static void Main(string[] args)
    {
        // The client code.
        Component1 component1 = new Component1();
        Component2 component2 = new Component2();
        new ConcreteMediator(component1, component2);

        Console.WriteLine("Client triggers operation A.");
        component1.DoA();

        Console.WriteLine();

        Console.WriteLine("Client triggers operation D.");
        component2.DoD();
    }
}

The components in this example communicate through intermediaries rather than directly, which reduces dependencies between components and makes them easier to modify independently. When a component has an event (e.g.,"Component 1 does A"), it notifies other components through the mediator, so that other components can respond based on the event (e.g.,"Component 2 does C").

6. Memento model (Memento)

The memo pattern is a behavioral design pattern that saves the state of an object so that it can be restored later. In most cases, this mode allows you to save and restore the historical state of an object without destroying the object encapsulation.

以下是一个简单的备忘录模式的实现,其中有三个主要的类:Originator(保存了一个重要的状态,这个状态可能会随着时间改变),Memento(保存了Originator的一个快照,这个快照包含了Originator的状态),以及Caretaker(负责保存Memento)。

// Originator 类可以生成一个备忘录,并且可以通过备忘录恢复其状态。
public class Originator
{
    private string _state;

    public Originator(string state)
    {
        this._state = state;
        Console.WriteLine($"Originator: My initial state is: {_state}");
    }

    public void DoSomething()
    {
        Console.WriteLine("Originator: I'm doing something important.");
        _state = GenerateRandomString(30);
        Console.WriteLine($"Originator: and my state has changed to: {_state}");
    }

    private string GenerateRandomString(int length = 10)
    {
        string allowedSymbols = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
        string result = string.Empty;

        while (length > 0)
        {
            result += allowedSymbols[new Random().Next(0, allowedSymbols.Length)];

            length--;
        }

        return result;
    }

    public IMemento Save()
    {
        return new ConcreteMemento(_state);
    }

    public void Restore(IMemento memento)
    {
        _state = memento.GetState();
        Console.WriteLine($"Originator: My state has changed to: {_state}");
    }
}

// 备忘录接口提供了获取备忘录和原发器状态的方法。但在该接口中并未声明所有的方法,一些方法只在原发器中声明。
public interface IMemento
{
    string GetName();

    string GetState();

    DateTime GetDate();
}

// Concrete Memento 存储原发器状态,并通过原发器实现备份。备忘录是不可变的,因此,没有 set 方法。
public class ConcreteMemento : IMemento
{
    private string _state;
    private DateTime _date;

    public ConcreteMemento(string state)
    {
        _state = state;
        _date = DateTime.Now;
    }

    public string GetState()
    {
        return _state;
    }

    public string GetName()
    {
        return $"{_date} / ({_state.Substring(0, 9)})...";
    }

    public DateTime GetDate()
    {
        return _date;
    }
}

// Caretaker 不依赖于具体备忘录类。结果,它不会有任何访问原发器状态的权利,它只能获取备忘录的元数据。
public class Caretaker
{
    private List<IMemento> _mementos = new List<IMemento>();
    private Originator _originator = null;

    public Caretaker(Originator originator)
    {
        this._originator = originator;
    }

    public void Backup()
    {
        Console.WriteLine("\nCaretaker: Saving Originator's state...");
        _mementos.Add(_originator.Save());
    }

    public void Undo()
    {
        if (_mementos.Count == 0)
        {
            return;
        }

        var memento = _mementos.Last();
        _mementos.Remove(memento);

        Console.WriteLine("Caretaker: Restoring state to: " + memento.GetName());
        try
        {
            _originator.Restore(memento);
        }
        catch (Exception)
        {
            Undo();
        }
    }

    public void ShowHistory()
    {
        Console.WriteLine("Caretaker: Here's the list of mementos:");

        foreach (var memento in _mementos)
        {
            Console.WriteLine(memento.GetName());
        }
    }
}

// 客户端代码
class Program
{
    static void Main(string[] args)
    {
        Originator originator = new Originator("Super-duper-super-puper-super.");
        Caretaker caretaker = new Caretaker(originator);

        caretaker.Backup();
        originator.DoSomething();

        caretaker.Backup();
        originator.DoSomething();

        caretaker.Backup();
        originator.DoSomething();

        Console.WriteLine();
        caretaker.ShowHistory();

        Console.WriteLine("\nClient: Now, let's rollback!\n");
        caretaker.Undo();

        Console.WriteLine("\nClient: Once more!\n");
        caretaker.Undo();
    }
}

In the above code, the Originator holds some important states and provides methods to save and restore its state to and from a memo object. Caretaker is responsible for saving the memo, but it cannot manipulate the state in the memo object. When a user performs an action, we first save the current state and then perform the action. If users are later dissatisfied with the new status, they can easily restore the status from the old memo.

7. Observer mode

Observer Pattern is a behavioral design pattern. When the state of an object changes, all objects that depend on it are notified and automatically updated. Here is a simple example of implementing Observer Mode in C#:

// 抽象观察者
public interface IObserver
{
    void Update();
}

// 具体观察者
public class ConcreteObserver : IObserver
{
    private string name;

    public ConcreteObserver(string name)
    {
        this.name = name;
    }

    public void Update()
    {
        Console.WriteLine($"{name} received an update!");
    }
}

// 抽象主题
public interface ISubject
{
    void RegisterObserver(IObserver observer);
    void RemoveObserver(IObserver observer);
    void NotifyObservers();
}

// 具体主题
public class ConcreteSubject : ISubject
{
    private List<IObserver> observers = new List<IObserver>();

    public void RegisterObserver(IObserver observer)
    {
        observers.Add(observer);
    }

    public void RemoveObserver(IObserver observer)
    {
        if (observers.Contains(observer))
        {
            observers.Remove(observer);
        }
    }

    public void NotifyObservers()
    {
        foreach (var observer in observers)
        {
            observer.Update();
        }
    }

    public void ChangeState()
    {
        // 触发状态变化,通知所有观察者
        NotifyObservers();
    }
}

在这个代码中,IObserver是抽象观察者,定义了Update方法,ConcreteObserver是具体观察者,实现了IObserver接口。ISubject是抽象主题,定义了RegisterObserverRemoveObserverNotifyObservers方法,ConcreteSubject是具体主题,实现了ISubject接口。

The following is an example of using this pattern:

class Program
{
    static void Main(string[] args)
    {
        ConcreteSubject subject = new ConcreteSubject();

        subject.RegisterObserver(new ConcreteObserver("Observer 1"));
        subject.RegisterObserver(new ConcreteObserver("Observer 2"));
        subject.RegisterObserver(new ConcreteObserver("Observer 3"));

        subject.ChangeState();

        Console.ReadLine();
    }
}

在这个例子中,我们创建了一个ConcreteSubject对象,并注册了三个观察者。然后我们通过ChangeState方法改变了主题的状态,这会触发主题通知所有观察者。

The implementation process is as follows:

  1. Create a specific theme object.
  2. 创建几个具体观察者对象,并通过主题的RegisterObserver方法将这些观察者注册到主题中。
  3. 通过主题的ChangeState方法改变主题的状态,这会触发主题通知所有观察者。

8. State Mode (State)

State pattern In object-oriented programming, a design pattern that allows an object to change its behavior when its internal state changes. This type of design pattern is behavioral. In state mode, we create objects that represent various states, as well as a context object whose behavior changes as the state changes.

The following is an example of a state pattern. In this example, we will create a bank account with two states: Normal State and OverdrawnState. When a user performs an action (deposits and withdrawals), the account status will change accordingly.

First, we define an interface that represents state:

public interface IAccountState
{
    void Deposit(Action addToBalance);
    void Withdraw(Action subtractFromBalance);
    void ComputeInterest();
}

We then create two classes that represent specific states:

public class NormalState : IAccountState
{
    public void Deposit(Action addToBalance)
    {
        addToBalance();
        Console.WriteLine("Deposit in NormalState");
    }

    public void Withdraw(Action subtractFromBalance)
    {
        subtractFromBalance();
        Console.WriteLine("Withdraw in NormalState");
    }

    public void ComputeInterest()
    {
        Console.WriteLine("Interest computed in NormalState");
    }
}

public class OverdrawnState : IAccountState
{
    public void Deposit(Action addToBalance)
    {
        addToBalance();
        Console.WriteLine("Deposit in OverdrawnState");
    }

    public void Withdraw(Action subtractFromBalance)
    {
        Console.WriteLine("No withdraw in OverdrawnState");
    }

    public void ComputeInterest()
    {
        Console.WriteLine("Interest and fees computed in OverdrawnState");
    }
}

We then create a Context class that uses these states to perform its tasks:

public class BankAccount
{
    private IAccountState _state;
    private double _balance;

    public BankAccount(IAccountState state)
    {
        _state = state;
        _balance = 0;
    }

    public void Deposit(double amount)
    {
        _state.Deposit(() => _balance += amount);
        StateChangeCheck();
    }

    public void Withdraw(double amount)
    {
        _state.Withdraw(() => _balance -= amount);
        StateChangeCheck();
    }

    public void ComputeInterest()
    {
        _state.ComputeInterest();
    }

    private void StateChangeCheck()
    {
        if (_balance < 0.0)
            _state = new OverdrawnState();
        else
            _state = new NormalState();
    }
}

Now, you can create an instance and run a Demo to test the code for this state pattern:

public class Program
{
    public static void Main(string[] args)
    {
        var account = new BankAccount(new NormalState());

        account.Deposit(1000); // Deposit in NormalState
        account.Withdraw(2000); // Withdraw in NormalState; No withdraw in OverdrawnState
        account.Deposit(100); // Deposit in OverdrawnState

        account.ComputeInterest(); // Interest and fees computed in OverdrawnState

        Console.ReadKey();
    }
}

This program first performs a deposit operation under normal conditions, and then attempts to withdraw money. Since the withdrawal amount exceeded the account balance, the account entered an overdraft status and further withdrawal operations were blocked. But deposits are still allowed to return the account to normal. The behavior of calculating interest also changes depending on the status of the account.

9. Strategy model

The policy pattern defines a series of algorithms and encapsulates each algorithm so that they can be replaced with each other. The policy model allows the algorithm to change independently of the customers using it.

Here is a simple C#implementation of a policy pattern. In this example, we will create a sorting strategy, such as Quick Sort and Bubble Sort, that implement the same interface, and then create a Context class that uses these strategies to perform sorting operations.

First, we define an interface that represents the sorting strategy:

public interface ISortStrategy
{
    void Sort(List<int> list);
}

We then create two classes that represent specific policies:

public class QuickSort : ISortStrategy
{
    public void Sort(List<int> list)
    {
        list.Sort();  // Quick sort is in-place but here we are using built-in method
        Console.WriteLine("QuickSorted list ");
    }
}

public class BubbleSort : ISortStrategy
{
    public void Sort(List<int> list)
    {
        int n = list.Count;
        for (int i = 0; i < n - 1; i++)
            for (int j = 0; j < n - i - 1; j++)
                if (list[j] > list[j + 1])
                {
                    // swap temp and list[i]
                    int temp = list[j];
                    list[j] = list[j + 1];
                    list[j + 1] = temp;
                }

        Console.WriteLine("BubbleSorted list ");
    }
}

We then create a Context class that uses these policies to perform its tasks:

public class SortedList
{
    private List<int> _list = new List<int>();
    private ISortStrategy _sortstrategy;

    public void SetSortStrategy(ISortStrategy sortstrategy)
    {
        this._sortstrategy = sortstrategy;
    }

    public void Add(int num)
    {
        _list.Add(num);
    }

    public void Sort()
    {
        _sortstrategy.Sort(_list);

        // Print sorted list
        foreach (int num in _list)
        {
            Console.Write(num + " ");
        }
        Console.WriteLine();
    }
}

Now you can create an instance and run a Demo to test the code for this policy pattern:

public class Program
{
    public static void Main(string[] args)
    {
        SortedList sortedList = new SortedList();

        sortedList.Add(1);
        sortedList.Add(5);
        sortedList.Add(3);
        sortedList.Add(4);
        sortedList.Add(2);

        sortedList.SetSortStrategy(new QuickSort());
        sortedList.Sort();  // Output: QuickSorted list 1 2 3 4 5

        sortedList.SetSortStrategy(new BubbleSort());
        sortedList.Sort();  // Output: BubbleSorted list 1 2 3 4 5

        Console.ReadKey();
    }
}

The program first creates an unsorted list, and then it sorts it first using the quick sort strategy, and then using the bubble sort strategy.

10. Template Method Pattern

The template method pattern defines the skeleton of an algorithm in an operation, postponing these steps to subclasses. The template approach allows subclasses to redefine certain steps of the algorithm without changing the structure of the algorithm.

The following is an example of a template method pattern. In this example, we will create a process for cooking food that has certain fixed steps (e.g. preparing materials, cleaning), but the specific cooking steps depend on the specific food.

First, we define an abstract template class:

public abstract class CookingProcedure
{
    // The 'Template method'
    public void PrepareDish()
    {
        PrepareIngredients();
        Cook();
        CleanUp();
    }

    public void PrepareIngredients()
    {
        Console.WriteLine("Preparing the ingredients...");
    }

    // These methods will be overridden by subclasses
    public abstract void Cook();

    public void CleanUp()
    {
        Console.WriteLine("Cleaning up...");
    }
}

We then create two specific subclasses that implement specific cooking steps:

public class CookPasta : CookingProcedure
{
    public override void Cook()
    {
        Console.WriteLine("Cooking pasta...");
    }
}

public class BakeCake : CookingProcedure
{
    public override void Cook()
    {
        Console.WriteLine("Baking cake...");
    }
}

Now, you can create an instance and run a Demo to test the code for this template method pattern:

public class Program
{
    public static void Main(string[] args)
    {
        CookingProcedure cookingProcedure = new CookPasta();
        cookingProcedure.PrepareDish();

        Console.WriteLine();

        cookingProcedure = new BakeCake();
        cookingProcedure.PrepareDish();

        Console.ReadKey();
    }
}

在这个程序中,我们首先创建了一个CookPasta对象,然后调用其PrepareDish方法。然后,我们创建了一个BakeCake对象,再次调用其PrepareDish方法。这两个对象虽然具有不同的Cook方法,但是它们的PrepareDish方法的结构(即算法的骨架)是相同的。

11. Visitor Mode (Visitor)

Visitor Pattern is a software design pattern that separates algorithms from object structure. The basic idea of this model is to change the operation of elements through so-called "visitors." In this way, the class of the element can be used to represent the structure of the element, and the specific operations can be defined in the visitor class.

The following is an example of a visitor pattern implemented using C#, including detailed annotations and execution processes.

There are three main parts in this example: visitor (IVisitor), accessible element (IElement), and element structure (ObjectStructure). There are both concrete visitors (ConcreteVisitor) and concrete elements (ConcreteElement).

// 访问者接口
public interface IVisitor
{
    void VisitConcreteElementA(ConcreteElementA concreteElementA);
    void VisitConcreteElementB(ConcreteElementB concreteElementB);
}

// 具体访问者A
public class ConcreteVisitorA : IVisitor
{
    public void VisitConcreteElementA(ConcreteElementA concreteElementA)
    {
        Console.WriteLine($"{concreteElementA.GetType().Name} is being visited by {this.GetType().Name}");
    }

    public void VisitConcreteElementB(ConcreteElementB concreteElementB)
    {
        Console.WriteLine($"{concreteElementB.GetType().Name} is being visited by {this.GetType().Name}");
    }
}

// 具体访问者B
public class ConcreteVisitorB : IVisitor
{
    public void VisitConcreteElementA(ConcreteElementA concreteElementA)
    {
        Console.WriteLine($"{concreteElementA.GetType().Name} is being visited by {this.GetType().Name}");
    }

    public void VisitConcreteElementB(ConcreteElementB concreteElementB)
    {
        Console.WriteLine($"{concreteElementB.GetType().Name} is being visited by {this.GetType().Name}");
    }
}

// 元素接口
public interface IElement
{
    void Accept(IVisitor visitor);
}

// 具体元素A
public class ConcreteElementA : IElement
{
    public void Accept(IVisitor visitor)
    {
        visitor.VisitConcreteElementA(this);
    }
}

// 具体元素B
public class ConcreteElementB : IElement
{
    public void Accept(IVisitor visitor)
    {
        visitor.VisitConcreteElementB(this);
    }
}

// 对象结构
public class ObjectStructure
{
    private List<IElement> _elements = new List<IElement>();

    public void Attach(IElement element)
    {
        _elements.Add(element);
    }

    public void Detach(IElement element)
    {
        _elements.Remove(element);
    }

    public void Accept(IVisitor visitor)
    {
        foreach (var element in _elements)
        {
            element.Accept(visitor);
        }
    }
}

The implementation process is as follows:

  1. Create instances of concrete elements ConcreteElementA and ConcreteElementB.
  2. Create an instance of the object structure ObjectStructure and add the specific elements created in step 1 to the object structure.
  3. Create instances of specific visitors ConcreteVisitorA and ConcreteVisitorB.
  4. Call the Accept method of the object structure and pass in the specific visitor created in step 3, so that the specific visitor can access all elements in the object structure.

Here is an example of using the above code:

public class Program
{
    public static void Main()
    {
        ObjectStructure objectStructure = new ObjectStructure();

        objectStructure.Attach(new ConcreteElementA());
        objectStructure.Attach(new ConcreteElementB());

        ConcreteVisitorA visitorA = new ConcreteVisitorA();
        ConcreteVisitorB visitorB = new ConcreteVisitorB();

        objectStructure.Accept(visitorA);
        objectStructure.Accept(visitorB);

        Console.ReadKey();
    }
}

This program prints out information about visitor A and visitor B accessing specific element A and specific element B, respectively.

technical exchanges

NET Core Exchange Group: 737776595

Sharing from token

Keep Exploring

延伸阅读

更多文章
同分类 / 同标签 4/22/2026

Support for. NET by operating system versions (250707 update)

Use virtual machines and test machines to test the support of each version of the operating system for. NET. After installing the operating system, it is passed by measuring the corresponding running time of the installation and being able to run the Stardust Agent.

继续阅读
同分类 / 同标签 2/7/2026

Summary of experience in using AOT

From the very beginning of project creation, you should develop a good habit of conducting AOT release testing in a timely manner whenever new features are added or newer syntax is used.

继续阅读