本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:C#作为一种面向对象的编程语言,在Windows、Web、游戏开发中广泛应用。设计模式作为软件工程中的经验总结,是解决软件设计问题的标准化方案。在C#中,23种设计模式分为创建型、结构型、行为型三大类,并在实际开发中具有广泛应用。本教程详细探讨了这些设计模式在C#中的实现,帮助开发者编写出更加灵活、可维护和可扩展的代码。

1. C#编程语言概述

C#(发音为 “C Sharp”)是一种由微软开发的现代、类型安全的面向对象编程语言。它既继承了C和C++的语法特点,也融入了Visual Basic的快速开发特性。自2000年首次发布以来,C#已经成为.NET框架的主要开发语言,并且随着.NET Core的推出,C#已经变得更加灵活、跨平台。

C#是一种强类型语言,提供了丰富的数据类型和类型推断能力,使得开发过程既安全又高效。它的内存管理主要基于自动垃圾回收机制,这大大简化了内存管理的复杂性,降低了开发者的工作负担。除了基础的语法,C#还支持许多高级特性,比如泛型、Lambda表达式、LINQ查询语言、异步编程模型等。

在.NET平台的支持下,C#可以用来构建各种类型的应用程序,从常见的Windows桌面应用、Web应用,到跨平台的移动应用、游戏、云服务和微服务架构。随着每个新版本的发布,C#不断添加新特性和改进,致力于保持语言的活力和生产力。

// C# 示例代码:Hello World
using System;

namespace HelloWorld
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello, World!");
        }
    }
}

上述代码展示了C#的一个基础示例,其中包含了主程序入口和打印输出语句,体现了C#作为一门现代编程语言的简洁和直观。

2. 设计模式的分类介绍

2.1 设计模式的基本概念

2.1.1 设计模式的定义

设计模式是一套被反复使用、多数人知晓、经过分类编目、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。模式不是代码本身,而是某些代码的模板。一个模式通常包含三个方面:模式名称、问题、解决方案和效果。本质上,设计模式是在特定的上下文中,针对某一常见问题的通用的、可复用的设计解决方案。

设计模式通常包括以下四个基本要素:模式名称、问题、解决方案和效果。

  • 模式名称 :用于描述上下文、问题和解决方案的共性,便于在交流和文档中引用。
  • 问题 :描述了应该在何时使用模式,它会解释设计问题和问题存在的前因后果。
  • 解决方案 :描述了设计的组成成分,它们之间的相互关系及各自的职责和协作方式。解决方案的描述着重于结构和机制,而不描述特定的实现细节。
  • 效果 :描述了模式应用的效果及使用模式应权衡的问题。尽管描述设计决策时,并不总提到模式效果,但它们对于评价设计选择和理解使用模式的代价及好处具有重要意义。

2.1.2 设计模式的重要性

设计模式不仅仅是一些具体的代码实现,它们代表了软件设计领域中一些经过验证的最佳实践。掌握设计模式对软件工程师来说至关重要,原因如下:

  • 重用性 :设计模式使得你能够重用代码,而不是重新编写。这意味着你可以避免重复发明轮子,并在现有基础上快速搭建新的系统。
  • 可维护性 :好的设计模式可以增加代码的可维护性,因为它们遵循可预测的结构,使得代码更易于理解和修改。
  • 可扩展性 :设计模式帮助你构建出易于扩展的系统,因此你可以随着需求的变化添加新功能,而不必推翻整个系统。
  • 解耦 :设计模式有助于降低系统组件之间的耦合度,这样可以使得模块间协作更高效,同时也降低了解决问题的复杂性。
  • 优化性能 :某些设计模式比如享元模式可以优化应用程序的性能,特别是在资源受限或者需要大量对象的系统中。

2.2 设计模式的分类方法

2.2.1 创建型模式

创建型模式关注对象创建过程,它们提供创建对象的最佳方式,主要解决对象创建问题,如对象的创建时机、创建方式、创建者是谁等问题。

  • 单例模式 :确保一个类只有一个实例,并提供一个全局访问点。
  • 工厂方法模式 :定义一个用于创建对象的接口,让子类决定实例化哪一个类。
  • 抽象工厂模式 :提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类。
  • 建造者模式 :将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
  • 原型模式 :用于创建重复的对象,同时又能保证性能。

