C#中常见的设计模式
文章目录
- 引言
- 设计模式的分类
- 创建型模式 (Creational Patterns)
- 1. 单例模式 (Singleton)
- 2. 工厂方法模式 (Factory Method)
- 3. 抽象工厂模式 (Abstract Factory)
- 4. 建造者模式 (Builder)
- 结构型模式 (Structural Patterns)
- 5. 适配器模式 (Adapter)
- 6. 装饰器模式 (Decorator)
- 7. 外观模式 (Facade)
- 8. 代理模式 (Proxy)
- 行为型模式 (Behavioral Patterns)
- 9. 观察者模式 (Observer)
- 10. 策略模式 (Strategy)
- 11. 命令模式 (Command)
- 12. 模板方法模式 (Template Method)
- 总结与参考资源
- 设计模式的选择与应用
- C# 设计模式的实践建议
- 拓展学习资源
引言
设计模式(Design Pattern)代表了软件开发中针对特定问题、经过反复验证的最佳实践解决方案。它们不是可以直接转换成代码的成品,而是描述了在各种不同情况下解决问题的模板或蓝图。在 C# 开发中,熟练运用设计模式可以显著提高代码的可读性、可维护性、可扩展性和可重用性,从而构建出更健壮、更灵活的应用程序。
本文将深入探讨在 C# 开发中最常见和最实用的几类设计模式,包括创建型、结构型和行为型模式,并通过具体的 C# 代码示例和图示来阐释它们的核心思想和应用场景。
设计模式的分类
设计模式通常根据其目的或意图分为三大类:
- 创建型模式 (Creational Patterns):关注对象的创建机制,旨在以适合特定情况的方式创建对象。它们将对象的创建过程与其表示分离,使得系统独立于对象的创建、组合和表示方式。
- 结构型模式 (Structural Patterns):关注类和对象的组合,用于形成更大的结构。它们描述了如何将类或对象组合在一起以获得新的功能。
- 行为型模式 (Behavioral Patterns):关注对象之间的通信和职责分配。它们描述了对象之间如何交互以及如何分配职责,使得对象间的协作更加灵活、高效。
创建型模式 (Creational Patterns)
创建型模式提供了各种对象创建机制,从而增加了代码的灵活性和可重用性。
1. 单例模式 (Singleton)
意图:确保一个类只有一个实例,并提供一个全局访问点来访问该实例。
场景:当系统中某个类只需要一个实例时,例如全局配置管理器、日志记录器、数据库连接池等。
C# 示例 (线程安全):
using System;public sealed class Singleton
{// 使用静态只读字段和静态构造函数确保线程安全且延迟初始化 (Lazy Initialization)private static readonly Singleton instance = new Singleton();private static readonly object padlock = new object(); // 用于旧式双重检查锁定的锁对象,但在现代.NET中通常不需要// 私有构造函数,防止外部通过 new 创建实例private Singleton(){// 初始化代码,例如加载配置等Console.WriteLine("Singleton instance created.");}// 公共静态属性,提供全局访问点public static Singleton Instance{get{// 在现代.NET中,静态字段的初始化是线程安全的,// 所以通常不需要下面的双重检查锁定代码。// 但为了演示旧式实现,保留如下:/*if (instance == null){lock (padlock){if (instance == null){instance = new Singleton();}}}*/return instance;}}// 示例方法public void LogMessage(string message){Console.WriteLine($"Logging message: {message}");}// 防止克隆 (可选,取决于具体需求)private Singleton(Singleton other) { }public object Clone() => throw new NotSupportedException("Singleton cannot be cloned.");
}// 客户端代码
public class SingletonClient
{public static void Main(string[] args){Singleton s1 = Singleton.Instance;Singleton s2 = Singleton.Instance;s1.LogMessage("First message");s2.LogMessage("Second message");if (ReferenceEquals(s1, s2)){Console.WriteLine("s1 and s2 refer to the same instance."); // 输出此行}else{Console.WriteLine("s1 and s2 refer to different instances.");}}
}
类图 (Mermaid):
注意:现代 C# 中实现线程安全的单例模式通常推荐使用 Lazy<T>
或仅依赖静态字段的线程安全初始化特性,上述代码中的双重检查锁定部分更多是为了历史演示。
2. 工厂方法模式 (Factory Method)
意图:定义一个用于创建对象的接口,但让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。
场景:当一个类不知道它所必须创建的对象的类时;当一个类希望由它的子类来指定它所创建的对象时;当类将创建对象的职责委托给多个帮助子类中的某一个,并且你希望将哪一个帮助子类是代理者这一信息局部化的时候。
C# 示例:
using System;// 产品接口
public interface IProduct
{void Operate();
}// 具体产品 A
public class ConcreteProductA : IProduct
{public void Operate(){Console.WriteLine("Operating ConcreteProductA");}
}// 具体产品 B
public class ConcreteProductB : IProduct
{public void Operate(){Console.WriteLine("Operating ConcreteProductB");}
}// 创建者 (工厂) 抽象类
public abstract class Creator
{// 工厂方法,由子类实现public abstract IProduct FactoryMethod();// 创建者也可以包含一些核心业务逻辑,这些逻辑依赖于产品对象public void SomeOperation(){// 调用工厂方法创建产品对象IProduct product = FactoryMethod();// 使用产品Console.WriteLine("Creator: The same creator's code has just worked with:");product.Operate();}
}// 具体创建者 A,负责创建 ConcreteProductA
public class ConcreteCreatorA : Creator
{public override IProduct FactoryMethod(){return new ConcreteProductA();}
}// 具体创建者 B,负责创建 ConcreteProductB
public class ConcreteCreatorB : Creator
{public override IProduct FactoryMethod(){return new ConcreteProductB();}
}// 客户端代码
public class FactoryMethodClient
{public static void Main(string[] args){Console.WriteLine("App: Launched with the ConcreteCreatorA.");ClientCode(new ConcreteCreatorA());Console.WriteLine("");Console.WriteLine("App: Launched with the ConcreteCreatorB.");ClientCode(new ConcreteCreatorB());}// 客户端代码与抽象创建者合作public static void ClientCode(Creator creator){// ...creator.SomeOperation();// ...}
}
类图 (Mermaid):
3. 抽象工厂模式 (Abstract Factory)
意图:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
场景:当一个系统要独立于它的产品的创建、组合和表示时;当一个系统要由多个产品系列中的一个来配置时;当你要强调一系列相关的产品对象的设计以便进行联合使用时;当你提供一个产品类库,而只想显示它们的接口而不是实现时。
C# 示例:
using System;// 抽象产品 A
public interface IAbstractProductA
{string UsefulFunctionA();
}// 具体产品 A1
public class ConcreteProductA1 : IAbstractProductA
{public string UsefulFunctionA() => "The result of the product A1.";
}// 具体产品 A2
public class ConcreteProductA2 : IAbstractProductA
{public string UsefulFunctionA() => "The result of the product A2.";
}// 抽象产品 B
public interface IAbstractProductB
{string UsefulFunctionB();// 产品 B 还可以与产品 A 协作...string AnotherUsefulFunctionB(IAbstractProductA collaborator);
}// 具体产品 B1
public class ConcreteProductB1 : IAbstractProductB
{public string UsefulFunctionB() => "The result of the product B1.";public string AnotherUsefulFunctionB(IAbstractProductA collaborator){var result = collaborator.UsefulFunctionA();return $"The result of the B1 collaborating with the ({result})";}
}// 具体产品 B2
public class ConcreteProductB2 : IAbstractProductB
{public string UsefulFunctionB() => "The result of the product B2.";public string AnotherUsefulFunctionB(IAbstractProductA collaborator){var result = collaborator.UsefulFunctionA();return $"The result of the B2 collaborating with the ({result})";}
}// 抽象工厂接口
public interface IAbstractFactory
{IAbstractProductA CreateProductA();IAbstractProductB CreateProductB();
}// 具体工厂 1,创建产品 A1 和 B1
public class ConcreteFactory1 : IAbstractFactory
{public IAbstractProductA CreateProductA() => new ConcreteProductA1();public IAbstractProductB CreateProductB() => new ConcreteProductB1();
}// 具体工厂 2,创建产品 A2 和 B2
public class ConcreteFactory2 : IAbstractFactory
{public IAbstractProductA CreateProductA() => new ConcreteProductA2();public IAbstractProductB CreateProductB() => new ConcreteProductB2();
}// 客户端代码
public class AbstractFactoryClient
{public static void Main(string[] args){Console.WriteLine("Client: Testing client code with the first factory type...");ClientMethod(new ConcreteFactory1());Console.WriteLine();Console.WriteLine("Client: Testing the same client code with the second factory type...");ClientMethod(new ConcreteFactory2());}public static void ClientMethod(IAbstractFactory factory){var productA = factory.CreateProductA();var productB = factory.CreateProductB();Console.WriteLine(productB.UsefulFunctionB());Console.WriteLine(productB.AnotherUsefulFunctionB(productA));}
}
类图 (Mermaid):
4. 建造者模式 (Builder)
意图:将一个复杂对象的构建与其表示分离,使得同样的构建过程可以创建不同的表示。
场景:当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时;当构造过程必须允许被构造的对象有不同的表示时。
C# 示例:
using System;
using System.Collections.Generic;// 产品类 (例如:一份复杂的报告)
public class Report
{private List<string> parts = new List<string>();public void AddPart(string part){parts.Add(part);}public void Show(){Console.WriteLine("\nReport Parts-------");foreach (string part in parts)Console.WriteLine(part);}
}// 建造者接口
public interface IReportBuilder
{void BuildHeader();void BuildContent();void BuildFooter();Report GetReport();
}// 具体建造者:PDF 报告
public class PdfReportBuilder : IReportBuilder
{private Report report = new Report();public void BuildHeader(){report.AddPart("PDF Header: Company Logo, Title");}public void BuildContent(){report.AddPart("PDF Content: Charts, Tables, Text Paragraphs");}public void BuildFooter(){report.AddPart("PDF Footer: Page Numbers, Confidentiality Notice");}public Report GetReport(){Report result = report;// 重置建造者以备下次使用 (可选)report = new Report();return result;}
}// 具体建造者:Web 报告
public class WebReportBuilder : IReportBuilder
{private Report report = new Report();public void BuildHeader(){report.AddPart("<header>Web Header: Navigation Bar, Search</header>");}public void BuildContent(){report.AddPart("<main>Web Content: Interactive Elements, Data Grids</main>");}public void BuildFooter(){report.AddPart("<footer>Web Footer: Copyright Info, Links</footer>");}public Report GetReport(){Report result = report;report = new Report();return result;}
}// 指挥者类 (可选,有时客户端直接调用建造者)
public class ReportDirector
{private IReportBuilder builder;public ReportDirector(IReportBuilder builder){this.builder = builder;}public void ConstructReport(){builder.BuildHeader();builder.BuildContent();builder.BuildFooter();}public void SetBuilder(IReportBuilder builder){this.builder = builder;}
}// 客户端代码
public class BuilderClient
{public static void Main(string[] args){// 使用 PDF 建造者IReportBuilder pdfBuilder = new PdfReportBuilder();ReportDirector director = new ReportDirector(pdfBuilder);Console.WriteLine("Constructing PDF report:");director.ConstructReport();Report pdfReport = pdfBuilder.GetReport(); // 从建造者获取产品pdfReport.Show();Console.WriteLine("\n------------------\n");// 使用 Web 建造者IReportBuilder webBuilder = new WebReportBuilder();director.SetBuilder(webBuilder); // 更换建造者Console.WriteLine("Constructing Web report:");director.ConstructReport();Report webReport = webBuilder.GetReport(); // 从建造者获取产品webReport.Show();// 也可以不使用指挥者,客户端直接控制构建步骤Console.WriteLine("\n------------------\n");Console.WriteLine("Constructing Custom report (Client direct control):");IReportBuilder customBuilder = new PdfReportBuilder();customBuilder.BuildHeader();// customBuilder.BuildContent(); // 可以省略某些步骤customBuilder.BuildFooter();Report customReport = customBuilder.GetReport();customReport.Show();}
}
类图 (Mermaid):
结构型模式 (Structural Patterns)
结构型模式关注如何将类和对象组合成更大的结构,同时保持结构的灵活性和效率。
5. 适配器模式 (Adapter)
意图:将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
场景:你想使用一个已经存在的类,而它的接口不符合你的需求;你想创建一个可以复用的类,该类可以与其他不相关的类或不可预见的类协同工作;你想使用一些已经存在的子类,但是不可能对每一个都进行子类化以匹配它们的接口。对象适配器可以适配它的父类接口。
C# 示例:
using System;// 需要被适配的类 (Adaptee)
public class Adaptee
{public void SpecificRequest(){Console.WriteLine("Called SpecificRequest() in Adaptee");}
}// 目标接口 (Target),客户端期望的接口
public interface ITarget
{void Request();
}// 适配器类 (Adapter),实现目标接口,并包含一个 Adaptee 实例
public class Adapter : ITarget
{private readonly Adaptee adaptee;public Adapter(Adaptee adaptee){this.adaptee = adaptee;}// 实现目标接口的方法,内部调用 Adaptee 的方法public void Request(){Console.WriteLine("Adapter.Request() called - forwarding to Adaptee...");adaptee.SpecificRequest();}
}// 客户端代码
public class AdapterClient
{public static void Main(string[] args){// 创建需要被适配的对象Adaptee adapteeInstance = new Adaptee();// 创建适配器,将 Adaptee 包装起来ITarget target = new Adapter(adapteeInstance);// 客户端通过目标接口调用Console.WriteLine("Client: Calling Request() on Target interface...");target.Request();}
}
类图 (Mermaid):
这种是对象适配器模式。还有一种类适配器模式,它通过多重继承(C# 不直接支持类多重继承,但可以通过接口实现)或继承 Adaptee 并实现 ITarget 来工作,但在 C# 中对象适配器更常用。
6. 装饰器模式 (Decorator)
意图:动态地给一个对象添加一些额外的职责。就增加功能来说,装饰器模式相比生成子类更为灵活。
场景:在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责;处理那些可以撤销的职责;当不能采用生成子类的方法进行扩充时。例如,可能有大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长。
C# 示例:
using System;// 组件接口 (Component)
public interface ICoffee
{string GetDescription();double GetCost();
}// 具体组件 (Concrete Component)
public class SimpleCoffee : ICoffee
{public string GetDescription() => "Simple Coffee";public double GetCost() => 5.0;
}// 装饰器抽象类 (Decorator)
public abstract class CoffeeDecorator : ICoffee
{protected ICoffee decoratedCoffee;public CoffeeDecorator(ICoffee coffee){this.decoratedCoffee = coffee;}public virtual string GetDescription() => decoratedCoffee.GetDescription();public virtual double GetCost() => decoratedCoffee.GetCost();
}// 具体装饰器 A (Concrete Decorator A): 加牛奶
public class MilkDecorator : CoffeeDecorator
{public MilkDecorator(ICoffee coffee) : base(coffee){}public override string GetDescription() => base.GetDescription() + ", with Milk";public override double GetCost() => base.GetCost() + 1.5;
}// 具体装饰器 B (Concrete Decorator B): 加糖
public class SugarDecorator : CoffeeDecorator
{public SugarDecorator(ICoffee coffee) : base(coffee){}public override string GetDescription() => base.GetDescription() + ", with Sugar";public override double GetCost() => base.GetCost() + 0.5;
}// 客户端代码
public class DecoratorClient
{public static void Main(string[] args){// 点一杯简单的咖啡ICoffee simpleCoffee = new SimpleCoffee();Console.WriteLine($"Order: {simpleCoffee.GetDescription()}, Cost: {simpleCoffee.GetCost():C}");// 给简单咖啡加牛奶ICoffee milkCoffee = new MilkDecorator(simpleCoffee);Console.WriteLine($"Order: {milkCoffee.GetDescription()}, Cost: {milkCoffee.GetCost():C}");// 再加糖ICoffee milkAndSugarCoffee = new SugarDecorator(milkCoffee);Console.WriteLine($"Order: {milkAndSugarCoffee.GetDescription()}, Cost: {milkAndSugarCoffee.GetCost():C}");// 直接点一杯加牛奶和糖的咖啡ICoffee complexCoffee = new SugarDecorator(new MilkDecorator(new SimpleCoffee()));Console.WriteLine($"Order: {complexCoffee.GetDescription()}, Cost: {complexCoffee.GetCost():C}");}
}
类图 (Mermaid):
7. 外观模式 (Facade)
意图:为子系统中的一组接口提供一个一致的界面。外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
场景:当你要为一个复杂子系统提供一个简单接口时;客户程序与抽象类的实现部分之间存在着很大的依赖性;当你需要构建一个层次结构的子系统时,使用外观模式定义子系统中每层的入口点。
C# 示例:
using System;// 子系统组件 A
public class SubsystemA
{public void OperationA1() => Console.WriteLine("SubsystemA: Operation A1");public void OperationA2() => Console.WriteLine("SubsystemA: Operation A2");
}// 子系统组件 B
public class SubsystemB
{public void OperationB1() => Console.WriteLine("SubsystemB: Operation B1");public void OperationB2() => Console.WriteLine("SubsystemB: Operation B2");
}// 子系统组件 C
public class SubsystemC
{public void OperationC1() => Console.WriteLine("SubsystemC: Operation C1");public void OperationC2() => Console.WriteLine("SubsystemC: Operation C2");
}// 外观类 (Facade)
public class Facade
{private SubsystemA subsystemA;private SubsystemB subsystemB;private SubsystemC subsystemC;public Facade(){subsystemA = new SubsystemA();subsystemB = new SubsystemB();subsystemC = new SubsystemC();Console.WriteLine("Facade created with subsystems.");}// 提供简化的接口方法,封装子系统的复杂交互public void SimplifiedOperation1(){Console.WriteLine("\nFacade: SimplifiedOperation1() -----");subsystemA.OperationA1();subsystemB.OperationB1();subsystemC.OperationC1();}public void SimplifiedOperation2(){Console.WriteLine("\nFacade: SimplifiedOperation2() -----");subsystemA.OperationA2();subsystemC.OperationC2();}
}// 客户端代码
public class FacadeClient
{public static void Main(string[] args){// 客户端通过外观类与子系统交互Facade facade = new Facade();// 调用简化的操作facade.SimplifiedOperation1();facade.SimplifiedOperation2();}
}
类图 (Mermaid):
8. 代理模式 (Proxy)
意图:为其他对象提供一种代理以控制对这个对象的访问。
场景:远程代理(Remote Proxy)为一个位于不同的地址空间的对象提供一个本地的代理对象;虚拟代理(Virtual Proxy)根据需要创建开销很大的对象;保护代理(Protection Proxy)控制对原始对象的访问;智能指引(Smart Reference)取代了简单的指针,它在访问对象时执行一些附加操作。
C# 示例 (虚拟代理):
using System;
using System.Threading;// 主题接口 (Subject)
public interface IImage
{void Display();
}// 真实主题 (Real Subject) - 加载图片可能很耗时
public class RealImage : IImage
{private string filename;public RealImage(string filename){this.filename = filename;LoadImageFromDisk();}private void LoadImageFromDisk(){Console.WriteLine($"Loading image: {filename}...");// 模拟耗时操作Thread.Sleep(2000);Console.WriteLine($"Image {filename} loaded.");}public void Display(){Console.WriteLine($"Displaying image: {filename}");}
}// 代理 (Proxy) - 控制对 RealImage 的访问
public class ImageProxy : IImage
{private RealImage realImage; // 对真实主题的引用private string filename;private bool imageLoaded = false;public ImageProxy(string filename){this.filename = filename;Console.WriteLine($"ImageProxy created for {filename}. Image not loaded yet.");}public void Display(){// 虚拟代理:仅在首次需要显示时才加载真实图片if (!imageLoaded){Console.WriteLine($"Proxy: First time display request for {filename}. Loading real image now.");realImage = new RealImage(filename); // 延迟加载imageLoaded = true;}else{Console.WriteLine($"Proxy: Display request for {filename}. Image already loaded.");}// 委托给真实主题进行显示realImage.Display();}
}// 客户端代码
public class ProxyClient
{public static void Main(string[] args){Console.WriteLine("Creating proxy for image1.jpg");IImage image1 = new ImageProxy("image1.jpg");Console.WriteLine("\n--- First display call for image1.jpg ---");image1.Display(); // 此时会加载真实图片Console.WriteLine("\n--- Second display call for image1.jpg ---");image1.Display(); // 此时直接显示,不再加载Console.WriteLine("\nCreating proxy for image2.png");IImage image2 = new ImageProxy("image2.png");Console.WriteLine("\n--- Display call for image2.png ---");image2.Display(); // 加载 image2.png}
}
类图 (Mermaid):
[待续 - 行为型模式…]
行为型模式 (Behavioral Patterns)
行为型模式关注对象之间的通信方式,以及如何分配职责和算法。这些模式使得对象间的相互作用更加灵活,同时降低了系统的耦合度。
9. 观察者模式 (Observer)
意图:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并自动更新。
场景:当一个抽象模型有两个方面,其中一个方面依赖于另一个方面;当对一个对象的改变需要同时改变其他对象,而不知道具体有多少对象需要改变;当一个对象必须通知其他对象,而它又不能假定其他对象是谁。
C# 示例:
using System;
using System.Collections.Generic;// 主题接口 (Subject)
public interface ISubject
{void Attach(IObserver observer);void Detach(IObserver observer);void Notify();
}// 观察者接口
public interface IObserver
{void Update(ISubject subject);
}// 具体主题类:股票
public class Stock : ISubject
{private List<IObserver> observers = new List<IObserver>();private string symbol;private double price;public Stock(string symbol, double price){this.symbol = symbol;this.price = price;}public string Symbol => symbol;public double Price{get { return price; }set{Console.WriteLine($"\n{symbol} price changed from {price} to {value}");price = value;Notify();}}public void Attach(IObserver observer){observers.Add(observer);Console.WriteLine($"Added observer to {symbol}");}public void Detach(IObserver observer){observers.Remove(observer);Console.WriteLine($"Removed observer from {symbol}");}public void Notify(){Console.WriteLine($"Notifying observers about changes to {symbol}...");foreach (var observer in observers){observer.Update(this);}}
}// 具体观察者:投资者
public class Investor : IObserver
{private string name;public Investor(string name){this.name = name;}public void Update(ISubject subject){if (subject is Stock stock){Console.WriteLine($"Investor {name} notified of {stock.Symbol}'s price change to {stock.Price}");}}
}// 客户端代码
public class ObserverClient
{public static void Main(string[] args){// 创建股票Stock msft = new Stock("MSFT", 120.00);Stock aapl = new Stock("AAPL", 150.00);// 创建投资者Investor investor1 = new Investor("John");Investor investor2 = new Investor("Alice");Investor investor3 = new Investor("Bob");// 投资者订阅股票msft.Attach(investor1);msft.Attach(investor2);aapl.Attach(investor1);aapl.Attach(investor3);// 股票价格变化,观察者会自动收到通知msft.Price = 123.45;aapl.Price = 145.67;// 投资者取消订阅msft.Detach(investor2);// 再次变化价格msft.Price = 125.40;}
}
类图 (Mermaid):
10. 策略模式 (Strategy)
意图:定义一系列的算法,将每一个算法封装起来,并使它们可以相互替换。策略模式让算法独立于使用它的客户而变化。
场景:当需要使用一个算法的不同变体时;当需要隐藏复杂的、与算法相关的数据结构时;当一个类定义了多种行为,且这些行为以多个条件语句的形式出现时,可以用策略模式去掉这些条件语句。
C# 示例:
using System;// 策略接口 (Strategy)
public interface ISortStrategy
{void Sort(int[] array);string Name { get; }
}// 具体策略 A:冒泡排序
public class BubbleSortStrategy : ISortStrategy
{public string Name => "Bubble Sort";public void Sort(int[] array){Console.WriteLine("Sorting using Bubble Sort...");// 冒泡排序实现for (int i = 0; i < array.Length - 1; i++){for (int j = 0; j < array.Length - i - 1; j++){if (array[j] > array[j + 1]){// 交换元素int temp = array[j];array[j] = array[j + 1];array[j + 1] = temp;}}}}
}// 具体策略 B:快速排序
public class QuickSortStrategy : ISortStrategy
{public string Name => "Quick Sort";public void Sort(int[] array){Console.WriteLine("Sorting using Quick Sort...");QuickSort(array, 0, array.Length - 1);}private void QuickSort(int[] array, int low, int high){if (low < high){int partitionIndex = Partition(array, low, high);QuickSort(array, low, partitionIndex - 1);QuickSort(array, partitionIndex + 1, high);}}private int Partition(int[] array, int low, int high){int pivot = array[high];int i = low - 1;for (int j = low; j < high; j++){if (array[j] <= pivot){i++;// 交换元素int temp = array[i];array[i] = array[j];array[j] = temp;}}// 交换元素int temp1 = array[i + 1];array[i + 1] = array[high];array[high] = temp1;return i + 1;}
}// 上下文类 (Context)
public class SortContext
{private ISortStrategy sortStrategy;public SortContext(ISortStrategy strategy){this.sortStrategy = strategy;}public void SetStrategy(ISortStrategy strategy){this.sortStrategy = strategy;Console.WriteLine($"Strategy changed to {strategy.Name}");}public void SortData(int[] array){Console.WriteLine($"Context: Sorting array using {sortStrategy.Name}");sortStrategy.Sort(array);Console.WriteLine("Context: Array is sorted");}
}// 客户端代码
public class StrategyClient
{// 辅助方法:打印数组private static void PrintArray(int[] array){Console.WriteLine("Array: " + string.Join(", ", array));}public static void Main(string[] args){// 准备数据int[] data = { 5, 2, 9, 1, 7, 6, 3 };Console.WriteLine("Original array:");PrintArray(data);// 创建上下文,初始使用冒泡排序SortContext context = new SortContext(new BubbleSortStrategy());context.SortData(data);Console.WriteLine("Sorted array (Bubble Sort):");PrintArray(data);// 准备新数据并切换到快速排序int[] newData = { 10, 4, 8, 2, 5, 9, 1, 6, 3, 7 };Console.WriteLine("\nOriginal array (new data):");PrintArray(newData);context.SetStrategy(new QuickSortStrategy());context.SortData(newData);Console.WriteLine("Sorted array (Quick Sort):");PrintArray(newData);}
}
类图 (Mermaid):
11. 命令模式 (Command)
意图:将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。
场景:需要抽象出待执行的动作,将这些动作参数化;在不同的时刻指定、排列和执行请求;需要支持取消操作;需要支持修改日志功能,这样当系统崩溃时,这些修改可以被重做一遍;需要支持事务操作。
C# 示例:
using System;
using System.Collections.Generic;// 接收者类 (Receiver):电灯
public class Light
{private string location;private bool isOn;public Light(string location){this.location = location;isOn = false;}public void TurnOn(){if (!isOn){isOn = true;Console.WriteLine($"{location} light is now ON");}}public void TurnOff(){if (isOn){isOn = false;Console.WriteLine($"{location} light is now OFF");}}
}// 命令接口 (Command)
public interface ICommand
{void Execute();void Undo();
}// 具体命令:打开电灯命令
public class LightOnCommand : ICommand
{private Light light;public LightOnCommand(Light light){this.light = light;}public void Execute(){light.TurnOn();}public void Undo(){light.TurnOff();}
}// 具体命令:关闭电灯命令
public class LightOffCommand : ICommand
{private Light light;public LightOffCommand(Light light){this.light = light;}public void Execute(){light.TurnOff();}public void Undo(){light.TurnOn();}
}// 具体命令:无操作
public class NoCommand : ICommand
{public void Execute() { /* 不做任何操作 */ }public void Undo() { /* 不做任何操作 */ }
}// 调用者 (Invoker):遥控器
public class RemoteControl
{private ICommand[] onCommands;private ICommand[] offCommands;private Stack<ICommand> undoCommands;private int slots;public RemoteControl(int numberOfSlots){slots = numberOfSlots;onCommands = new ICommand[slots];offCommands = new ICommand[slots];undoCommands = new Stack<ICommand>();ICommand noCommand = new NoCommand();for (int i = 0; i < slots; i++){onCommands[i] = noCommand;offCommands[i] = noCommand;}}public void SetCommand(int slot, ICommand onCommand, ICommand offCommand){if (slot >= 0 && slot < slots){onCommands[slot] = onCommand;offCommands[slot] = offCommand;}}public void PressOnButton(int slot){if (slot >= 0 && slot < slots){onCommands[slot].Execute();undoCommands.Push(onCommands[slot]);}}public void PressOffButton(int slot){if (slot >= 0 && slot < slots){offCommands[slot].Execute();undoCommands.Push(offCommands[slot]);}}public void PressUndoButton(){if (undoCommands.Count > 0){Console.WriteLine("------ Undo Operation ------");ICommand lastCommand = undoCommands.Pop();lastCommand.Undo();}else{Console.WriteLine("------ No operations to undo ------");}}public void ShowStatus(){Console.WriteLine("\n------ Remote Control Status ------");for (int i = 0; i < slots; i++){Console.WriteLine($"[Slot {i}] ON: {onCommands[i].GetType().Name}, OFF: {offCommands[i].GetType().Name}");}Console.WriteLine($"Undo stack has {undoCommands.Count} operations.\n");}
}// 客户端代码
public class CommandClient
{public static void Main(string[] args){// 创建遥控器RemoteControl remote = new RemoteControl(3);// 创建接收者Light livingRoomLight = new Light("Living Room");Light kitchenLight = new Light("Kitchen");// 创建命令LightOnCommand livingRoomLightOn = new LightOnCommand(livingRoomLight);LightOffCommand livingRoomLightOff = new LightOffCommand(livingRoomLight);LightOnCommand kitchenLightOn = new LightOnCommand(kitchenLight);LightOffCommand kitchenLightOff = new LightOffCommand(kitchenLight);// 设置命令到遥控器的插槽remote.SetCommand(0, livingRoomLightOn, livingRoomLightOff);remote.SetCommand(1, kitchenLightOn, kitchenLightOff);// 显示遥控器状态remote.ShowStatus();// 操作遥控器Console.WriteLine("------ Using Remote Control ------");remote.PressOnButton(0); // 打开客厅灯remote.PressOnButton(1); // 打开厨房灯remote.PressOffButton(0); // 关闭客厅灯remote.PressUndoButton(); // 撤销上一操作(重新打开客厅灯)remote.PressOffButton(1); // 关闭厨房灯remote.PressUndoButton(); // 撤销上一操作(重新打开厨房灯)remote.PressUndoButton(); // 撤销上一操作(再次关闭客厅灯)remote.PressUndoButton(); // 撤销上一操作(再次打开厨房灯)remote.PressUndoButton(); // 撤销上一操作(再次关闭厨房灯)// 此时撤销栈已为空remote.PressUndoButton();}
}
类图 (Mermaid):
12. 模板方法模式 (Template Method)
意图:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
场景:当想要定义一个算法的基本结构,而让子类提供某些具体步骤时;当多个类中包含几乎相同的算法,但有少量差异时;当希望控制子类扩展,只允许在特定点进行扩展时。
C# 示例:
using System;// 抽象类 (Abstract Class)
public abstract class DocumentProcessor
{// 模板方法,定义算法骨架public void ProcessDocument(string document){Console.WriteLine("---- Starting document processing ----");// 1. 打开文档OpenDocument(document);// 2. 分析文档内容AnalyzeContent();// 3. 处理文档内容 (子类必须实现的步骤)ProcessContent();// 4. 可选的钩子方法:验证文档if (ShouldValidate()){ValidateDocument();}// 5. 保存文档SaveDocument();Console.WriteLine("---- Document processing completed ----\n");}// 具体方法 - 所有子类共享的实现protected void OpenDocument(string document){Console.WriteLine($"Opening document: {document}");}protected void AnalyzeContent(){Console.WriteLine("Analyzing document content...");}protected void SaveDocument(){Console.WriteLine("Saving processed document...");}// 抽象方法 - 子类必须实现protected abstract void ProcessContent();// 钩子方法 - 子类可以选择性覆盖protected virtual bool ShouldValidate(){return true; // 默认行为}protected virtual void ValidateDocument(){Console.WriteLine("Performing basic document validation...");}
}// 具体类:文本文档处理器
public class TextDocumentProcessor : DocumentProcessor
{protected override void ProcessContent(){Console.WriteLine("Processing TEXT document: Spell checking, formatting text...");}// 覆盖钩子方法protected override void ValidateDocument(){Console.WriteLine("Performing TEXT specific validation: Checking grammar, paragraph structure...");}
}// 具体类:PDF文档处理器
public class PdfDocumentProcessor : DocumentProcessor
{protected override void ProcessContent(){Console.WriteLine("Processing PDF document: Extracting text layers, processing forms...");}// 覆盖钩子方法,跳过验证protected override bool ShouldValidate(){return false; // PDF不需要验证}
}// 具体类:图像文档处理器
public class ImageDocumentProcessor : DocumentProcessor
{protected override void ProcessContent(){Console.WriteLine("Processing IMAGE document: Adjusting levels, applying filters...");}// 覆盖钩子方法protected override void ValidateDocument(){Console.WriteLine("Performing IMAGE specific validation: Checking resolution, color profile...");}
}// 客户端代码
public class TemplateMethodClient
{public static void Main(string[] args){Console.WriteLine("Processing a text document:");DocumentProcessor textProcessor = new TextDocumentProcessor();textProcessor.ProcessDocument("report.txt");Console.WriteLine("Processing a PDF document:");DocumentProcessor pdfProcessor = new PdfDocumentProcessor();pdfProcessor.ProcessDocument("manual.pdf");Console.WriteLine("Processing an image document:");DocumentProcessor imageProcessor = new ImageDocumentProcessor();imageProcessor.ProcessDocument("photo.jpg");}
}
类图 (Mermaid):
总结与参考资源
设计模式的选择与应用
在实际开发中,选择合适的设计模式需要考虑以下几点:
- 问题的本质:设计模式是解决特定问题的方案,首先要明确问题的本质。
- 系统需求:考虑系统的可扩展性、可维护性、性能等需求。
- 过度设计的风险:不要为了使用设计模式而使用设计模式,避免不必要的复杂性。
- 团队经验:考虑团队成员对设计模式的熟悉程度。
C# 设计模式的实践建议
- 结合 C# 特性:利用 C# 的语言特性(如泛型、LINQ、异步编程)与设计模式结合。
- 参考现有框架:学习 .NET 框架本身如何应用设计模式(例如,ASP.NET Core 中的中间件管道使用了责任链模式)。
- 测试驱动开发:通过单元测试验证设计模式的实现是否符合预期。
- 保持简单:记住 KISS 原则(Keep It Simple, Stupid),不要过度设计。
拓展学习资源
-
在线资源:
- Microsoft Learn
- Refactoring.Guru
- SourceMaking
-
开源项目:
- .NET 运行时源码
- ASP.NET Core
通过深入理解设计模式并结合 C# 的特性灵活应用,你可以构建出更加健壮、灵活、可维护的软件系统。设计模式是工具,而不是目标——它们的价值在于解决实际问题的能力。