一、设计模式的分类
总体来说设计模式分为三大类:
创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。
结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。
行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。
二、创建型模式
1、工厂方法模式
工厂方法模式(Factory Method Pattern)是一种创建型设计模式,它提供了一种将对象的实例化延迟到子类的方法。该模式定义了一个用于创建对象的接口,但由子类决定实例化哪个类。这样,工厂方法模式使得一个类的实例化延迟到其子类。
以下是工厂方法模式的一般结构:
(1)产品接口(Product):定义了产品的抽象接口,具体的产品类将实现这个接口。
(2)具体产品类(ConcreteProduct):实现了产品接口的具体产品。
(3)创建者接口(Creator):声明了工厂方法,该方法返回一个产品对象。可以包含一些默认的实现,也可以是一个抽象方法,由具体的创建者子类实现。
(4)具体创建者类(ConcreteCreator):实现了创建者接口,负责实例化具体的产品对象。
这种模式的核心思想是将对象的创建与使用分离,客户端通过调用工厂方法来创建产品,而不需要关心具体产品的实例化过程。
以下是一个简单的示例,说明工厂方法模式的基本结构:
using System;
// 1. 产品接口
interface IProduct
{
void Produce();
}
// 2. 具体产品类
class ConcreteProductA : IProduct
{
public void Produce()
{
Console.WriteLine("Product A");
}
}
// 3. 创建者接口
interface ICreator
{
IProduct FactoryMethod();
string Operation();
}
// 4. 具体创建者类
class ConcreteCreatorA : ICreator
{
public IProduct FactoryMethod()
{
return new ConcreteProductA();
}
public string Operation()
{
var product = FactoryMethod();
return $"Creator using {product.GetType().Name}";
}
}
class Program
{
static void Main()
{
// 客户端代码
ICreator creator = new ConcreteCreatorA();
string result = creator.Operation();
Console.WriteLine(result);
}
}
在这个例子中,IProduct
是产品接口,ConcreteProductA
是具体产品类。ICreator
是创建者接口,其中包含了 FactoryMethod
方法。具体的创建者类 ConcreteCreatorA
实现了该接口,通过工厂方法 FactoryMethod
返回具体的产品对象。最后,客户端代码通过创建具体的创建者类来使用工厂方法创建产品,并调用 Operation
方法。
2、抽象工厂模式
抽象工厂模式(Abstract Factory Pattern)是一种创建型设计模式,它提供一个接口用于创建一系列相关或相互依赖的对象,而无需指定它们的具体类。抽象工厂模式是工厂方法模式的扩展,它涉及到多个产品族的创建,而工厂方法模式只涉及单一产品线的创建。
以下是抽象工厂模式的一般结构:
(1)抽象工厂接口(AbstractFactory):声明创建一系列相关对象的方法,这些对象构成一个产品族。
(2)具体工厂类(ConcreteFactory):实现抽象工厂接口,负责创建一系列相关的产品。
(3)抽象产品接口(AbstractProduct):声明一系列产品的接口。
(4)具体产品类(ConcreteProduct):实现抽象产品接口,属于具体产品族的一员。
以下是一个简单的示例,说明抽象工厂模式的结构:
using System;
// 1. 抽象产品接口
interface IButton
{
void Click();
}
// 2. 具体产品类 - Windows按钮
class WindowsButton : IButton
{
public void Click()
{
Console.WriteLine("Windows button clicked");
}
}
// 3. 具体产品类 - MacOS按钮
class MacOSButton : IButton
{
public void Click()
{
Console.WriteLine("MacOS button clicked");
}
}
// 4. 抽象工厂接口
interface IUIFactory
{
IButton CreateButton();
}
// 5. 具体工厂类 - Windows工厂
class WindowsUIFactory : IUIFactory
{
public IButton CreateButton()
{
return new WindowsButton();
}
}
// 6. 具体工厂类 - MacOS工厂
class MacOSUIFactory : IUIFactory
{
public IButton CreateButton()
{
return new MacOSButton();
}
}
class Program
{
static void Main()
{
// 客户端代码
IUIFactory windowsFactory = new WindowsUIFactory();
IButton windowsButton = windowsFactory.CreateButton();
windowsButton.Click();
IUIFactory macosFactory = new MacOSUIFactory();
IButton macosButton = macosFactory.CreateButton();
macosButton.Click();
}
}
3、单例模式
单例模式是一种创建型设计模式,其主要目的是确保一个类只有一个实例,并提供一个全局访问点。这对于需要在整个应用程序中共享状态或控制某个资源的情况非常有用。
以下是一个简单的C#实现单例模式的例子:
public class Singleton
{
// 静态变量instance用于保存类的唯一实例
private static Singleton instance;
// 私有构造函数,防止外部直接实例化对象
private Singleton()
{
// 可以在这里进行一些初始化操作
}
// 提供一个全局访问点,用于获取类的唯一实例
public static Singleton Instance
{
get
{
// 如果实例为null,则创建一个新实例;否则,返回已有的实例
if (instance == null)
{
instance = new Singleton();
}
return instance;
}
}
// 其他成员方法和属性
public void SomeMethod()
{
Console.WriteLine("Executing some method in Singleton");
}
}
class Program
{
static void Main()
{
// 获取单例实例
Singleton singletonInstance1 = Singleton.Instance;
Singleton singletonInstance2 = Singleton.Instance;
// 验证是否是同一个实例
Console.WriteLine(singletonInstance1 == singletonInstance2); // 输出 true
// 调用单例中的方法
singletonInstance1.SomeMethod();
}
}
在这个例子中,Singleton
类有一个私有的静态变量 instance
,用于保存类的唯一实例。构造函数是私有的,以防止外部直接实例化对象。通过 Instance
属性,我们提供了一个全局访问点,用于获取类的唯一实例。在 Instance
属性的 get
方法中,如果实例为 null
,则创建一个新实例;否则,返回已有的实例。这确保了在应用程序中只有一个 Singleton
实例。
4、建造者模式
建造者模式(Builder Pattern)是一种创建型设计模式,它的主要目的是将一个复杂对象的构建过程和其表示分离,使得同样的构建过程可以创建不同的表示。通过将构建过程拆分为多个步骤,建造者模式使得客户端能够根据需要选择构建的步骤,以及在每个步骤中选择不同的实现。
建造者模式一般包含以下几个主要组成部分:
(1)产品类(Product):
定义了要构建的复杂对象的属性和方法。
(2)抽象建造者接口(Builder):
声明了构建产品的抽象方法,包括各个部分的构建方法以及返回产品的方法。
(3)具体建造者类(ConcreteBuilder):
实现了抽象建造者接口,负责实际构建产品的每个部分。
提供获取最终产品的方法。
(4)指导者类(Director):
控制构建过程的顺序,调用具体建造者的方法来构建产品。
可以根据需要变换具体建造者,以得到不同的产品表示。
(5)客户端代码:
创建具体的建造者对象和指导者对象。
通过指导者控制构建过程,获取最终构建好的产品。
以下是一个简单的C#实现建造者模式的例子:
// 1. 产品类
class Product
{
public string Part1 { get; set; }
public string Part2 { get; set; }
public void Display()
{
Console.WriteLine($"Part1: {Part1}, Part2: {Part2}");
}
}
// 2. 抽象建造者接口
interface IBuilder
{
void BuildPart1();
void BuildPart2();
Product GetResult();
}
// 3. 具体建造者类
class ConcreteBuilder : IBuilder
{
private Product product = new Product();
public void BuildPart1()
{
product.Part1 = "Part1 built";
}
public void BuildPart2()
{
product.Part2 = "Part2 built";
}
public Product GetResult()
{
return product;
}
}
// 4. 指导者类
class Director
{
public void Construct(IBuilder builder)
{
builder.BuildPart1();
builder.BuildPart2();
}
}
class Program
{
static void Main()
{
// 客户端代码
IBuilder builder = new ConcreteBuilder();
Director director = new Director();
director.Construct(builder);
Product product = builder.GetResult();
// 显示构建的产品
product.Display();
}
}
在上述例子中,Product
是最终构建的对象,IBuilder
是抽象建造者接口,定义了构建产品的方法。ConcreteBuilder
是具体建造者类,实现了具体的构建步骤。Director
是指导者类,负责控制构建过程的顺序。客户端代码通过创建具体的建造者对象和指导者对象,可以根据需要定制构建过程,最终得到构建好的产品。
建造者模式的优势在于能够灵活地创建不同表示的对象,同时隐藏了构建的具体过程。这使得客户端代码不需要知道对象的内部结构,只需通过指导者和具体的建造者进行构建。
5、原型模式
原型模式(Prototype Pattern)是一种创建型设计模式,其主要思想是通过复制现有对象来创建新的对象,而不是通过实例化新的对象。这种模式通常用于需要创建多个相似对象的场景,以减少资源消耗和提高性能。
在原型模式中,一个对象充当原型,其他对象通过复制这个原型来创建新的对象。通常,原型对象实现一个克隆方法,该方法用于创建并返回当前对象的副本。复制可以是浅复制或深复制,具体取决于对象的结构和需求。
原型模式一般包含以下几个主要组成部分:
(1)原型接口(Prototype):
声明了克隆方法 Clone 的接口,是所有具体原型类的公共接口。
(2)具体原型类(ConcretePrototype):
实现了原型接口,提供了一个克隆方法。
具体原型类通常包含复杂的内部结构,可以包含值类型和引用类型成员。
(3)客户端(Client):
使用原型模式的客户端,通过调用原型对象的克隆方法创建新的对象。
不直接通过实例化构造新对象,而是通过克隆现有对象。
以下是一个简单的C#实现原型模式的例子:
using System;
// 1. 原型接口
interface IPrototype
{
IPrototype Clone();
}
// 2. 具体原型类
class ConcretePrototype : IPrototype
{
public int Data { get; set; }
public ConcretePrototype(int data)
{
Data = data;
}
// 实现克隆方法
public IPrototype Clone()
{
// 使用MemberwiseClone方法进行浅复制
return (IPrototype)MemberwiseClone();
}
}
class Program
{
static void Main()
{
// 客户端代码
ConcretePrototype prototype = new ConcretePrototype(10);
ConcretePrototype clone = (ConcretePrototype)prototype.Clone();
// 验证是否是克隆对象
Console.WriteLine(prototype == clone); // 输出 False,因为是不同的对象
Console.WriteLine(prototype.Data == clone.Data); // 输出 True,因为数据相同
}
}
在上述例子中,IPrototype
是原型接口,定义了一个 Clone
方法用于复制对象。ConcretePrototype
是具体原型类,实现了 IPrototype
接口,并在克隆方法中使用了 MemberwiseClone
进行浅复制。
在客户端代码中,首先创建一个原型对象 prototype
,然后通过克隆方法创建一个新的对象 clone
。验证 clone
是一个新的对象,但其数据与原型相同。
需要注意的是,MemberwiseClone
进行的是浅复制,即对象内部的引用类型成员仍然是相同的引用。如果对象包含引用类型的成员,并且需要进行深复制,就需要在克隆方法中自行处理这些成员的复制。
三、结构型模式
1、适配器模式
适配器模式(Adapter Pattern)是一种结构型设计模式,它允许接口不兼容的类之间进行协作。适配器模式充当两个不兼容接口之间的桥梁,使得它们能够在一起工作。
适配器模式的主要目标是将一个类的接口转换成客户端所期望的接口。这可以通过两种方式实现:类适配器模式和对象适配器模式。
类适配器模式:
- 类适配器使用多重继承,继承目标接口并关联原始类,实现目标接口中的方法,同时调用原始类的方法。这样,适配器就能够适应目标接口,并且可以调用原始类的功能。
对象适配器模式:
- 对象适配器使用组合,将原始类的实例嵌套在适配器中,同时实现目标接口。适配器通过委托原始类的方法来实现目标接口的方法,从而使得原始类与目标接口兼容。
以下是一个简单的C#示例,演示对象适配器模式:
// 目标接口
interface ITarget
{
void Request();
}
// 原始类
class Adaptee
{
public void SpecificRequest()
{
Console.WriteLine("Specific Request");
}
}
// 对象适配器
class Adapter : ITarget
{
private Adaptee adaptee;
public Adapter(Adaptee adaptee)
{
this.adaptee = adaptee;
}
public void Request()
{
// 通过委托调用原始类的方法
adaptee.SpecificRequest();
}
}
class Program
{
static void Main()
{
// 客户端代码
Adaptee adaptee = new Adaptee();
ITarget target = new Adapter(adaptee);
// 调用目标接口的方法,实际上会调用原始类的方法
target.Request();
}
}
在上述例子中,ITarget
是目标接口,Adaptee
是原始类,Adapter
是适配器。适配器实现了目标接口,并在其内部持有一个原始类的实例。当客户端通过目标接口调用 Request
方法时,实际上会委托给原始类的 SpecificRequest
方法,从而实现了适配。
2、装饰器模式
装饰器模式(Decorator Pattern)是一种结构型设计模式,它允许向一个对象动态添加新的行为,而无需改变其原始类的结构。装饰器模式通过创建一个包装类,该类包装了原始类的实例,从而在运行时向对象添加新的功能。
该模式通常用于以下情况:
- 当需要扩展一个类的功能,但不希望通过子类进行扩展或者创建太多子类时。
- 当要求动态地、透明地向对象添加功能,且这些功能可以被组合和排列时。
以下是一个简单的C#实现装饰器模式的例子:
using System;
// 1. 抽象组件接口
interface IComponent
{
void Operation();
}
// 2. 具体组件类
class ConcreteComponent : IComponent
{
public void Operation()
{
Console.WriteLine("ConcreteComponent operation");
}
}
// 3. 抽象装饰器类
abstract class Decorator : IComponent
{
protected IComponent component;
public Decorator(IComponent component)
{
this.component = component;
}
public virtual void Operation()
{
if (component != null)
{
component.Operation();
}
}
}
// 4. 具体装饰器类A
class ConcreteDecoratorA : Decorator
{
public ConcreteDecoratorA(IComponent component) : base(component) { }
public override void Operation()
{
base.Operation();
AddedBehaviorA();
}
void AddedBehaviorA()
{
Console.WriteLine("ConcreteDecoratorA added behavior");
}
}
// 5. 具体装饰器类B
class ConcreteDecoratorB : Decorator
{
public ConcreteDecoratorB(IComponent component) : base(component) { }
public override void Operation()
{
base.Operation();
AddedBehaviorB();
}
void AddedBehaviorB()
{
Console.WriteLine("ConcreteDecoratorB added behavior");
}
}
class Program
{
static void Main()
{
// 客户端代码
IComponent component = new ConcreteComponent();
// 使用装饰器A包装组件
IComponent decoratedComponentA = new ConcreteDecoratorA(component);
decoratedComponentA.Operation();
Console.WriteLine("-----");
// 使用装饰器B包装组件
IComponent decoratedComponentB = new ConcreteDecoratorB(component);
decoratedComponentB.Operation();
}
}
在这个例子中,IComponent
是抽象组件接口,ConcreteComponent
是具体组件类。Decorator
是抽象装饰器类,它包含一个指向 IComponent
的引用,并实现了 Operation
方法。ConcreteDecoratorA
和 ConcreteDecoratorB
是具体装饰器类,它们分别添加了不同的行为。
客户端代码可以使用不同的装饰器来动态地扩展组件的功能。通过组合不同的装饰器,可以实现不同的组合效果。这样的设计使得增加新的功能变得灵活而透明,同时不需要修改原始组件的代码。
3、代理模式
代理模式是一种结构型设计模式,其主要目的是通过引入代理对象来控制对其他对象的访问。代理对象通常充当客户端和目标对象之间的中介,以实现对目标对象的间接访问。
代理模式有多种形式,其中包括:
- 静态代理:在编译时就已经确定代理关系。
- 动态代理:在运行时动态创建代理对象,根据需要代理不同的目标对象。
以下是一个简单的C#实现代理模式的例子,使用动态代理:
using System;
using System.Reflection;
using System.Reflection.Emit;
// 1. 主题接口
interface ISubject
{
void Request();
}
// 2. 具体主题类
class RealSubject : ISubject
{
public void Request()
{
Console.WriteLine("RealSubject handles the request");
}
}
// 3. 代理类
class DynamicProxy : DispatchProxy, ISubject
{
private ISubject realSubject;
protected override object Invoke(MethodInfo targetMethod, object[] args)
{
Console.WriteLine($"Before invoking {targetMethod.Name}");
object result = targetMethod.Invoke(realSubject, args);
Console.WriteLine($"After invoking {targetMethod.Name}");
return result;
}
public static ISubject CreateProxy(ISubject realSubject)
{
object proxy = Create<DynamicProxy, ISubject>();
((DynamicProxy)proxy).realSubject = realSubject;
return (ISubject)proxy;
}
}
class Program
{
static void Main()
{
// 客户端代码
ISubject realSubject = new RealSubject();
ISubject proxy = DynamicProxy.CreateProxy(realSubject);
// 通过代理对象调用真实主题对象的方法
proxy.Request();
}
}
在这个例子中,ISubject
是主题接口,RealSubject
是具体主题类,实现了 ISubject
接口。DynamicProxy
是代理类,继承自 DispatchProxy
类,并实现了 ISubject
接口。在 Invoke
方法中,可以在调用真实主题对象的方法前后添加额外的逻辑。DynamicProxy
类使用 DispatchProxy.Create
方法创建代理对象,并通过 CreateProxy
方法返回代理对象。
客户端代码中创建了一个真实主题对象 realSubject
,然后通过 DynamicProxy.CreateProxy
创建了一个代理对象 proxy
。通过代理对象调用 Request
方法时,额外的逻辑会在方法调用前后执行,从而实现了代理的效果。
4、外观模式
外观模式(Facade Pattern)是一种结构型设计模式,其主要目的是为复杂系统提供一个简化的接口,隐藏系统的复杂性,并向客户端提供一个更方便的入口点。
外观模式通过定义一个高层接口,将子系统的一组接口集成在一起,从而提供了一个更简单的接口供客户端使用。这有助于降低客户端与系统之间的耦合度,使得客户端无需了解系统的内部实现细节,只需通过外观接口与系统交互。
以下是一个简单的C#实现外观模式的例子:
using System;
// 子系统A
class SubsystemA
{
public void OperationA()
{
Console.WriteLine("SubsystemA OperationA");
}
}
// 子系统B
class SubsystemB
{
public void OperationB()
{
Console.WriteLine("SubsystemB OperationB");
}
}
// 子系统C
class SubsystemC
{
public void OperationC()
{
Console.WriteLine("SubsystemC OperationC");
}
}
// 外观类
class Facade
{
private SubsystemA subsystemA;
private SubsystemB subsystemB;
private SubsystemC subsystemC;
public Facade()
{
subsystemA = new SubsystemA();
subsystemB = new SubsystemB();
subsystemC = new SubsystemC();
}
public void PerformOperations()
{
Console.WriteLine("Facade performing operations:");
subsystemA.OperationA();
subsystemB.OperationB();
subsystemC.OperationC();
}
}
class Program
{
static void Main()
{
// 客户端代码
Facade facade = new Facade();
facade.PerformOperations();
}
}
在这个例子中,SubsystemA
、SubsystemB
、和 SubsystemC
是子系统,它们包含了复杂的实现细节。Facade
是外观类,将子系统的操作整合在一个高层接口中,供客户端使用。
客户端代码通过创建一个 Facade
对象,然后调用 PerformOperations
方法,就可以执行整个子系统的一系列操作。这样,客户端无需了解子系统的具体实现,只需通过外观接口与系统进行交互,从而降低了客户端与系统之间的耦合度。
外观模式在以下情况下通常被使用:
- 当一个系统的复杂性增加,而客户端只需要与系统的某一部分交互时。
- 当需要将系统的内部实现细节隐藏,以提供更简单的接口供客户端使用时。
- 当希望构建一个可以重构的系统,使得系统内部变化对客户端影响最小时。
5、桥接模式
桥接模式(Bridge Pattern)是一种结构型设计模式,其主要目的是将抽象部分与实现部分分离,使它们可以独立变化,降低它们之间的耦合性。
在桥接模式中,抽象部分和实现部分分别由两个独立的层次结构组成,抽象部分包含一个实现部分的引用,通过这种方式,可以在运行时将不同的抽象部分和实现部分组合起来。
以下是一个简单的C#实现桥接模式的例子:
using System;
// 实现部分的接口
interface IImplementor
{
void OperationImp();
}
// 具体实现部分A
class ConcreteImplementorA : IImplementor
{
public void OperationImp()
{
Console.WriteLine("ConcreteImplementorA OperationImp");
}
}
// 具体实现部分B
class ConcreteImplementorB : IImplementor
{
public void OperationImp()
{
Console.WriteLine("ConcreteImplementorB OperationImp");
}
}
// 抽象部分
abstract class Abstraction
{
protected IImplementor implementor;
public Abstraction(IImplementor implementor)
{
this.implementor = implementor;
}
public abstract void Operation();
}
// 扩充抽象部分A
class RefinedAbstractionA : Abstraction
{
public RefinedAbstractionA(IImplementor implementor) : base(implementor) { }
public override void Operation()
{
Console.WriteLine("RefinedAbstractionA Operation");
implementor.OperationImp();
}
}
// 扩充抽象部分B
class RefinedAbstractionB : Abstraction
{
public RefinedAbstractionB(IImplementor implementor) : base(implementor) { }
public override void Operation()
{
Console.WriteLine("RefinedAbstractionB Operation");
implementor.OperationImp();
}
}
class Program
{
static void Main()
{
// 客户端代码
IImplementor implementorA = new ConcreteImplementorA();
IImplementor implementorB = new ConcreteImplementorB();
Abstraction abstractionA = new RefinedAbstractionA(implementorA);
abstractionA.Operation();
Abstraction abstractionB = new RefinedAbstractionB(implementorB);
abstractionB.Operation();
}
}
在这个例子中,IImplementor
是实现部分的接口,ConcreteImplementorA
和 ConcreteImplementorB
是具体的实现部分。Abstraction
是抽象部分的抽象类,包含一个对实现部分的引用。RefinedAbstractionA
和 RefinedAbstractionB
是扩充抽象部分的类,它们继承自 Abstraction
并实现了具体的业务逻辑。
通过这种方式,实现部分和抽象部分可以独立变化,可以在运行时动态组合不同的实现部分和抽象部分,实现不同的业务组合。桥接模式有助于降低系统中两个独立变化的维度之间的耦合性。
6、组合模式
组合模式是一种结构型设计模式,其主要目的是将对象组合成树形结构以表示“部分-整体”的层次结构。组合模式使得客户端可以像对待单个对象一样对待组合对象和叶子对象,从而使得组合对象和叶子对象对客户端的透明性增加。
组合模式有两种主要类型的对象:
- 叶子对象(Leaf):是组合中的基本元素,它不包含其他子对象。
- 组合对象(Composite):包含其他子对象,可以是叶子对象,也可以是其他组合对象。
以下是一个简单的C#实现组合模式的例子:
using System;
using System.Collections.Generic;
// 抽象组件
abstract class Component
{
public string Name { get; protected set; }
public Component(string name)
{
Name = name;
}
public abstract void Display();
}
// 叶子对象
class Leaf : Component
{
public Leaf(string name) : base(name) { }
public override void Display()
{
Console.WriteLine($"Leaf: {Name}");
}
}
// 组合对象
class Composite : Component
{
private List<Component> children = new List<Component>();
public Composite(string name) : base(name) { }
public void Add(Component component)
{
children.Add(component);
}
public void Remove(Component component)
{
children.Remove(component);
}
public override void Display()
{
Console.WriteLine($"Composite: {Name}");
foreach (var child in children)
{
child.Display();
}
}
}
class Program
{
static void Main()
{
// 客户端代码
Component root = new Composite("Root");
Component branch1 = new Composite("Branch1");
branch1.Add(new Leaf("Leaf1.1"));
branch1.Add(new Leaf("Leaf1.2"));
Component branch2 = new Composite("Branch2");
branch2.Add(new Leaf("Leaf2.1"));
branch2.Add(new Leaf("Leaf2.2"));
root.Add(branch1);
root.Add(branch2);
root.Display();
}
}
在这个例子中,Component
是抽象组件,Leaf
是叶子对象,Composite
是组合对象。组合对象可以包含其他组件,无论是叶子对象还是其他组合对象。客户端代码可以递归地调用组合对象的 Display
方法,从而透明地处理组合结构。
组合模式适用于以下情况:
- 当客户端希望对叶子对象和组合对象进行一致的操作时。
- 当希望用户忽略组合对象和叶子对象之间的差异时,统一使用相同的接口。
- 当希望在组合对象和叶子对象上面建立一个递归的组合结构时。
组合模式的主要优点在于能够简化客户端代码,使得客户端无需区分叶子对象和组合对象。同时,组合模式也有助于新增新的组件类型,而不需要修改现有的代码。
7、享元模式
享元模式(Flyweight Pattern)是一种结构型设计模式,其主要目的是通过共享对象来减少内存使用和提高性能。在享元模式中,相似的对象共享相同的部分,而不是每个对象都保存一份相同的信息。
享元模式适用于以下情况:
- 应用程序使用了大量的相似对象。
- 对象的大多数状态可以变为外部状态,而剩余部分可以共享。
- 对象的创建和销毁会频繁发生。
以下是一个简单的C#实现享元模式的例子:
using System;
using System.Collections.Generic;
// 享元接口
interface IFlyweight
{
void Operation();
}
// 具体享元类
class ConcreteFlyweight : IFlyweight
{
private string intrinsicState;
public ConcreteFlyweight(string intrinsicState)
{
this.intrinsicState = intrinsicState;
}
public void Operation()
{
Console.WriteLine($"ConcreteFlyweight: {intrinsicState}");
}
}
// 享元工厂
class FlyweightFactory
{
private Dictionary<string, IFlyweight> flyweights = new Dictionary<string, IFlyweight>();
public IFlyweight GetFlyweight(string 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("A");
flyweight1.Operation();
IFlyweight flyweight2 = factory.GetFlyweight("B");
flyweight2.Operation();
IFlyweight flyweight3 = factory.GetFlyweight("A");
flyweight3.Operation();
// 输出结果表明,flyweight1 和 flyweight3 共享相同的 ConcreteFlyweight 实例
}
}
在这个例子中,IFlyweight
是享元接口,ConcreteFlyweight
是具体享元类。FlyweightFactory
是享元工厂,负责创建和管理享元对象。客户端通过工厂获取享元对象,并调用其 Operation
方法。
在客户端代码中,flyweight1
和 flyweight3
共享了相同的 ConcreteFlyweight
实例,因为它们传递的相同的内部状态 "
A"。这样,通过共享对象,可以减少内存使用,提高性能。
四、行为型模式
1、策略模式
策略模式(Strategy Pattern)是一种行为设计模式,它定义了算法族,分别封装起来,使它们之间可以互相替换。策略模式让算法的变化独立于使用算法的客户端。
主要参与角色:
- 环境类(Context): 持有一个策略类的引用,可以切换不同的策略。
- 抽象策略类(Strategy): 定义了算法的接口,所有具体策略类都要实现这个接口。
- 具体策略类(Concrete Strategy): 实现了抽象策略类定义的接口,封装了具体的算法。
优点:
- 易于扩展: 可以灵活地增加新的策略类,而不需要修改现有代码。
- 避免条件语句: 策略模式消除了大量的条件语句,使代码更加清晰。
- 提高代码复用性: 可以将一些通用的算法封装成策略类,多个环境类共享。
示例代码:
假设有一个商场系统,需要计算商品的折扣价格,可以使用策略模式。首先,定义抽象策略类:
// 抽象策略类
public interface IDiscountStrategy
{
double ApplyDiscount(double price);
}
然后,实现具体的策略类:
// 具体策略类1
public class RegularDiscountStrategy : IDiscountStrategy
{
public double ApplyDiscount(double price)
{
return price * 0.95; // 5% off for regular customers
}
}
// 具体策略类2
public class VIPDiscountStrategy : IDiscountStrategy
{
public double ApplyDiscount(double price)
{
return price * 0.85; // 15% off for VIP customers
}
}
接下来,定义环境类:
// 环境类
public class ShoppingCart
{
private IDiscountStrategy discountStrategy;
public ShoppingCart(IDiscountStrategy strategy)
{
this.discountStrategy = strategy;
}
public double CalculateTotal(double totalPrice)
{
return discountStrategy.ApplyDiscount(totalPrice);
}
}
使用策略模式的示例:
var regularCustomer = new RegularDiscountStrategy();
var vipCustomer = new VIPDiscountStrategy();
var regularCart = new ShoppingCart(regularCustomer);
var vipCart = new ShoppingCart(vipCustomer);
double regularTotal = regularCart.CalculateTotal(100.0);
double vipTotal = vipCart.CalculateTotal(100.0);
Console.WriteLine("Regular customer total: " + regularTotal); // 输出:95.0
Console.WriteLine("VIP customer total: " + vipTotal); // 输出:85.0
在这个例子中,ShoppingCart
是环境类,可以根据不同的策略(折扣算法)进行计算总价。客户端可以根据需要选择不同的具体策略,而不影响ShoppingCart
的使用。这就是策略模式的核心思想。
2、模板方法模式
模板方法模式(Template Method Pattern)是一种行为设计模式,它定义了一个算法的骨架,将一些步骤的具体实现延迟到子类。模板方法使得子类可以在不改变算法结构的情况下重新定义算法的某些步骤。
主要参与角色:
- 抽象类(Abstract Class): 定义算法骨架,其中包含一个或多个抽象方法,这些方法由子类实现。
- 具体类(Concrete Class): 实现抽象类中定义的抽象方法,完成算法的具体步骤。
优点:
- 复用性: 具体的步骤由子类实现,使得算法的具体实现可以灵活变化。
- 扩展性: 新的算法可以通过增加新的具体类来扩展,而不需要修改原有的算法骨架。
示例代码:
假设有一个制作咖啡的模板方法,其中包含冲泡咖啡的步骤,如烧水、冲泡、加料。这个过程可以用模板方法模式来实现。
// 抽象类
public abstract class CoffeeTemplate
{
// 模板方法
public void MakeCoffee()
{
BoilWater();
BrewCoffeeGrounds();
PourInCup();
AddCondiments();
}
// 抽象步骤
protected abstract void BoilWater();
protected abstract void BrewCoffeeGrounds();
protected abstract void PourInCup();
protected abstract void AddCondiments();
}
// 具体类1
public class CoffeeWithHook : CoffeeTemplate
{
protected override void BoilWater()
{
Console.WriteLine("Boiling water");
}
protected override void BrewCoffeeGrounds()
{
Console.WriteLine("Brewing coffee grounds");
}
protected override void PourInCup()
{
Console.WriteLine("Pouring coffee into cup");
}
protected override void AddCondiments()
{
Console.WriteLine("Adding sugar and milk");
}
}
// 具体类2
public class CoffeeWithoutHook : CoffeeTemplate
{
protected override void BoilWater()
{
Console.WriteLine("Boiling water");
}
protected override void BrewCoffeeGrounds()
{
Console.WriteLine("Brewing coffee grounds");
}
protected override void PourInCup()
{
Console.WriteLine("Pouring coffee into cup");
}
protected override void AddCondiments()
{
// 不添加调料
}
}
在这个例子中,CoffeeTemplate
是抽象类,定义了冲泡咖啡的步骤,并包含一个模板方法MakeCoffee
。具体的步骤由子类实现。CoffeeWithHook
和CoffeeWithoutHook
是两个具体的子类,分别实现了添加和不添加调料的步骤。客户端可以通过调用MakeCoffee
方法来制作咖啡,而不关心具体的步骤。这就是模板方法模式的应用。
3、观察者模式
观察者模式(Observer Pattern)是一种行为设计模式,它定义了一种一对多的依赖关系,使得当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并自动更新。观察者模式中有两个主要角色:观察者和被观察者。
主要参与角色:
- 被观察者(Subject): 也称为主题或可观察对象,维护一组观察者对象,提供添加、删除观察者以及通知观察者的方法。
- 观察者(Observer): 定义一个更新接口,用于在被观察者状态发生变化时得到通知。
- 具体被观察者(Concrete Subject): 实现被观察者接口,维护一组观察者,并在状态变化时通知它们。
- 具体观察者(Concrete Observer): 实现观察者接口,存储与被观察者的关联关系,以便在状态变化时得到通知并执行相应的操作。
优点:
- 松耦合: 被观察者和观察者之间是松散耦合的,它们之间并不直接依赖于彼此,可以独立变化。
- 可扩展性: 可以轻松地添加新的观察者和被观察者,扩展系统功能。
示例代码:
假设有一个简单的气象站,观察者模式可以用来通知不同的显示器(观察者),以便它们可以更新显示最新的气象信息。首先,定义观察者接口:
// 观察者接口
public interface IObserver
{
void Update(string temperature, string humidity, string pressure);
}
然后,定义被观察者接口:
// 被观察者接口
public interface ISubject
{
void RegisterObserver(IObserver observer);
void RemoveObserver(IObserver observer);
void NotifyObservers();
}
接着,实现具体的观察者和被观察者:
// 具体观察者
public class Display : IObserver
{
public void Update(string temperature, string humidity, string pressure)
{
Console.WriteLine($"Display: Temperature - {temperature}, Humidity - {humidity}, Pressure - {pressure}");
}
}
// 具体被观察者
public class WeatherStation : ISubject
{
private List<IObserver> observers = new List<IObserver>();
private string temperature;
private string humidity;
private string pressure;
public void RegisterObserver(IObserver observer)
{
observers.Add(observer);
}
public void RemoveObserver(IObserver observer)
{
observers.Remove(observer);
}
public void NotifyObservers()
{
foreach (var observer in observers)
{
observer.Update(temperature, humidity, pressure);
}
}
public void SetMeasurements(string temperature, string humidity, string pressure)
{
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
NotifyObservers();
}
}
在这个例子中,WeatherStation
是具体的被观察者,负责维护观察者列表并在状态变化时通知它们。Display
是具体的观察者,负责更新并显示气象信息。
使用观察者模式的示例:
WeatherStation weatherStation = new WeatherStation();
Display display1 = new Display();
Display display2 = new Display();
weatherStation.RegisterObserver(display1);
weatherStation.RegisterObserver(display2);
weatherStation.SetMeasurements("25°C", "60%", "1012hPa");
在这个例子中,当气象信息发生变化时,WeatherStation
通知所有注册的观察者(Display
对象),它们将相应地更新并显示最新的气象信息。