2.2.2 结构型模式

结构型模式涉及如何组合类和对象以获得更大的结构。在解决设计问题时,它们主要关注类和对象的组合。

  • 适配器模式 :将一个类的接口转换成客户期望的另一个接口,使得原本由于接口不兼容不能在一起工作的那些类可以一起工作。
  • 桥接模式 :将抽象部分与它的实现部分分离,使它们都可以独立地变化。
  • 组合模式 :将对象组合成树形结构以表示“部分-整体”的层次结构,使得用户对单个对象和组合对象的使用具有一致性。
  • 装饰器模式 :动态地给一个对象添加一些额外的职责,就增加功能来说,装饰器模式比生成子类更为灵活。
  • 外观模式 :为子系统中的一组接口提供一个统一的接口,外观定义了一个高层接口,让子系统更容易使用。
  • 享元模式 :运用共享技术有效地支持大量细粒度的对象。
  • 代理模式 :为其他对象提供一种代理以控制对这个对象的访问。

2.2.3 行为型模式

行为型模式关注对象之间的通信问题,涉及算法和对象间职责的分配。

  • 责任链模式 :使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。
  • 命令模式 :将请求封装为一个对象,从而可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。
  • 解释器模式 :给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。
  • 迭代器模式 :提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露其内部的表示。
  • 中介者模式 :用一个中介对象来封装一系列的对象交互,中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。
  • 备忘录模式 :在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。
  • 观察者模式 :定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
  • 状态模式 :允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类。
  • 策略模式 :定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换。本模式使得算法可独立于使用它的客户而变化。
  • 模板方法模式 :在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中,使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
  • 访问者模式 :表示一个作用于某对象结构中的各元素的操作,它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。

2.3 设计模式的原则

2.3.1 单一职责原则

单一职责原则(Single Responsibility Principle, SRP)规定一个类应该只有一个引起它变化的原因。换言之,一个类应该只有一个职责。这个原则提供了一个清晰的职责定义:如果一个类承担的职责过多,就等于把这些职责耦合在一起,一个职责的变化可能会削弱或者抑制这个类完成其它职责的能力。

2.3.2 开闭原则

开闭原则(Open/Closed Principle, OCP)要求软件实体应当对扩展开放,对修改关闭。这意味着一个实体是允许在不改变它的源代码的前提下变更它的行为。开闭原则是面向对象设计中最重要的原则之一。遵循这个原则可以提高系统的可复用性,增加新的功能可以不需要修改现有的代码,而是通过扩展来实现。

2.3.3 里氏替换原则

里氏替换原则(Liskov Substitution Principle, LSP)是面向对象设计的基本原则之一。里氏替换原则表明:如果对每一个类型为S的对象o1,都有类型为T的对象o2,使得以T定义的所有程序P在所有的对象o1代换o2时,程序P的行为没有变化,那么类型S是类型T的子类型。这个概念可能听起来有些复杂,但简单地说,里氏替换原则就是要确保子类型可以替换掉它们的父类型。

2.3.4 依赖倒置原则

依赖倒置原则(Dependence Inversion Principle, DIP)要求高层模块不应该依赖低层模块,两者都应该依赖其抽象;抽象不应该依赖细节,细节应该依赖抽象。这个原则的意思是说,应该针对接口编程,而不是针对实现编程。这个原则可以减少类间的耦合性,提高系统的稳定性,提高代码的可读性和可维护性,并且能够减少后续维护的工作量。

下一章将深入探讨创建型设计模式的C#实现。我们将逐个分析各种模式的概念、应用场景以及在C#中的实现方法。

3. 创建型设计模式的C#实现

3.1 单例模式

3.1.1 单例模式的概念

单例模式(Singleton Pattern)是一种创建型设计模式,用于确保一个类只有一个实例,并提供一个全局访问点来获取这个实例。该模式在需要一个全局访问点,同时又不希望创建多个实例,以免消耗过多资源或引发状态不一致问题时十分有用。

3.1.2 单例模式的C#实现

单例模式通常通过以下几种方式在C#中实现:

  • 饿汉式(Eager Initialization):在类加载时就完成了初始化,依赖于JVM在类加载时互斥的特性来保证线程安全。
  • 懒汉式(Lazy Initialization):在第一次使用实例时进行初始化。考虑线程安全问题,通常需要通过双重检查锁定(Double-Checked Locking)来实现。

