一、引言
在软件开发领域,设计模式是经过实践验证的、用于解决特定软件设计问题的通用方案。它们如同建筑师手中的蓝图,能帮助开发者构建出结构清晰、可维护性强且易于扩展的软件系统。了解并掌握这 些设计模式,对于提升软件质量、降低开发成本以及提高开发效率具有重要意义。以下将详细介绍这些设计模式及其在 C# 中的应用实例。
二、创建型模式
(一)工厂方法模式
-
用途
定义一个创建对象的接口,但让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。在需要根据不同条件创建不同类型对象时很有用,比如在不同操作系统下创建不同风格的界面组件。 -
优缺点
- 优点:将对象的创建和使用分离,增加了代码的可维护性和可扩展性。符合开闭原则,对扩展开放,对修改关闭。
- 缺点:每增加一种产品,就需要增加一个工厂类的子类,增加了代码量。
-
C# 示例
// 产品接口
interface IProduct
{
void Operation();
}
// 具体产品类
class ConcreteProductA : IProduct
{
public void Operation()
{
Console.WriteLine("ConcreteProductA Operation");
}
}
class ConcreteProductB : IProduct
{
public void Operation()
{
Console.WriteLine("ConcreteProductB Operation");
}
}
// 工厂接口
abstract class Creator
{
public abstract IProduct FactoryMethod();
}
// 具体工厂类
class ConcreteCreatorA : Creator
{
public override IProduct FactoryMethod()
{
return new ConcreteProductA();
}
}
class ConcreteCreatorB : Creator
{
public override IProduct FactoryMethod()
{
return new ConcreteProductB();
}
}
class Program
{
static void Main()
{
Creator creatorA = new ConcreteCreatorA();
IProduct productA = creatorA.FactoryMethod();
productA.Operation();Creator creatorB = new ConcreteCreatorB(); IProduct productB = creatorB.FactoryMethod(); productB.Operation(); }
}
(二)抽象工厂模式
-
用途
提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。适用于创建一组具有相同主题但不同细节的对象,比如创建不同数据库类型(如 MySQL、Oracle)的连接对象和操作对象组合。 -
优缺点
- 优点:隔离了具体类的生成,使得客户代码不需要依赖具体类,易于交换产品系列。
- 缺点:难以扩展新的产品族,增加新的产品族时需要修改抽象工厂的接口。
-
C# 示例
// 抽象产品 A
interface AbstractProductA
{
void MethodA();
}
// 抽象产品 B
interface AbstractProductB
{
void MethodB();
}
// 具体产品 A1
class ConcreteProductA1 : AbstractProductA
{
public void MethodA()
{
Console.WriteLine("ConcreteProductA1 MethodA");
}
}
// 具体产品 B1
class ConcreteProductB1 : AbstractProductB
{
public void MethodB()
{
Console.WriteLine("ConcreteProductB1 MethodB");
}
}
// 具体产品 A2
class ConcreteProductA2 : AbstractProductA
{
public void MethodA()
{
Console.WriteLine("ConcreteProductA2 MethodA");
}
}
// 具体产品 B2
class ConcreteProductB2 : AbstractProductB
{
public void MethodB()
{
Console.WriteLine("ConcreteProductB2 MethodB");
}
}
// 抽象工厂
interface AbstractFactory
{
AbstractProductA CreateProductA();
AbstractProductB CreateProductB();
}
// 具体工厂 1
class ConcreteFactory1 : AbstractFactory
{
public AbstractProductA CreateProductA()
{
return new ConcreteProductA1();
}
public AbstractProductB CreateProductB()
{
return new ConcreteProductB1();
}
}
// 具体工厂 2
class ConcreteFactory2 : AbstractFactory
{
public AbstractProductA CreateProductA()
{
return new ConcreteProductA2();
}
public AbstractProductB CreateProductB()
{
return new ConcreteProductB2();
}
}
class Program
{
static void Main()
{
AbstractFactory factory1 = new ConcreteFactory1();
AbstractProductA productA1 = factory1.CreateProductA();
AbstractProductB productB1 = factory1.CreateProductB();
productA1.MethodA();
productB1.MethodB();AbstractFactory factory2 = new ConcreteFactory2(); AbstractProductA productA2 = factory2.CreateProductA(); AbstractProductB productB2 = factory2.CreateProductB(); productA2.MethodA(); productB2.MethodB(); }
}
(三)单例模式
-
用途
保证一个类仅有一个实例,并提供一个访问它的全局访问点。适用于需要全局唯一实例的场景,如数据库连接池管理、日志记录系统等。 -
优缺点
- 优点:减少了系统资源开销,对唯一实例的受控访问。
- 缺点:违反单一职责原则,可能隐藏类的依赖关系,并且在多线程环境下实现较为复杂。
-
C# 示例
class Singleton
{
private static Singleton instance;
private Singleton() {}
public static Singleton Instance
{
get
{
if (instance == null)
{
instance = new Singleton();
}
return instance;
}
}
}
class Program
{
static void Main()
{
Singleton s1 = Singleton.Instance;
Singleton s2 = Singleton.Instance;
if (s1 == s2)
{
Console.WriteLine("Same instance");
}
}
}
(四)建造者模式
-
用途
将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。适用于创建复杂对象且对象的各个部分构建步骤相似但具体实现不同的情况,比如构建不同配置的汽车对象。 -
优缺点
- 优点:封装了复杂对象的创建过程,使得代码结构清晰,易于扩展和维护。可以精细控制对象创建的过程。
- 缺点:如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类。
-
C# 示例
// 产品类
class Product
{
public string PartA { get; set; }
public string PartB { get; set; }
public string PartC { get; set; }
}
// 抽象建造者
abstract class Builder
{
public abstract void BuildPartA();
public abstract void BuildPartB();
public abstract void BuildPartC();
public abstract Product GetResult();
}
// 具体建造者
class ConcreteBuilder : Builder
{
private Product product = new Product();
public override void BuildPartA()
{
product.PartA = "PartA built";
}
public override void BuildPartB()
{
product.PartB = "PartB built";
}
public override void BuildPartC()
{
product.PartC = "PartC built";
}
public override Product GetResult()
{
return product;
}
}
// 指挥者
class Director
{
public void Construct(Builder builder)
{
builder.BuildPartA();
builder.BuildPartB();
builder.BuildPartC();
}
}
class Program
{
static void Main()
{
Director director = new Director();
ConcreteBuilder builder = new ConcreteBuilder();
director.Construct(builder);
Product product = builder.GetResult();
Console.WriteLine($"PartA: {product.PartA}, PartB: {product.PartB}, PartC: {product.PartC}");
}
}
(五)原型模式
-
用途
用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。适用于创建对象成本较大或者创建过程较为复杂的情况,比如创建复杂图形对象的副本。 -
优缺点
- 优点:性能优良,简化对象创建过程,通过克隆避免了类创建时的构造函数等开销。
- 缺点:需要为每一个类配备一个克隆方法,实现较为繁琐。对克隆对象中的引用类型成员处理不当可能导致错误。
-
C# 示例
class Prototype
{
public int Data { get; set; }
public List<string> ListData { get; set; }
public Prototype()
{
ListData = new List<string>();
}
public Prototype Clone()
{
Prototype clone = (Prototype)MemberwiseClone();
clone.ListData = new List<string>(ListData);
return clone;
}
}
class Program
{
static void Main()
{
Prototype original = new Prototype { Data = 10 };
original.ListData.Add("Item 1");
Prototype clone = original.Clone();
clone.Data = 20;
clone.ListData.Add("Item 2");Console.WriteLine($"Original Data: {original.Data}, Clone Data: {clone.Data}"); Console.WriteLine($"Original List: {string.Join(", ", original.ListData)}"); Console.WriteLine($"Clone List: {string.Join(", ", clone.ListData)}"); }
}
三、结构型模式
(一)适配器模式
-
用途
将一个类的接口转换成客户希望的另外一个接口。使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。例如将一个旧系统的接口适配到新系统中。 -
优缺点
- 优点:更好的复用性,解决了接口不兼容的问题。
- 缺点:增加了系统的复杂性,过多使用可能会使系统零乱。
-
C# 示例
// 目标接口
interface ITarget
{
void Request();
}
// 需要适配的类
class Adaptee
{
public void SpecificRequest()
{
Console.WriteLine("Adaptee SpecificRequest");
}
}
// 适配器类
class Adapter : ITarget
{
private Adaptee adaptee = new Adaptee();
public void Request()
{
adaptee.SpecificRequest();
}
}
class Program
{
static void Main()
{
ITarget target = new Adapter();
target.Request();
}
}
(二)桥接模式
-
用途
将抽象部分与它的实现部分分离,使它们都可以独立地变化。适用于需要在不同维度独立变化的类层次结构,比如图形绘制中不同形状和不同颜色绘制方式的独立变化。 -
优缺点
- 优点:分离了抽象和实现,提高了系统的可扩展性和可维护性。
- 缺点:增加了系统的理解和设计难度,需要正确识别系统中的抽象和实现部分。
-
C# 示例
// 实现部分接口
interface Implementor
{
void OperationImpl();
}
// 具体实现类
class ConcreteImplementorA : Implementor
{
public void OperationImpl()
{
Console.WriteLine("ConcreteImplementorA OperationImpl");
}
}
class ConcreteImplementorB : Implementor
{
public void OperationImpl()
{
Console.WriteLine("ConcreteImplementorB OperationImpl");
}
}
// 抽象部分基类
abstract class Abstraction
{
protected Implementor implementor;
public Abstraction(Implementor implementor)
{
this.implementor = implementor;
}
public abstract void Operation();
}
// 具体抽象类
class RefinedAbstraction : Abstraction
{
public RefinedAbstraction(Implementor implementor) : base(implementor) {}
public override void Operation()
{
implementor.OperationImpl();
}
}
class Program
{
static void Main()
{
Implementor implementorA = new ConcreteImplementorA();
Abstraction abstractionA = new RefinedAbstraction(implementorA);
abstractionA.Operation();Implementor implementorB = new ConcreteImplementorB(); Abstraction abstractionB = new RefinedAbstraction(implementorB); abstractionB.Operation(); }
}
(三)组合模式
-
用途
将对象组合成树形结构以表示 "部分 - 整体" 的层次结构。使得用户对单个对象和组合对象的使用具有一致性。比如文件系统中的文件夹和文件的组织。 -
优缺点
- 优点:定义了包含基本对象和组合对象的类层次结构,简化了客户端代码。
- 缺点:设计较为复杂,不容易限制组合中的组件类型。
-
C# 示例
interface IComponent
{
void Operation();
}
class Leaf : IComponent
{
private string name;
public Leaf(string name)
{
this.name = name;
}
public void Operation()
{
Console.WriteLine("Leaf {name} Operation"); } } class Composite : IComponent { private Listchildren = new List "Composite {name} Operation");(); private string name; public Composite(string name) { this.name = name; } public void Add(IComponent component) { children.Add(component); } public void Remove(IComponent component) { children.Remove(component); } public void Operation() { Console.WriteLine(
foreach (var child in children)
{
child.Operation();
}
}
}
class Program
{
static void Main()
{
Composite root = new Composite("Root");
Leaf leaf1 = new Leaf("Leaf 1");
Leaf leaf2 = new Leaf("Leaf 2");
Composite composite1 = new Composite("Composite 1");root.Add(leaf1); root.Add(composite1); composite1.Add(leaf2); root.Operation(); }
}
(四)装饰器模式
-
用途
动态地给一个对象添加一些额外的职责。相比子类化方式更加灵活。例如为一个文本处理类添加不同的格式装饰功能。 -
优缺点
- 优点:比静态继承更灵活,可以在运行时动态地添加或删除职责。
- 缺点:装饰类过多时可能导致代码复杂,并且装饰的顺序可能会影响结果。
-
C# 示例
// 组件接口
interface IComponent
{
void Operation();
}
// 具体组件类
class ConcreteComponent : IComponent
{
public void Operation()
{
Console.WriteLine("ConcreteComponent Operation");
}
}
// 装饰器抽象类
abstract class Decorator : IComponent
{
protected IComponent component;
public Decorator(IComponent component)
{
this.component = component;
}
public virtual void Operation()
{
component.Operation();
}
}
// 具体装饰器类
class ConcreteDecoratorA : Decorator
{
public ConcreteDecoratorA(IComponent component) : base(component) {}
public override void Operation()
{
Console.WriteLine("Before operation in ConcreteDecoratorA");
base.Operation();
Console.WriteLine("After operation in ConcreteDecoratorA");
}
}
class ConcreteDecoratorB : Decorator
{
public ConcreteDecoratorB(IComponent component) : base(component) {}
public override void Operation()
{
Console.WriteLine("Before operation in ConcreteDecoratorB");
base.Operation();
Console.WriteLine("After operation in ConcreteDecoratorB");
}
}
class Program
{
static void Main()
{
IComponent component = new ConcreteComponent();
ConcreteDecoratorA decoratorA = new ConcreteDecoratorA(component);
ConcreteDecoratorB decoratorB = new ConcreteDecoratorB(decoratorA);
decoratorB.Operation();
}
}
(五)外观模式
-
用途
为子系统中的一组接口提供一个一致的界面,简化了子系统的使用。例如在一个复杂的多媒体系统中,为用户提供一个简单的操作界面来控制音频、视频等功能。 -
优缺点
- 优点:减少了系统的相互依赖,提高了系统的安全性,隐藏了子系统的复杂性。
- 缺点:不符合开闭原则,如果要修改子系统的功能,可能需要修改外观类。
-
C# 示例
// 子系统类
class SubsystemA
{
public void OperationA()
{
Console.WriteLine("SubsystemA OperationA");
}
}
class SubsystemB
{
public void OperationB()
{
(六)享元模式
-
用途
运用共享技术有效地支持大量细粒度的对象。在某些场景下,如游戏中大量相似的图形对象或者文本处理中重复的字符对象等,可以通过共享部分属性来减少内存占用和提高性能。 -
优缺点
- 优点:减少内存占用,提高性能,特别是在创建大量相似对象的场景下效果显著。
- 缺点:增加了系统的复杂性,需要区分内部状态和外部状态,并且在外部状态管理上可能存在一定难度。
-
C# 示例
// 享元接口
interface IFlyweight
{
void Operation(int extrinsicState);
}
// 具体享元类
class ConcreteFlyweight : IFlyweight
{
private int intrinsicState;public ConcreteFlyweight(int state) { intrinsicState = state; } public void Operation(int extrinsicState) { Console.WriteLine($"Intrinsic State: {intrinsicState}, Extrinsic State: {extrinsicState}"); }
}
// 享元工厂
class FlyweightFactory
{
private Dictionary<int, IFlyweight> flyweights = new Dictionary<int, IFlyweight>();public IFlyweight GetFlyweight(int key) { if (!flyweights.ContainsKey(key)) { flyweights[key] = new ConcreteFlyweight(key); } return flyweights[key]; }
}
class Program
{
static void Main()
{
FlyweightFactory factory = new FlyweightFactory();
IFlyweight flyweight1 = factory.GetFlyweight(1);
flyweight1.Operation(10);IFlyweight flyweight2 = factory.GetFlyweight(1); flyweight2.Operation(20); IFlyweight flyweight3 = factory.GetFlyweight(2); flyweight3.Operation(30); }
}
(七)代理模式
-
用途
为其他对象提供一种代理以控制对这个对象的访问。可以用于实现远程代理、虚拟代理、保护代理等多种功能。例如在访问网络资源时通过代理来控制访问权限或者缓存数据。 -
优缺点
- 优点:可以隐藏目标对象的细节,增强安全性,实现延迟加载等功能。
- 缺点:可能会导致系统响应变慢,并且代码复杂度增加。
-
C# 示例
// 抽象主题
interface ISubject
{
void Request();
}
// 真实主题
class RealSubject : ISubject
{
public void Request()
{
Console.WriteLine("RealSubject Request");
}
}
// 代理类
class Proxy : ISubject
{
private RealSubject realSubject;public Proxy() { realSubject = new RealSubject(); } public void Request() { // 可以在调用真实主题之前或之后添加额外逻辑 Console.WriteLine("Proxy before request"); realSubject.Request(); Console.WriteLine("Proxy after request"); }
}
class Program
{
static void Main()
{
ISubject subject = new Proxy();
subject.Request();
}
}
四、行为型模式
(一)职责链模式
-
用途
使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递请求,直到有一个对象处理它为止。例如在审批流程中,不同级别的审批人员形成一个职责链来处理审批请求。 -
优缺点
- 优点:降低了对象之间的耦合度,增强了系统的可扩展性,可以动态地添加或删除处理请求的对象。
- 缺点:不能保证请求一定被处理,请求可能在链中传递过程中没有被处理,并且调试较为困难。
-
C# 示例
// 抽象处理者
abstract class Handler
{
protected Handler successor;public void SetSuccessor(Handler successor) { this.successor = successor; } public abstract void HandleRequest(int request);
}
// 具体处理者类
class ConcreteHandler1 : Handler
{
public override void HandleRequest(int request)
{
if (request >= 0 && request < 10)
{
Console.WriteLine("ConcreteHandler1 handled request {request}"); } else if (successor!= null) { successor.HandleRequest(request); } } } class ConcreteHandler2 : Handler { public override void HandleRequest(int request) { if (request >= 10 && request < 20) { Console.WriteLine("ConcreteHandler2 handled request {request}");
}
else if (successor!= null)
{
successor.HandleRequest(request);
}
}
}
class Program
{
static void Main()
{
Handler handler1 = new ConcreteHandler1();
Handler handler2 = new ConcreteHandler2();handler1.SetSuccessor(handler2); handler1.HandleRequest(5); handler1.HandleRequest(15); }
}
(二)命令模式
-
用途
将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。在图形界面应用中,如菜单项的点击操作可以封装为命令对象进行处理。 -
优缺点
- 优点:降低了系统的耦合度,新的命令可以很容易地添加到系统中,实现了请求的参数化和可撤销性。
- 缺点:可能会导致系统中存在过多的具体命令类。
-
C# 示例
// 命令接口
interface ICommand
{
void Execute();
}
// 具体命令类
class ConcreteCommand : ICommand
{
private Receiver receiver;public ConcreteCommand(Receiver receiver) { this.receiver = receiver; } public void Execute() { receiver.Action(); }
}
// 接收者类
class Receiver
{
public void Action()
{
Console.WriteLine("Receiver Action");
}
}
// 调用者类
class Invoker
{
private ICommand command;public void SetCommand(ICommand command) { this.command = command; } public void ExecuteCommand() { command.Execute(); }
}
class Program
{
static void Main()
{
Receiver receiver = new Receiver();
ICommand command = new ConcreteCommand(receiver);
Invoker invoker = new Invoker();
invoker.SetCommand(command);
invoker.ExecuteCommand();
}
}
(三)解释器模式
-
用途
给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。适用于语法比较简单且相对固定的场景,例如简单的计算器程序对表达式的解析。 -
优缺点
- 优点:可扩展性比较好,容易改变和扩展文法。实现了文法定义与执行的分离。
- 缺点:对于复杂的语法,构建解释器的代码较为复杂,并且效率可能不高。
-
C# 示例
// 抽象表达式类
abstract class Expression
{
public abstract int Interpret();
}
// 终结符表达式类
class TerminalExpression : Expression
{
private int value;public TerminalExpression(int value) { this.value = value; } public override int Interpret() { return value; }
}
// 非终结符表达式类
class NonterminalExpression : Expression
{
private Expression left;
private Expression right;public NonterminalExpression(Expression left, Expression right) { this.left = left; this.right = right; } public override int Interpret() { return left.Interpret() + right.Interpret(); }
}
class Program
{
static void Main()
{
Expression expression = new NonterminalExpression(
new TerminalExpression(5),
new TerminalExpression(3)
);
int result = expression.Interpret();
Console.WriteLine($"Result: {result}");
}
}
(四)迭代器模式
-
用途
提供一种方法顺序访问一个聚合对象中各个元素,而又不需暴露该对象的内部表示。在遍历各种集合类(如列表、数组等)时非常有用。 -
优缺点
- 优点:简化了遍历方式,统一了对不同集合的访问接口,使得代码更加通用和可维护。
- 缺点:对于一些特殊的遍历需求可能不够灵活,并且增加了一层抽象。
-
C# 示例
class Aggregate
{
private List<int> items = new List<int>();public void AddItem(int item) { items.Add(item); } public Iterator CreateIterator() { return new Iterator(this); } private class Iterator { private Aggregate aggregate; private int currentIndex = 0; public Iterator(Aggregate aggregate) { this.aggregate = aggregate; } public bool HasNext() { return currentIndex < aggregate.items.Count; } public int Next() { if (HasNext()) { return aggregate.items[currentIndex++]; } else { throw new InvalidOperationException(); } } }
}
class Program
{
static void Main()
{
Aggregate aggregate = new Aggregate();
aggregate.AddItem(1);
aggregate.AddItem(2);
aggregate.AddItem(3);Iterator iterator = aggregate.CreateIterator(); while (iterator.HasNext()) { Console.WriteLine(iterator.Next()); } }
}
(五)中介者模式
-
用途
用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。在多人聊天系统中,聊天消息通过中介者(服务器)进行转发和管理。 -
优缺点
- 优点:减少了对象之间的耦合,使得一个对象的改变不需要影响其他对象,集中控制交互。
- 缺点:中介者可能会变得过于复杂,承担过多的责任,并且可能会违反单一职责原则。
-
C# 示例
// 抽象中介者
abstract class Mediator
{
public abstract void Send(string message, Colleague colleague);
}
// 具体中介者
class ConcreteMediator : Mediator
{
private List<Colleague> colleagues = new List<Colleague>();public override void Send(string message, Colleague colleague) { foreach (var c in colleagues) { if (c!= colleague) { c.Receive(message); } } } public void Register(Colleague colleague) { colleagues.Add(colleague); }
}
// 抽象同事类
abstract class Colleague
{
protected Mediator mediator;public Colleague(Mediator mediator) { this.mediator = mediator; mediator.Register(this); } public abstract void Send(string message); public abstract void Receive(string message);
}
// 具体同事类
class ConcreteColleague : Colleague
{
public ConcreteColleague(Mediator mediator) : base(mediator) {}public override void Send(string message) { mediator.Send(message, this); } public override void Receive(string message) { Console.WriteLine($"Received message: {message}"); }
}
class Program
{
static void Main()
{
ConcreteMediator mediator = new ConcreteMediator();
ConcreteColleague colleague1 = new ConcreteColleague(mediator);
ConcreteColleague colleague2 = new ConcreteColleague(mediator);colleague1.Send("Hello from colleague 1"); }
}
(六)备忘录模式
-
用途
在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态。例如在文本编辑软件中保存文档的历史状态以便撤销操作。 -
优缺点
- 优点:可以方便地实现状态恢复,保持对象的封装性,不会破坏对象的内部结构。
- 缺点:如果状态数据量较大,可能会占用较多内存,并且管理备忘录的生命周期可能较为复杂。
-
C# 示例
// 原发器类
class Originator
{
private string state;public string State { get { return state; } set { state = value; } } public Memento CreateMemento() { return new Memento(state); } public void SetMemento(Memento memento) { state = memento.State; }
}
// 备忘录类
class Memento
{
private string state;public Memento(string state) { this.state = state; } public string State { get { return state; } }
}
// 管理者类
class Caretaker
{
private Memento memento;public Memento Memento { get { return memento; } set { memento = value; } }
}
class Program
{
static void Main()
{
Originator originator = new Originator();
originator.State = "Initial state";Caretaker caretaker = new Caretaker(); caretaker.Memento = originator.CreateMemento(); originator.State = "New state"; Console.WriteLine($"Current state: {originator.State}"); originator.SetMemento(caretaker.Memento); Console.WriteLine($"Restored state: {originator.State}"); }
}
(七)观察者模式
-
用途
定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知并自动更新。在图形界面编程中,如按钮点击后多个组件需要响应;在消息推送系统中,当有新消息时多个用户界面需要更新等场景下广泛应用。 -
优缺点
- 优点:实现了主题和观察者之间的松耦合,主题不需要知道观察者的具体类和数量,方便添加和删除观察者。
- 缺点:如果观察者过多或者更新操作复杂,可能会导致性能问题,并且在某些情况下可能出现循环依赖等问题。
-
C# 示例
// 抽象观察者
interface IObserver
{
void Update();
}
// 具体观察者类
class ConcreteObserver : IObserver
{
private string name;public ConcreteObserver(string name) { this.name = name; } public void Update() { Console.WriteLine($"{name} received update."); }
}
// 抽象主题
abstract class Subject
{
protected List<IObserver> observers = new List<IObserver>();public void Attach(IObserver observer) { observers.Add(observer); } public void Detach(IObserver observer) { observers.Remove(observer); } public abstract void Notify();
}
// 具体主题类
class ConcreteSubject : Subject
{
private string state;public string State { get { return state; } set { state = value; Notify(); } } public override void Notify() { foreach (var observer in observers) { observer.Update(); } }
}
class Program
{
static void Main()
{
ConcreteSubject subject = new ConcreteSubject();
IObserver observer1 = new ConcreteObserver("Observer 1");
IObserver observer2 = new ConcreteObserver("Observer 2");subject.Attach(observer1); subject.Attach(observer2); subject.State = "New state"; }
}
(八)状态模式
-
用途
允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它的类。在处理对象具有多种状态且不同状态下行为不同的场景时很有用,例如网络连接的不同状态(连接中、已连接、断开连接等)。 -
优缺点
- 优点:将状态的逻辑和行为封装在状态类中,使得代码结构清晰,易于维护和扩展,符合开闭原则。
- 缺点:状态类过多时可能导致代码复杂,并且状态切换的逻辑管理需要小心处理。
-
C# 示例
// 抽象状态类
abstract class State
{
public abstract void Handle(Context context);
}
// 具体状态类 A
class ConcreteStateA : State
{
public override void Handle(Context context)
{
Console.WriteLine("In State A, doing something.");
(九)策略模式
-
用途
定义一系列的算法,把它们一个个封装起来,并且使它们可以相互替换。策略模式让算法的变化独立于使用算法的客户。例如在电商系统中,根据不同的促销活动选择不同的折扣计算策略;在导航软件中,根据用户需求选择不同的路径规划策略。 -
优缺点
- 优点:算法可以自由切换,易于扩展和维护,符合开闭原则。将算法封装起来,使得代码结构更加清晰,各个算法之间相互独立,减少了代码的耦合度。
- 缺点:客户端需要了解不同策略的区别,并且需要选择合适的策略,增加了一定的复杂性。如果策略过多,类的数量也会相应增加。
-
C# 示例
// 策略接口
interface IStrategy
{
int Execute(int num1, int num2);
}
// 具体策略类 - 加法
class AddStrategy : IStrategy
{
public int Execute(int num1, int num2)
{
return num1 + num2;
}
}
// 具体策略类 - 减法
class SubtractStrategy : IStrategy
{
public int Execute(int num1, int num2)
{
return num1 - num2;
}
}
// 上下文类
class Context
{
private IStrategy strategy;public Context(IStrategy strategy) { this.strategy = strategy; } public int ExecuteStrategy(int num1, int num2) { return strategy.Execute(num1, num2); }
}
class Program
{
static void Main()
{
Context contextAdd = new Context(new AddStrategy());
int resultAdd = contextAdd.ExecuteStrategy(5, 3);
Console.WriteLine($"Add result: {resultAdd}");Context contextSubtract = new Context(new SubtractStrategy()); int resultSubtract = contextSubtract.ExecuteStrategy(5, 3); Console.WriteLine($"Subtract result: {resultSubtract}"); }
}
(十)模板方法模式
-
用途
在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。比如在数据处理流程中,有一些通用的前期准备和后期清理步骤,中间的数据处理核心步骤可以由子类实现。 -
优缺点
- 优点:提高了代码的复用性,将不变的部分在父类中实现,可变的部分留给子类实现,符合开闭原则。有助于维护代码的一致性和逻辑结构。
- 缺点:可能会限制子类的灵活性,如果父类中的某些步骤设计不合理,子类实现起来可能会比较困难。
-
C# 示例
abstract class AbstractClass
{
// 模板方法
public void TemplateMethod()
{
Step1();
Step2();
var result = Step3();
Step4(result);
}protected abstract void Step1(); protected abstract void Step2(); protected abstract int Step3(); protected abstract void Step4(int result);
}
class ConcreteClass : AbstractClass
{
protected override void Step1()
{
Console.WriteLine("ConcreteClass - Step 1");
}protected override void Step2() { Console.WriteLine("ConcreteClass - Step 2"); } protected override int Step3() { Console.WriteLine("ConcreteClass - Step 3"); return 42; } protected override void Step4(int result) { Console.WriteLine($"ConcreteClass - Step 4 with result {result}"); }
}
class Program
{
static void Main()
{
ConcreteClass concreteClass = new ConcreteClass();
concreteClass.TemplateMethod();
}
}
(十一)访问者模式
-
用途
表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。例如在对一个复杂的文档对象模型(包含文本、图片、表格等元素)进行不同类型的分析或处理时。 -
优缺点
- 优点:增加新的操作很容易,不用修改元素类本身。将数据结构和数据操作分离,使得代码的职责更加清晰。
- 缺点:破坏了对象的封装性,增加新的数据结构比较困难,并且访问者模式的代码结构相对复杂,理解和维护成本较高。
-
C# 示例
interface IElement
{
void Accept(IVisitor visitor);
}
class ConcreteElementA : IElement
{
public void Accept(IVisitor visitor)
{
visitor.Visit(this);
}
}
class ConcreteElementB : IElement
{
public void Accept(IVisitor visitor)
{
visitor.Visit(this);
}
}
interface IVisitor
{
void Visit(ConcreteElementA elementA);
void Visit(ConcreteElementB elementB);
}
class ConcreteVisitor : IVisitor
{
public void Visit(ConcreteElementA elementA)
{
Console.WriteLine("Visiting ConcreteElementA");
}public void Visit(ConcreteElementB elementB) { Console.WriteLine("Visiting ConcreteElementB"); }
}
class Program
{
static void Main()
{
IElement elementA = new ConcreteElementA();
IElement elementB = new ConcreteElementB();IVisitor visitor = new ConcreteVisitor(); elementA.Accept(visitor); elementB.Accept(visitor); }
}
(十二)链模式(Chain of Responsibility Pattern)
-
用途
使多个对象都有机会处理请求,将这些对象连成一条链,并沿着这条链传递请求,直到有一个对象处理它为止。与职责链模式类似,但可能更强调链上对象的顺序和关系。例如在一个 Web 请求处理中,经过多个过滤器或中间件的处理。 -
优缺点
- 优点:降低了请求的发送者和接收者之间的耦合度,增强了系统的灵活性和可扩展性。可以动态地组织和调整链上的处理对象。
- 缺点:请求可能在链中传递后没有得到处理,链的构建和维护可能较为复杂,并且调试难度较大。
-
C# 示例
abstract class Handler
{
protected Handler next;public void SetNext(Handler nextHandler) { next = nextHandler; } public abstract void HandleRequest(string request);
}
class ConcreteHandler1 : Handler
{
public override void HandleRequest(string request)
{
if (request.Contains("Type1"))
{
Console.WriteLine("ConcreteHandler1 handled the request.");
}
else if (next!= null)
{
next.HandleRequest(request);
}
}
}
class ConcreteHandler2 : Handler
{
public override void HandleRequest(string request)
{
if (request.Contains("Type2"))
{
Console.WriteLine("ConcreteHandler2 handled the request.");
}
else if (next!= null)
{
next.HandleRequest(request);
}
}
}
class Program
{
static void Main()
{
Handler handler1 = new ConcreteHandler1();
Handler handler2 = new ConcreteHandler2();handler1.SetNext(handler2); handler1.HandleRequest("This is a Type1 request."); handler1.HandleRequest("This is a Type2 request."); handler1.HandleRequest("This is an unknown request."); }
}
(十三)空对象模式(Null Object Pattern)
-
用途
在空对象的情况下提供默认的行为,避免空指针异常并简化代码中的条件判断。常用于处理可能返回空值的对象引用场景,比如数据库查询结果为空时的处理、函数可能返回空对象的情况等。 -
优缺点
- 优点:简化了代码中的空值检查逻辑,使代码更简洁、易读、可维护。增强了程序的稳定性,避免因空指针引发的错误。
- 缺点:需要额外创建空对象类,增加了一定的代码量。可能会掩盖一些潜在的逻辑问题,如果过度使用可能导致不易察觉的错误传播。
-
C# 示例
// 抽象对象接口
interface IObject
{
void DoSomething();
}
// 具体对象类
class RealObject : IObject
{
public void DoSomething()
{
Console.WriteLine("RealObject is doing something.");
}
}
// 空对象类
class NullObject : IObject
{
public void DoSomething()
{
// 这里可以选择什么都不做或者执行一些默认的无害操作
Console.WriteLine("This is a null object, doing nothing.");
}
}
class Program
{
static IObject GetObject(bool condition)
{
if (condition)
{
return new RealObject();
}
else
{
return new NullObject();
}
}static void Main() { IObject obj1 = GetObject(true); obj1.DoSomething(); IObject obj2 = GetObject(false); obj2.DoSomething(); }
}
(十四)规格模式(Specification Pattern)
-
用途
用于将业务规则或条件封装成可组合和复用的对象。在查询、筛选数据以及验证业务逻辑时非常有用。比如在电商系统中筛选符合特定价格范围、品牌等条件的商品。 -
优缺点
- 优点:提高了代码的可维护性和灵活性,将复杂的业务规则分离出来,使得规则可以方便地组合、修改和复用。增强了代码的可读性。
- 缺点:实现起来可能有一定的复杂性,尤其是当规则之间的组合关系变得复杂时。可能会增加一些类的数量。
-
C# 示例
interface ISpecification<T>
{
bool IsSatisfiedBy(T item);
ISpecification<T> And(ISpecification<T> other);
ISpecification<T> Or(ISpecification<T> other);
ISpecification<T> Not();
}
class GreaterThanSpecification<T> : ISpecification<T> where T : IComparable<T>
{
private T value;public GreaterThanSpecification(T compareValue) { value = compareValue; } public bool IsSatisfiedBy(T item) { return item.CompareTo(value) > 0; } public ISpecification<T> And(ISpecification<T> other) { return new AndSpecification<T>(this, other); } public ISpecification<T> Or(ISpecification<T> other) { return new OrSpecification<T>(this, other); } public ISpecification<T> Not() { return new NotSpecification<T>(this); }
}
class Program
{
static void Main()
{
int[] numbers = { 1, 5, 3, 7, 2 };ISpecification<int> greaterThanThreeSpec = new GreaterThanSpecification<int>(3); var filteredNumbers = numbers.Where(x => greaterThanThreeSpec.IsSatisfiedBy(x)); foreach (var num in filteredNumbers) { Console.WriteLine(num); } }
}
(十五)对象池模式(Object Pool Pattern)
-
用途
预先创建和维护一组可复用的对象,避免频繁地创建和销毁对象带来的性能开销。适用于创建对象成本较高或者需要频繁创建和销毁相同类型对象的场景,比如数据库连接对象池、线程池等。 -
优缺点
- 优点:减少对象创建和销毁的开销,提高系统性能,尤其是在处理高并发情况下效果显著。对资源进行有效管理和复用。
- 缺点:需要管理对象池的大小、对象的状态等,实现相对复杂。如果对象池配置不当,可能会导致资源浪费或不足。
-
C# 示例
class ObjectPool<T> where T : new()
{
private Queue<T> pool = new Queue<T>();
private int maxPoolSize;public ObjectPool(int maxSize) { maxPoolSize = maxSize; for (int i = 0; i < maxSize; i++) { pool.Enqueue(new T()); } } public T GetObject() { if (pool.Count > 0) { return pool.Dequeue(); } else { // 可以根据需求选择是否扩展池大小或采取其他策略 throw new Exception("Pool is empty."); } } public void ReturnObject(T obj) { if (pool.Count < maxPoolSize) { pool.Enqueue(obj); } }
}
class Program
{
static void Main()
{
ObjectPool<int> pool = new ObjectPool<int>(5);
int num1 = pool.GetObject();
int num2 = pool.GetObject();pool.ReturnObject(num1); int num3 = pool.GetObject(); }
}
(十六)双缓冲模式(Double Buffer Pattern)
-
用途
在图形处理、数据传输等场景中用于减少画面闪烁、提高数据处理效率。通过使用两个缓冲区,一个用于读取或显示,另一个用于写入或更新数据,避免在数据更新过程中出现不一致或闪烁的情况。 -
优缺点
- 优点:有效地解决了数据更新过程中的视觉闪烁问题,提高了用户体验。在数据处理方面,能够实现更高效的流式处理,提高系统的响应速度。
- 缺点:需要额外的内存来存储第二个缓冲区,增加了一定的内存开销。实现过程相对复杂一些,需要仔细管理两个缓冲区的切换和同步。
-
C# 示例(简单示意图形绘制中的双缓冲)
using System.Drawing;
class DoubleBufferExample
{
private Bitmap backBuffer;
private Graphics backBufferGraphics;
private Form form;public DoubleBufferExample() { form = new Form(); form.Paint += Form_Paint; backBuffer = new Bitmap(form.ClientSize.Width, form.ClientSize.Height); backBufferGraphics = Graphics.FromImage(backBuffer); Application.Run(form); } private void Form_Paint(object sender, PaintEventArgs e) { // 将后台缓冲区内容绘制到前台 e.Graphics.DrawImage(backBuffer, 0, 0); } public void UpdateAndRender() { // 在后台缓冲区进行绘制操作 backBufferGraphics.Clear(Color.White); backBufferGraphics.DrawRectangle(Pens.Black, 50, 50, 100, 100); // 触发窗体重绘,显示后台缓冲区内容 form.Invalidate(); }
}
class Program
{
static void Main()
{
DoubleBufferExample example = new DoubleBufferExample();
while (true)
{
example.UpdateAndRender();
}
}
}
(十七)基于事件的异步模式(Event-based Asynchronous Pattern)
-
用途
在处理耗时的异步操作时,通过事件机制来通知调用方操作的完成、进度更新等情况。适用于需要在后台执行任务且不阻塞主线程,同时又需要及时获取任务状态信息的场景,如文件下载、网络请求等。 -
优缺点
- 优点:提高了应用程序的响应性,避免了线程阻塞。使用事件机制使得代码结构更清晰,便于处理异步操作的结果和状态变化。
- 缺点:事件的注册和处理可能会使代码逻辑分散,如果处理不当可能会导致回调地狱等问题。调试异步代码相对复杂一些。
-
C# 示例
class AsyncOperation
{
public event EventHandler<AsyncCompletedEventArgs> Completed;public void StartAsync() { // 模拟异步操作 Thread.Sleep(2000); var args = new AsyncCompletedEventArgs(null, false, null); OnCompleted(args); } protected virtual void OnCompleted(AsyncCompletedEventArgs e) { Completed?.Invoke(this, e); }
}
class Program
{
static void Main()
{
AsyncOperation operation = new AsyncOperation();
operation.Completed += (sender, e) =>
{
Console.WriteLine("Async operation completed.");
};operation.StartAsync(); Console.ReadLine(); }
}
(十八)解释器扩展模式(Interpreter Extension Pattern)
-
用途
在解释器模式的基础上进行扩展,增加了对更多复杂语法规则或新功能的支持。常用于需要对现有语言解释器进行功能增强或扩展语法解析能力的场景,例如自定义脚本语言的扩展。 -
优缺点
- 优点:保留了解释器模式的优点,同时能够灵活地扩展语法解析功能,适应不断变化的需求。可以在不破坏原有解释器结构的基础上增加新特性。
- 缺点:实现过程相对复杂,需要深入理解原有解释器的结构和机制。扩展后的解释器可能会增加维护成本。
-
C# 示例(假设已有一个简单的数学表达式解释器,进行扩展以支持变量)
// 抽象表达式类
abstract class Expression
{
public abstract double Interpret(Dictionary<string, double> variables);
}
// 数字表达式类
class NumberExpression : Expression
{
private double number;public NumberExpression(double num) { number = num; } public override double Interpret(Dictionary<string, double> variables) { return number; }
}
// 变量表达式类
class VariableExpression : Expression
{
private string variableName;public VariableExpression(string name) { variableName = name; } public override double Interpret(Dictionary<string, double> variables) { if (variables.ContainsKey(variableName)) { return variables[variableName]; } else { throw new Exception($"Variable {variableName} not defined."); } }
}
// 加法表达式类(扩展原有功能)
class AddExpression : Expression
{
private Expression left;
private Expression right;public AddExpression(Expression leftExpr, Expression rightExpr) { left = leftExpr; right = rightExpr; } public override double Interpret(Dictionary<string, double> variables) { return left.Interpret(variables) + right.Interpret(variables); }
}
class Program
{
static void Main()
{
Dictionary<string, double> variables = new Dictionary<string, double>
{
{ "x", 5 },
{ "y", 3 }
};Expression expression = new AddExpression( new VariableExpression("x"), new VariableExpression("y") ); double result = expression.Interpret(variables); Console.WriteLine($"Result: {result}"); }
}
(十九)微核模式(Microkernel Pattern)
-
用途
将核心系统功能与扩展功能分离,核心部分保持最小化且稳定,扩展功能以插件的形式添加。适用于需要高度可扩展和可定制的系统,如操作系统内核、软件框架等。 -
优缺点
- 优点:提供了高度的可扩展性和灵活性,核心系统稳定且易于维护。可以方便地添加、删除和更新扩展功能,适应不同的需求变化。
- 缺点:设计和实现相对复杂,需要良好的插件管理机制。插件之间的通信和交互可能会带来一些性能开销和兼容性问题。
-
C# 示例(简单示意一个文本处理系统的微核架构)
// 微核接口
interface ICore
{
void Process(string text);
}
// 核心类
class Core : ICore
{
private List<IPlugin> plugins = new List<IPlugin>();public void AddPlugin(IPlugin plugin) { plugins.Add(plugin); } public void Process(string text) { // 核心的基本处理 Console.WriteLine($"Core processing: {text}"); // 调用插件进行扩展处理 foreach (var plugin in plugins) { plugin.Process(text); } }
}
// 插件接口
interface IPlugin
{
void Process(string text);
}
// 具体插件类
class SpellCheckPlugin : IPlugin
{
public void Process(string text)
{
Console.WriteLine("Spell checking...");
}
}
class Program
{
static void Main()
{
ICore core = new Core();
core.AddPlugin(new SpellCheckPlugin());core.Process("This is a sample text."); }
}
(二十)MVC 模式(Model-View-Controller Pattern)
-
用途
将应用程序分为三个主要部分:模型(Model)负责处理数据和业务逻辑;视图(View)负责展示数据给用户;控制器(Controller)负责处理用户输入并协调模型和视图之间的交互。常用于构建用户界面相关的应用程序,如 Web 应用、桌面应用等。 -
优缺点
- 优点:实现了关注点分离,提高了代码的可维护性、可扩展性和可测试性。不同部分可以独立开发和修改,便于团队协作。
- 缺点:增加了代码的复杂性和学习成本。对于简单的应用可能会有一定的过度设计。视图和控制器之间的交互有时可能会变得复杂。
-
C# 示例(简单的控制台应用模拟 MVC)
// 模型类
class Model
{
private string data;public string Data { get { return data; } set { data = value; } }
}
// 视图类
class View
{
public void DisplayData(string data)
{
Console.WriteLine($"Displaying data: {data}");
}
}
// 控制器类
class Controller
{
private Model model;
private View view;public Controller(Model model, View view) { this.model = model; this.view = view; } public void UpdateData(string newData) { model.Data = newData; view.DisplayData(model.Data); }
}
class Program
{
static void Main()
{
Model model = new Model();
View view = new View();
Controller controller = new Controller(model, view);controller.UpdateData("Hello from MVC."); }
}
(二十一)黑板模式(Blackboard Pattern)
-
用途
适用于解决没有确定算法解决方案的问题,多个不同的知识源可以协作来解决问题。知识源将信息添加到黑板上,其他知识源可以读取和处理这些信息,逐步求解问题。常用于人工智能领域中的专家系统、自然语言处理等。 -
优缺点
- 优点:能够处理复杂的、动态变化的问题,允许多个不同的组件协同工作。便于添加新的知识源来增强系统的功能。
- 缺点:实现复杂,需要良好的同步和协调机制来管理知识源对黑板的访问。可能会出现冲突和竞争条件等问题。
-
C# 示例(简单模拟一个翻译系统的黑板模式)
class Blackboard
{
private Dictionary<string, object> data = new Dictionary<string, object>();public void AddData(string key, object value) { data[key] = value; } public object GetData(string key) { if (data.ContainsKey(key)) { return data[key]; } else { return null; } }
}
class KnowledgeSource
{
private Blackboard blackboard;public KnowledgeSource(Blackboard bb) { blackboard = bb; } public void Process() { // 假设从黑板获取单词并进行简单翻译处理 object wordObj = blackboard.GetData("word"); if (wordObj!= null && wordObj is string word) { if (word == "hello") { blackboard.AddData("translation", "你好"); } } }
}
class Program
{
static void Main()
{
Blackboard blackboard = new Blackboard();
blackboard.AddData("word", "hello");KnowledgeSource source = new KnowledgeSource(blackboard); source.Process(); object translation = blackboard.GetData("translation"); Console.WriteLine($"Translation: {translation}"); }
}
(二十二)管道和过滤器模式(Pipe and Filter Pattern)
-
用途
将系统处理的数据流经一系列的过滤器进行逐步处理和转换。每个过滤器独立完成特定的功能,数据按照管道顺序依次通过各个过滤器。常用于数据处理流水线,如文本处理、图像处理、音频处理等领域,能够实现灵活的功能组合和扩展。 -
优缺点
- 优点:易于理解和扩展,各个过滤器可以独立开发、测试和维护。方便添加或删除过滤器来调整数据处理流程,增强了系统的灵活性和可维护性。
- 缺点:过滤器之间的数据传递和同步可能需要额外的机制来保证,处理复杂流程时管道的构建和管理可能会变得复杂。
-
C# 示例
interface IFilter<T>
{
T Process(T input);
}class UpperCaseFilter : IFilter<string>
{
public string Process(string input)
{
return input.ToUpper();
}
}class TrimFilter : IFilter<string>
{
public string Process(string input)
{
return input.Trim();
}
}class Pipeline<T>
{
private List<IFilter<T>> filters = new List<IFilter<T>>();public void AddFilter(IFilter<T> filter) { filters.Add(filter); } public T Process(T data) { T result = data; foreach (var filter in filters) { result = filter.Process(result); } return result; }
}
class Program
{
static void Main()
{
Pipeline<string> pipeline = new Pipeline<string>();
pipeline.AddFilter(new UpperCaseFilter());
pipeline.AddFilter(new TrimFilter());string input = " hello world "; string output = pipeline.Process(input); Console.WriteLine(output); }
}
(二十三)代理服务发现模式(Proxy Service Discovery Pattern)
-
用途
用于在分布式系统中帮助客户端发现和访问服务。代理作为中间层,缓存服务信息、进行负载均衡、提供服务发现机制等功能。适用于微服务架构中,客户端需要动态获取服务地址和进行服务调用的场景。 -
优缺点
- 优点:简化了客户端与服务端的交互逻辑,将服务发现和一些管理功能集中在代理层。可以实现负载均衡、提高系统的可靠性和可扩展性。
- 缺点:增加了系统的复杂性和额外的性能开销。代理可能成为单点故障点,如果代理出现问题可能影响整个系统的服务发现功能。
-
C# 示例
class Service
{
public void DoWork()
{
Console.WriteLine("Service is working.");
}
}class ProxyService
{
private List<Service> services = new List<Service>();
private Random random = new Random();public ProxyService() { // 初始化一些服务实例 for (int i = 0; i < 3; i++) { services.Add(new Service()); } } public void CallService() { int index = random.Next(services.Count); Service selectedService = services[index]; selectedService.DoWork(); }
}
class Program
{
static void Main()
{
ProxyService proxy = new ProxyService();
proxy.CallService();
}
}