以下是饿汉式单例模式的一个简单实现示例:

public sealed class Singleton
{
    private static readonly Singleton instance = new Singleton();

    // 私有构造函数,确保外部不能通过new来创建实例
    private Singleton() { }

    // 提供一个全局访问点
    public static Singleton Instance
    {
        get
        {
            return instance;
        }
    }
}

在上面的代码中,私有静态变量 instance 负责存储唯一实例。 Singleton 类的构造函数是私有的,这意味着不能通过外部代码使用 new 关键字来创建 Singleton 类的实例。外部唯一获取实例的方式是通过公共的静态属性 Instance

在多线程环境下,饿汉式单例模式可能是线程安全的,因为 static 变量的初始化是由JVM在类加载时完成的,并且JVM确保了在一个时间点只会有一个线程加载指定的类。因此,该方式是线程安全的,但需要注意的是,在某些情况下,类加载的时机可能会影响性能和资源的使用。

3.2 工厂方法模式

3.2.1 工厂方法模式的概念

工厂方法模式(Factory Method Pattern)是一种创建型设计模式,它定义了一个创建对象的接口,但让子类决定实例化哪一个类。工厂方法把实例化的工作推迟到子类,使得创建过程更加灵活和可扩展。

3.2.2 工厂方法模式的C#实现

工厂方法模式通常包含以下四个角色:

  • 抽象产品(Product):定义产品的接口。
  • 具体产品(Concrete Product):实现了抽象产品接口的具体产品类。
  • 抽象工厂(Creator):声明返回抽象产品的方法,该方法通常返回一个产品类型的实例。
  • 具体工厂(Concrete Creator):实现了抽象工厂接口,重写返回具体产品实例的方法。

下面是一个工厂方法模式的简单实现:

// 抽象产品接口
public interface IProduct
{
    void Operation();
}

// 具体产品实现A
public class ConcreteProductA : IProduct
{
    public void Operation()
    {
        // 实现具体操作
    }
}

// 具体产品实现B
public class ConcreteProductB : IProduct
{
    public void Operation()
    {
        // 实现具体操作
    }
}

// 抽象工厂
public abstract class Creator
{
    // 声明返回产品类型的工厂方法
    public abstract IProduct FactoryMethod();
}

// 具体工厂A
public class ConcreteCreatorA : Creator
{
    public override IProduct FactoryMethod()
    {
        return new ConcreteProductA();
    }
}

// 具体工厂B
public class ConcreteCreatorB : Creator
{
    public override IProduct FactoryMethod()
    {
        return new ConcreteProductB();
    }
}

在上述代码中, Creator 类定义了一个工厂方法 FactoryMethod ,该方法返回一个 IProduct 类型对象。具体工厂类( ConcreteCreatorA ConcreteCreatorB )覆盖了这个方法,返回了具体的产品( ConcreteProductA ConcreteProductB )。

工厂方法模式通过这种方式延迟了具体类的实例化,增加了系统的扩展性和灵活性。当需要添加新的产品类型时,只需添加新的产品类和对应的工厂类即可,无需修改现有代码。

3.3 抽象工厂模式

3.3.1 抽象工厂模式的概念

抽象工厂模式(Abstract Factory Pattern)是一种创建型设计模式,它提供了一个接口用于创建一系列相关或相互依赖的对象,而无需指定它们具体的类。抽象工厂允许客户端通过抽象接口创建一系列的产品族,使得客户端与具体实现解耦。

3.3.2 抽象工厂模式的C#实现

抽象工厂模式通常包含以下四个角色:

  • 抽象工厂(AbstractFactory):声明创建抽象产品的方法。
  • 具体工厂(ConcreteFactory):实现创建具体产品的方法。
  • 抽象产品(AbstractProduct):为一系列产品声明接口。
  • 具体产品(ConcreteProduct):实现抽象产品接口的具体产品。

下面是一个抽象工厂模式的实现示例:

// 抽象产品A
public interface IAbstractProductA
{
    void OperationA();
}

// 抽象产品B
public interface IAbstractProductB
{
    void OperationB();
}

// 具体产品A1
public class ConcreteProductA1 : IAbstractProductA
{
    public void OperationA()
    {
        // 实现操作A1
    }
}

// 具体产品A2
public class ConcreteProductA2 : IAbstractProductA
{
    public void OperationA()
    {
        // 实现操作A2
    }
}

// 具体产品B1
public class ConcreteProductB1 : IAbstractProductB
{
    public void OperationB()
    {
        // 实现操作B1
    }
}

// 具体产品B2
public class ConcreteProductB2 : IAbstractProductB
{
    public void OperationB()
    {
        // 实现操作B2
    }
}

// 抽象工厂
public interface IAbstractFactory
{
    IAbstractProductA CreateProductA();
    IAbstractProductB CreateProductB();
}

// 具体工厂1
public class ConcreteFactory1 : IAbstractFactory
{
    public IAbstractProductA CreateProductA()
    {
        return new ConcreteProductA1();
    }

    public IAbstractProductB CreateProductB()
    {
        return new ConcreteProductB1();
    }
}

// 具体工厂2
public class ConcreteFactory2 : IAbstractFactory
{
    public IAbstractProductA CreateProductA()
    {
        return new ConcreteProductA2();
    }

    public IAbstractProductB CreateProductB()
    {
        return new ConcreteProductB2();
    }
}

在这个示例中,抽象工厂接口 IAbstractFactory 定义了创建两种抽象产品 IAbstractProductA IAbstractProductB 的方法。 ConcreteFactory1 ConcreteFactory2 提供了具体的实现,分别创建了两组不同的产品实例。这样,客户端代码可以通过抽象工厂接口来创建一系列相关的产品,而无需关心具体产品的实现。

抽象工厂模式特别适用于系统需要独立于其产品的创建、组合和表示时。此外,当一个产品族中的对象被设计为一起工作时,抽象工厂模式可以保证客户端代码使用到的是一致的产品集合。

4. 结构型设计模式的C#实现

结构型设计模式主要关注如何将类或对象结合在一起形成更大的结构。在本章中,将探讨四种常见的结构型设计模式及其在C#语言中的实现方式。

4.1 适配器模式

适配器模式允许将一个类的接口转换成客户期望的另一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的类可以一起工作。

4.1.1 适配器模式的概念

适配器模式通过定义一个新的接口来实现两个不兼容接口之间的兼容。这种模式涉及到一个单一的类,该类负责加入独立的或不兼容的接口功能。

4.1.2 适配器模式的C#实现

考虑一个案例,一个旧的绘图库只能绘制圆形对象,而我们需要使用该库绘制矩形对象。通过适配器模式,我们可以创建一个新的类,该类将矩形对象作为圆形对象绘制。

// Target Interface
public interface IDrawCircle
{
    void DrawCircle();
}

// Adaptee Class
public class LegacyRectangle
{
    public void DrawRectangle()
    {
        Console.WriteLine("Drawing Legacy Rectangle");
    }
}

// Adapter Class
public class RectangleToCircleAdapter : IDrawCircle
{
    private readonly LegacyRectangle _rectangle;

    public RectangleToCircleAdapter(LegacyRectangle rectangle)
    {
        this._rectangle = rectangle;
    }

    public void DrawCircle()
    {
        // Drawing rectangle as circle
        _rectangle.DrawRectangle();
    }
}

// Client Code
public class AdapterPatternDemo
{
    public void Draw(IDrawCircle circle)
    {
        circle.DrawCircle();
    }
}

// Usage
var rectangle = new LegacyRectangle();
var adapter = new RectangleToCircleAdapter(rectangle);
var demo = new AdapterPatternDemo();
demo.Draw(adapter);

在上述代码中, RectangleToCircleAdapter 类将 LegacyRectangle 类的接口转换成了 IDrawCircle 接口。这样, AdapterPatternDemo 类可以利用 RectangleToCircleAdapter 类在不知道 LegacyRectangle 类内部细节的情况下,绘制出一个“圆形”。

4.2 装饰器模式

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

4.2.1 装饰器模式的概念

装饰器模式是动态地给一个对象添加一些额外的职责。就增加功能来说,装饰器模式比生成子类更为灵活。

4.2.2 装饰器模式的C#实现

假设我们需要给 Component 类添加额外的功能,但又不想修改它的代码。我们可以创建一个 Decorator 类来实现这个需求。

// Component
public interface IComponent
{
    void Operation();
}

// Concrete Component
public class ConcreteComponent : IComponent
{
    public void Operation()
    {
        Console.WriteLine("ConcreteComponent Operation");
    }
}

// Abstract Decorator
public abstract class Decorator : IComponent
{
    protected readonly IComponent _component;

    public Decorator(IComponent component)
    {
        this._component = component;
    }

    public virtual void Operation()
    {
        _component.Operation();
    }
}

// Concrete Decorator A
public class ConcreteDecoratorA : Decorator
{
    public ConcreteDecoratorA(IComponent component) : base(component)
    {
    }

    public void AddedBehavior()
    {
        Console.WriteLine("Added Behavior A");
    }

    public override void Operation()
    {
        base.Operation();
        AddedBehavior();
    }
}

// Concrete Decorator B
public class ConcreteDecoratorB : Decorator
{
    public ConcreteDecoratorB(IComponent component) : base(component)
    {
    }

    public void AddedBehavior()
    {
        Console.WriteLine("Added Behavior B");
    }

    public override void Operation()
    {
        base.Operation();
        AddedBehavior();
    }
}

// Client Code
public class DecoratorPatternDemo
{
    public void PerformOperation(IComponent component)
    {
        component.Operation();
    }
}

// Usage
var simple = new ConcreteComponent();
var decoratorA = new ConcreteDecoratorA(simple);
var decoratorB = new ConcreteDecoratorB(decoratorA);
var demo = new DecoratorPatternDemo();
demo.PerformOperation(decoratorB);

在这个例子中,我们定义了 Decorator 类,它实现了 IComponent 接口,并包含了对组件的引用。 ConcreteDecoratorA ConcreteDecoratorB 类继承自 Decorator 并添加了额外的功能。 DecoratorPatternDemo 类的 PerformOperation 方法接受一个 IComponent 类型的对象,并调用其 Operation 方法,这样不管传递给它的对象是原始组件还是任何装饰过的组件,都能以相同的方式工作。

4.3 代理模式

代理模式为其他对象提供一种代理以控制对这个对象的访问。它是一种结构型设计模式。

4.3.1 代理模式的概念

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

4.3.2 代理模式的C#实现

考虑到在C#中,可以使用接口来实现代理模式。以下是一个简单的示例,模拟了在访问真实的数据库之前,执行一些日志记录任务的代理。

// Subject Interface
public interface IDatabase
{
    void Query();
}

// Real Subject
public class RealDatabase : IDatabase
{
    public void Query()
    {
        Console.WriteLine("Querying database.");
    }
}

// Proxy
public class DatabaseProxy : IDatabase
{
    private RealDatabase _realDatabase = new RealDatabase();
    private void LogAccess()
    {
        Console.WriteLine("Accessing database");
    }

    public void Query()
    {
        LogAccess();
        _realDatabase.Query();
    }
}

// Client Code
public class ProxyPatternDemo
{
    public void PerformQuery(IDatabase database)
    {
        database.Query();
    }
}

// Usage
var proxy = new DatabaseProxy();
var demo = new ProxyPatternDemo();
demo.PerformQuery(proxy);

在这个例子中, RealDatabase 类实现了 IDatabase 接口,并执行了实际的查询操作。 DatabaseProxy 类同样实现了 IDatabase 接口,但它在执行查询前增加了日志记录功能。 ProxyPatternDemo 类的 PerformQuery 方法接受 IDatabase 接口的实现作为参数,因此它可以使用 RealDatabase DatabaseProxy ,而不需要知道它们之间的区别。

4.4 组合模式

组合模式允许将对象组合成树形结构以表示“部分-整体”的层次结构。组合使得用户对单个对象和组合对象的使用具有一致性。

4.4.1 组合模式的概念

组合模式通过对象的组合,实现对整体和部分的统一处理。这种模式提供了一个树形结构的类层次。

4.4.2 组合模式的C#实现

假设我们需要一个图形库,其中包含组合和单独的图形元素,可以通过组合模式来实现。

// Component
public abstract class GraphicObject
{
    protected string _name;

    public GraphicObject(string name)
    {
        this._name = name;
    }

    public abstract void Draw();
}

// Leaf
public class Circle : GraphicObject
{
    public Circle(string name) : base(name)
    {
    }

    public override void Draw()
    {
        Console.WriteLine($"Drawing Circle: {_name}");
    }
}

// Composite
public class CompositeGraphic : GraphicObject
{
    private readonly List<GraphicObject> _children = new List<GraphicObject>();

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

    public void Add(GraphicObject graphic)
    {
        _children.Add(graphic);
    }

    public override void Draw()
    {
        Console.WriteLine($"Drawing {Name}");
        foreach (var child in _children)
        {
            child.Draw();
        }
    }
}

// Client Code
public class CompositePatternDemo
{
    public void Draw(GraphicsObject graphic)
    {
        graphic.Draw();
    }
}

// Usage
var circle = new Circle("Circle");
var composite = new CompositeGraphic("Group 1");
composite.Add(circle);

var demo = new CompositePatternDemo();
demo.Draw(composite);

在上述代码中, GraphicObject 是一个抽象类,它有一个抽象方法 Draw Circle 类继承自 GraphicObject ,实现了具体的 Draw 方法。 CompositeGraphic 类也继承自 GraphicObject ,但它可以包含其他的 GraphicObject 实例。 CompositePatternDemo 类的 Draw 方法接受一个 GraphicObject 类型的对象,并调用其 Draw 方法,以相同的方式处理单独的图形和组合图形。

在本章的最后,我们了解了适配器模式、装饰器模式、代理模式和组合模式的概念和C#实现。每种模式都关注于解决不同的结构问题,并提供了不同方式的灵活性来扩展和维护系统。了解这些模式能够帮助开发者更好地管理复杂系统中的类和对象之间的关系。

5. 行为型设计模式的C#实现

行为型设计模式关注对象之间的职责分配,通过创建松耦合的对象集合,使得系统更易于复用、扩展和维护。在本章中,我们将深入探讨几个重要的行为型设计模式以及它们在C#中的实现。

5.1 观察者模式

观察者模式的概念

观察者模式定义了一种一对多的依赖关系,当一个对象状态发生改变时,所有依赖于它的对象都会得到通知并自动更新。观察者模式主要由两个部分组成:主题(Subject)和观察者(Observer)。

观察者模式的C#实现

在C#中,我们可以利用事件(event)和委托(delegate)来实现观察者模式。下面是一个简单的示例:

// 定义观察者接口
public interface IObserver
{
    void Update(string message);
}

// 主题类
public class Subject
{
    // 存储观察者列表
    private List<IObserver> observers = new List<IObserver>();

    // 添加观察者
    public void Attach(IObserver observer)
    {
        observers.Add(observer);
    }

    // 移除观察者
    public void Detach(IObserver observer)
    {
        observers.Remove(observer);
    }

    // 通知所有观察者
    public void Notify(string message)
    {
        foreach (var observer in observers)
        {
            observer.Update(message);
        }
    }
}

// 实际观察者类
public class ConcreteObserver : IObserver
{
    private string name;

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

    public void Update(string message)
    {
        Console.WriteLine($"{name} received notification: {message}");
    }
}

// 使用观察者模式
public static void ObserverPatternExample()
{
    Subject subject = new Subject();

    IObserver observer1 = new ConcreteObserver("Observer1");
    IObserver observer2 = new ConcreteObserver("Observer2");

    subject.Attach(observer1);
    subject.Attach(observer2);

    subject.Notify("Hello Observers!");

    // 移除一个观察者
    subject.Detach(observer1);

    subject.Notify("Only one observer will receive this");
}

上述代码中,我们创建了一个 Subject 类来维护观察者列表,并允许观察者订阅和取消订阅。当 Subject 状态改变时,它通过调用 Notify 方法来通知所有已注册的观察者。

5.2 策略模式

策略模式的概念

策略模式定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的变化不会影响到使用算法的客户端。策略模式通常包含三个角色:策略(Strategy)、具体策略(ConcreteStrategy)和上下文(Context)。

策略模式的C#实现

策略模式的实现通常涉及定义策略接口和若干实现该接口的具体策略类。下面是策略模式的一个简单实现:

// 策略接口
public interface IStrategy
{
    void AlgorithmInterface();
}

// 具体策略A
public class ConcreteStrategyA : IStrategy
{
    public void AlgorithmInterface()
    {
        Console.WriteLine("Executing strategy A");
    }
}

// 具体策略B
public class ConcreteStrategyB : IStrategy
{
    public void AlgorithmInterface()
    {
        Console.WriteLine("Executing strategy B");
    }
}

// 上下文类
public class Context
{
    private IStrategy strategy;

    public Context(IStrategy strategy)
    {
        this.strategy = strategy;
    }

    public void ContextInterface()
    {
        strategy.AlgorithmInterface();
    }

    public void SetStrategy(IStrategy strategy)
    {
        this.strategy = strategy;
    }
}

// 使用策略模式
public static void StrategyPatternExample()
{
    Context context = new Context(new ConcreteStrategyA());
    context.ContextInterface();

    context.SetStrategy(new ConcreteStrategyB());
    context.ContextInterface();
}

上述示例中,我们定义了一个策略接口 IStrategy 和两个实现了该接口的具体策略类 ConcreteStrategyA ConcreteStrategyB 。上下文类 Context 通过接受一个策略接口类型的参数,可以自由切换内部策略的实现,这样客户端就可以使用不同的策略算法而无需关心其具体实现。

5.3 命令模式

命令模式的概念

命令模式是一种行为设计模式,它将请求封装成对象,这样可以使用不同的请求、队列或者日志请求来参数化其他对象,同时支持可撤销的操作。命令模式主要包含四个角色:调用者(Invoker)、接收者(Receiver)、命令(Command)和具体命令(ConcreteCommand)。

命令模式的C#实现

在C#中实现命令模式,通常会涉及到定义命令接口和具体命令类,并在调用者和接收者之间进行解耦。下面是一个命令模式的简单实现:

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

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

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

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

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

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

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

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

// 使用命令模式
public static void CommandPatternExample()
{
    Receiver receiver = new Receiver();
    ICommand command = new ConcreteCommand(receiver);
    Invoker invoker = new Invoker();

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

在这个例子中, Receiver 是执行实际操作的类, ConcreteCommand 封装了 Receiver 的实例,并实现了 ICommand 接口。 Invoker 负责调用命令对象的 Execute 方法。这种模式使得客户端可以要求执行不同的命令,而无需知道命令的具体实现细节。

5.4 模板方法模式

模板方法模式的概念

模板方法模式是一种行为设计模式,它在抽象类中定义了一个算法的骨架,将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法的某些步骤。

模板方法模式的C#实现

模板方法模式的实现比较简单,只需要在抽象类中定义算法的骨架,然后在子类中覆盖抽象方法。以下是一个典型的实现例子:

// 抽象类
public abstract class AbstractClass
{
    // 模板方法定义了算法的骨架
    public void TemplateMethod()
    {
        this.StepOne();
        this.StepTwo();
        this.StepThree();
    }

    // 抽象方法
    protected abstract void StepOne();
    protected abstract void StepTwo();

    // 可选的具体方法
    private void StepThree()
    {
        Console.WriteLine("Step Three implemented in AbstractClass.");
    }
}

// 具体子类
public class ConcreteClassA : AbstractClass
{
    protected override void StepOne()
    {
        Console.WriteLine("Step One implemented in ConcreteClassA.");
    }

    protected override void StepTwo()
    {
        Console.WriteLine("Step Two implemented in ConcreteClassA.");
    }
}

// 使用模板方法模式
public static void TemplateMethodPatternExample()
{
    AbstractClass concreteClassA = new ConcreteClassA();
    concreteClassA.TemplateMethod();
}

在这个示例中, AbstractClass 定义了一个 TemplateMethod 方法,该方法代表了算法的骨架。 StepOne StepTwo 是需要被子类实现的抽象方法,而 StepThree 是一个不需要子类实现的默认行为。 ConcreteClassA 继承了 AbstractClass 并提供了 StepOne StepTwo 的具体实现。当执行 TemplateMethod 方法时,子类的实现将会被调用,从而保证了算法的一致性,同时又给了子类足够的灵活性。

以上就是行为型设计模式的C#实现。接下来的章节将探讨这些模式在实际开发中的应用。

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:C#作为一种面向对象的编程语言,在Windows、Web、游戏开发中广泛应用。设计模式作为软件工程中的经验总结,是解决软件设计问题的标准化方案。在C#中,23种设计模式分为创建型、结构型、行为型三大类,并在实际开发中具有广泛应用。本教程详细探讨了这些设计模式在C#中的实现,帮助开发者编写出更加灵活、可维护和可扩展的代码。


本文还有配套的精品资源,点击获取
menu-r.4af5f7ec.gif

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