文章目录
结构型设计模式
一、外观模式
外观模式(Facade Pattern) 是一种结构型设计模式,它通过为复杂的子系统提供一个统一的接口,来简化客户端与系统之间的交互。
1、定义
外观模式提供了一个简化的接口给客户端,用于访问子系统中的一群接口。客户端不需要直接与子系统中的各个组件交互,而是通过外观类来间接访问子系统。
2、结构
外观模式主要包含以下几个角色:
- 外观角色(Facade):这是模式的核心角色,它提供了一个简化的接口,用于访问子系统中的功能。外观类封装了子系统的复杂性,使得外部客户端无需了解内部细节就能进行交互。
- 子系统角色(Subsystem):这些是实际执行具体任务的类或模块。它们可能包含多个类和更复杂的逻辑,对于客户端来说,直接与这些子系统交互可能会非常复杂。
- 客户角色(Client):客户端使用外观类提供的接口与子系统进行交互。通过这种方式,客户端可以简化其代码,因为它只需要与外观类打交道,而不是直接与复杂的子系统打交道。
3、优点
- 简化接口:客户端只需与外观类交互,无需了解系统的复杂性。
- 解耦客户端和子系统:外观类作为中介者,降低了客户端和子系统之间的耦合度。
- 提高灵活性:可以随时修改外观类以适应系统变化,而不会影响客户端代码。
- 提高安全性:当系统需要对外封闭时,外观模式可以提供一个统一的接口,有效防止外部直接访问子系统内部。
- 降低编译依赖性:简化了系统在不同平台之间的移植过程,因为编译一个子系统不会影响其他的子系统,也不会影响外观对象。
4、缺点
- 不符合“开闭原则”:如果新增子系统或删除子系统,可能需要修改外观类的代码,这在一定程度上违反了“开闭原则”。
- 可能隐藏了子系统的复杂性:如果外观类设计得过于复杂,可能会隐藏子系统的复杂性,使得客户端难以理解和使用。
- 封装过度导致灵活性降低:如果外观类封装了过多的子系统功能,可能会导致其变得过于庞大和复杂,反而增加了理解和维护的难度。
5、应用场景
- 当系统的某一子系统变得过于复杂,不容易使用时,可以使用外观模式进行简化。
- 当系统中存在多个接口之间的依赖关系比较复杂时,外观模式可以进行封装,将复杂性内部化,从而简化其使用和维护。
- 当系统需要对外封闭,外界只能通过一个统一的接口来访问系统时,可以使用外观模式进行封装。
- 当系统需要进行重构,需要对原有的代码进行优化和改进时,可以使用外观模式进行重构,使得代码更加易于理解和维护。
6、示例
以计算机启动过程为例,CPU、内存、硬盘等是复杂的子系统,而计算机类(Computer)可以视为一个外观类,它封装了这些子系统的复杂交互过程,并提供了一个简单的启动接口给客户端使用。这样,客户端只需要调用计算机类的启动方法,而无需关心内部具体的实现细节。
综上所述,外观模式是一种非常实用的设计模式,它通过提供一个统一的接口来简化客户端与系统之间的交互,从而提高了系统的易用性和可维护性。
下面是一个简单的C#代码示例,展示了外观模式(Facade Pattern)的应用。在这个例子中,我们假设有一个复杂的系统,该系统由几个不同的组件组成,比如Database
、Logger
和Security
。我们将创建一个SystemFacade
类来封装这些组件的复杂性,并提供一个简单的接口给客户端使用。
// Database 组件的接口和类
public interface IDatabase
{
void Connect();
void Disconnect();
void ExecuteQuery(string query);
}
public class Database : IDatabase
{
public void Connect()
{
Console.WriteLine("Database connected.");
}
public void Disconnect()
{
Console.WriteLine("Database disconnected.");
}
public void ExecuteQuery(string query)
{
Console.WriteLine($"Executing query: {query}");
}
}
// Logger 组件的接口和类
public interface ILogger
{
void Log(string message);
}
public class Logger : ILogger
{
public void Log(string message)
{
Console.WriteLine($"Logged: {message}");
}
}
// Security 组件的接口和类
public interface ISecurity
{
bool Authenticate(string username, string password);
}
public class Security : ISecurity
{
public bool Authenticate(string username, string password)
{
// 这里只是模拟认证过程
return username == "admin" && password == "password";
}
}
接下来,我们定义SystemFacade
类,它封装了上述组件的复杂性:
public class SystemFacade
{
private readonly IDatabase _database;
private readonly ILogger _logger;
private readonly ISecurity _security;
public SystemFacade(IDatabase database, ILogger logger, ISecurity security)
{
_database = database;
_logger = logger;
_security = security;
}
public void PerformSecureOperation(string username, string password, string query)
{
if (_security.Authenticate(username, password))
{
_logger.Log("User authenticated successfully.");
_database.Connect();
_database.ExecuteQuery(query);
_database.Disconnect();
_logger.Log("Operation completed successfully.");
}
else
{
_logger.Log("Authentication failed.");
}
}
}
最后,我们创建一个客户端来演示如何使用SystemFacade
:
class Program
{
static void Main(string[] args)
{
IDatabase database = new Database();
ILogger logger = new Logger();
ISecurity security = new Security();
SystemFacade facade = new SystemFacade(database, logger, security);
facade.PerformSecureOperation("admin", "password", "SELECT * FROM Users");
// 尝试使用错误的凭据
facade.PerformSecureOperation("user", "wrongpassword", "SELECT * FROM SensitiveData");
}
}
在这个例子中,SystemFacade
类封装了与Database
、Logger
和Security
组件的交互,提供了一个简单的PerformSecureOperation
方法给客户端使用。客户端不需要直接与这些组件交互,而是通过SystemFacade
来执行操作,从而简化了系统的使用和维护。
二、装饰器模式
装饰器模式(Decorator Pattern)允许你动态地为对象添加额外的功能。它通过创建一个装饰类来包裹原始类,从而在不改变原始类代码的情况下扩展其功能。这种类型的设计模式属于结构型模式。
主要组件
- 组件接口:定义一个接口以表示对象的基本功能。
- 具体组件:实现组件接口的具体类,定义基本行为。
- 装饰器基类:实现组件接口并持有一个组件对象的引用,以便在装饰的同时调用它的方法。
- 具体装饰器:继承装饰器基类,增加额外的行为
案例代码
namespace SweetTeaDotNet.Core.DesignPatterns.Structural;
// 1. 组件接口
public interface IComponent
{
string Operation();
}
// 2. 具体组件
public class ConcreteComponentA : IComponent
{
public string Operation()
{
return "ConcreteComponentA";
}
}
public class ConcreteComponentB : IComponent
{
public string Operation()
{
return "ConcreteComponentB";
}
}
// 3. 装饰器基类
public abstract class Decorator : IComponent
{
private readonly IComponent _component;
protected Decorator(IComponent component)
{
_component = component;
}
public virtual string Operation()
{
return _component.Operation();
}
}
// 4. 具体装饰器
public class ConcreteDecoratorA : Decorator
{
public ConcreteDecoratorA(IComponent component) : base(component)
{
}
public override string Operation()
{
return $"ConcreteDecoratorA({base.Operation()})";
}
}
public class ConcreteDecoratorB : Decorator
{
public ConcreteDecoratorB(IComponent component) : base(component)
{
}
public override string Operation()
{
return $"ConcreteDecoratorB({base.Operation()})";
}
}
// 测试类
public class DecoratorPatternTest
{
private readonly ITestOutputHelper _testOutputHelper;
public DecoratorPatternTest(ITestOutputHelper testOutputHelper)
{
_testOutputHelper = testOutputHelper;
}
[Fact]
public void RunTest()
{
IComponent componentA = new ConcreteComponentA();
IComponent componentB = new ConcreteComponentB();
_testOutputHelper.WriteLine("Client: I've got a simple component:");
_testOutputHelper.WriteLine(componentA.Operation());
_testOutputHelper.WriteLine(componentB.Operation());
// 装饰组件
componentA = new ConcreteDecoratorA(componentA);
_testOutputHelper.WriteLine("Client: Now I've got a decorated component:");
_testOutputHelper.WriteLine(componentA.Operation());
// 再次装饰
componentB = new ConcreteDecoratorB(componentB);
_testOutputHelper.WriteLine("Client: Now I've got a decorated component again:");
_testOutputHelper.WriteLine(componentB.Operation());
}
}
// 测试输出
/*
Client: I've got a simple component:
ConcreteComponentA
ConcreteComponentB
Client: Now I've got a decorated component:
ConcreteDecoratorA(ConcreteComponentA)
Client: Now I've got a decorated component again:
ConcreteDecoratorB(ConcreteComponentB)
*/
使用场景
动态添加功能:当你想在运行时为对象添加额外功能,而不影响其他对象的行为时,例如增加日志记录、缓存或权限检查。
避免类膨胀:当类的功能需要组合,但创建多种子类会导致类数量爆炸时,可以使用装饰器动态组合功能。
功能的可扩展性:允许通过组合不同的装饰器来扩展功能,增加灵活性。
三、适配器模式
适配器模式(Adapter Pattern)允许将一个接口转换为客户端所期望的另一个接口。适配器模式使得原本由于接口不兼容而无法一起工作的类可以协同工作。
主要组件
- 目标接口:客户端希望使用的接口。
- 具体目标:实现目标接口的类。
- 适配器:实现目标接口并包含一个对原始类的引用,负责将目标接口的方法调用转发到原始类。
案例代码
// 1. 目标接口
public interface ITarget
{
string GetRequest();
}
// 2. 具体目标
public class Adaptee
{
public string SpecificRequest()
{
return "Specific request from Adaptee.";
}
}
// 3. 适配器
public class Adapter : ITarget
{
private readonly Adaptee _adaptee;
public Adapter(Adaptee adaptee)
{
_adaptee = adaptee;
}
public string GetRequest()
{
// 调用Adaptee的方法并转换为Target的接口
return _adaptee.SpecificRequest();
}
}
// 测试类
public class AdapterPatternTest
{
private readonly ITestOutputHelper _testOutputHelper;
public AdapterPatternTest(ITestOutputHelper testOutputHelper)
{
_testOutputHelper = testOutputHelper;
}
[Fact]
public void RunTest()
{
_testOutputHelper.WriteLine("Run Test starting");
// 创建Adaptee实例
Adaptee adaptee = new Adaptee();
// 使用适配器将Adaptee转换为ITarget
ITarget target = new Adapter(adaptee);
// 调用ITarget的方法
_testOutputHelper.WriteLine("Client: " + target.GetRequest());
_testOutputHelper.WriteLine("Run Test started");
}
}
// 测试输出
/*
Run Test starting
Client: Specific request from Adaptee.
Run Test started
*/
使用场景
接口不兼容:当你有一个类(或类库)需要与另一个不兼容的类协作时,适配器可以桥接这两个接口。
复用现有代码:通过适配器模式,可以在不修改现有代码的情况下,使其适应新的接口。
整合第三方库:当引入第三方库时,如果其接口与项目中的现有接口不匹配,可以使用适配器使其兼容。
四、组合模式
组合模式(Composite Pattern),又叫部分整体模式,是用于把一组相似的对象当作一个单一的对象。组合模式依据树形结构来组合对象,用来表示部分以及整体层次。
主要组件
- 组件接口:定义叶子和组合对象的共同接口。
- 叶子:实现组件接口,表示树叶节点。
- 组合:实现组件接口,存储叶子和其他组合对象。
/ 1. 组件接口
public interface ICompositeComponent
{
void Operation();
}
// 2. 叶子类
public class Leaf : ICompositeComponent
{
private readonly string _name;
public Leaf(string name)
{
_name = name;
}
public void Operation()
{
Console.WriteLine($"Leaf: {_name}");
}
}
// 3. 组合类
public class Composite : ICompositeComponent
{
private readonly List<ICompositeComponent> _children = new List<ICompositeComponent>();
public void Add(ICompositeComponent component)
{
_children.Add(component);
}
public void Remove(ICompositeComponent component)
{
_children.Remove(component);
}
public void Operation()
{
Console.WriteLine("Composite Operation:");
foreach (var child in _children)
{
child.Operation();
}
}
}
//测试类
public class CompositePatternTest
{
private readonly ITestOutputHelper _testOutputHelper;
public CompositePatternTest(ITestOutputHelper testOutputHelper)
{
_testOutputHelper = testOutputHelper;
}
[Fact]
public void RunTest()
{
// 创建叶子
ICompositeComponent leaf1 = new Leaf("Leaf 1");
ICompositeComponent leaf2 = new Leaf("Leaf 2");
// 创建组合
Composite composite = new Composite();
composite.Add(leaf1);
composite.Add(leaf2);
// 创建另一个组合
Composite composite2 = new Composite();
composite2.Add(new Leaf("Leaf 3"));
composite2.Add(composite); // 添加之前的组合
// 执行操作
composite2.Operation();
}
}
//测试输出
/*
Composite Operation:
Leaf: Leaf 3
Composite Operation:
Leaf: Leaf 1
Leaf: Leaf 2
*/
五、享元模式
享元模式(Flyweight Pattern)是一种结构性设计模式,旨在通过共享对象来减少内存占用,提高性能。该模式特别适用于大量相似对象的场景,可以将共享的状态与独享的状态分离。
主要组件
- 享元接口:定义享元对象的接口。
- 具体享元:实现享元接口,包含共享的状态。
- 享元工厂:管理享元对象的创建和共享,确保对象的复用。
- 上下文:持有具体享元的独享状态。
/ 1. 享元接口
public interface IFlyweight
{
void Operation(string uniqueState);
}
// 2. 具体享元
public class ConcreteFlyweight : IFlyweight
{
private readonly string _sharedState;
public ConcreteFlyweight(string sharedState)
{
_sharedState = sharedState;
}
public void Operation(string uniqueState)
{
Console.WriteLine($"ConcreteFlyweight: Shared State = {_sharedState}, Unique State = {uniqueState}");
}
}
// 3. 享元工厂
public class FlyweightFactory
{
private readonly Dictionary<string, IFlyweight> _flyweights = new Dictionary<string, IFlyweight>();
public IFlyweight GetFlyweight(string sharedState)
{
if (!_flyweights.TryGetValue(sharedState, out var flyweight))
{
flyweight = new ConcreteFlyweight(sharedState);
_flyweights[sharedState] = flyweight;
Console.WriteLine("Creating new flyweight for state: " + sharedState);
}
return flyweight;
}
}
//测试类
[Fact]
public class FlyweightPatternTest
{
public void RunTest()
{
var factory = new FlyweightFactory();
// 创建和使用享元对象
var flyweight1 = factory.GetFlyweight("State A");
flyweight1.Operation("Unique 1");
var flyweight2 = factory.GetFlyweight("State B");
flyweight2.Operation("Unique 2");
// 重用已有享元对象
var flyweight3 = factory.GetFlyweight("State A");
flyweight3.Operation("Unique 3");
}
}
// 测试输出
/*
Creating new flyweight for state: State A
ConcreteFlyweight: Shared State = State A, Unique State = Unique 1
Creating new flyweight for state: State B
ConcreteFlyweight: Shared State = State B, Unique State = Unique 2
ConcreteFlyweight: Shared State = State A, Unique State = Unique 3
*/
六、桥接模式
桥接模式(Bridge Pattern)通过将抽象部分与实现部分分离,使它们可以独立变化。这种模式尤其适合需要在抽象和实现之间进行解耦的场景。
主要组件
- 抽象类(Abstraction):定义了高层抽象接口,它依赖于实现类的接口。
- 具体抽象类(Refined Abstraction):扩展了抽象类的功能,客户端通过它与实现类进行交互。
- 实现类接口(Implementor):定义了实现类的接口,抽象类依赖于此接口。
- 具体实现类(Concrete Implementor):实现了实现类接口中的方法。
// 1. 实现类接口
public interface IImplementation
{
string OperationImplementation();
}
// 2. 具体实现类A
public class ConcreteImplementationA : IImplementation
{
public string OperationImplementation()
{
return "ConcreteImplementationA: Here's the result on platform A.";
}
}
// 3. 具体实现类B
public class ConcreteImplementationB : IImplementation
{
public string OperationImplementation()
{
return "ConcreteImplementationB: Here's the result on platform B.";
}
}
// 4. 抽象类
public class Abstraction
{
protected IImplementation _implementation;
public Abstraction(IImplementation implementation)
{
_implementation = implementation;
}
public virtual string Operation()
{
return "Abstraction: Base operation with:\n" + _implementation.OperationImplementation();
}
}
// 5. 具体抽象类
public class ExtendedAbstraction : Abstraction
{
public ExtendedAbstraction(IImplementation implementation) : base(implementation) { }
public override string Operation()
{
return "ExtendedAbstraction: Extended operation with:\n" + base.Operation();
}
}
// 测试类
public class BridgePatternTest
{
private readonly ITestOutputHelper _testOutputHelper;
public BridgePatternTest(ITestOutputHelper testOutputHelper)
{
_testOutputHelper = testOutputHelper;
}
[Fact]
public void RunTest()
{
// 使用具体实现类A
IImplementation implementationA = new ConcreteImplementationA();
Abstraction abstractionA = new Abstraction(implementationA);
Console.WriteLine(abstractionA.Operation());
// 使用具体实现类B
IImplementation implementationB = new ConcreteImplementationB();
Abstraction abstractionB = new ExtendedAbstraction(implementationB);
Console.WriteLine(abstractionB.Operation());
}
}
// 测试输出
/*
Abstraction: Base operation with:
ConcreteImplementationA: Here's the result on platform A.
ExtendedAbstraction: Extended operation with:
Abstraction: Base operation with:
ConcreteImplementationB: Here's the result on platform B.
*/
使用场景
多个维度变化:当系统的抽象部分和实现部分都有多个变化维度时,桥接模式使它们可以独立扩展。
解耦抽象和实现:桥接模式可以让抽象与实现独立变化,避免直接继承带来的复杂性,尤其适合需要灵活组合的场景。
七、代理模式
代理模式(Proxy Pattern)它为其他对象提供一种代理,以控制对这个对象的访问。代理可以在不修改原始对象的情况下,添加额外的功能。
主要组件
- 接口或抽象类(Subject):定义了原始对象和代理对象的共同接口,客户端通过该接口与原始对象交互。
- 具体类(RealSubject):实现了接口的实际类,代表真实的对象,执行实际的操作。
- 代理类(Proxy):实现接口,包含对真实对象的引用,并控制对它的访问。
/ 1. 抽象接口
public interface ISubject
{
void Request();
}
// 2. 真实类
public class RealSubject : ISubject
{
public void Request()
{
Console.WriteLine("RealSubject: Handling Request.");
}
}
// 3. 代理类
public class Proxy : ISubject
{
private RealSubject _realSubject;
public void Request()
{
if (_realSubject == null)
{
_realSubject = new RealSubject();
}
Console.WriteLine("Proxy: Controlling access to RealSubject.");
_realSubject.Request();
}
}
// 测试类
public class ProxyPatternTest
{
[Fact]
public void RunTest()
{
// 客户端使用代理类来控制访问
ISubject proxy = new Proxy();
proxy.Request();
}
}
// 测试输出
/*
Proxy: Controlling access to RealSubject.
RealSubject: Handling Request.
*/
使用场景
-
控制对象访问:
通过代理对象可以控制对目标对象的访问权限。例如,在某些应用中,可能需要对某些敏感资源或操作进行访问控制,这时可以使用代理模式来检查客户端是否具有适当的访问权限。
-
延迟实例化:
代理模式可以实现延迟加载(Lazy Loading),即在真正需要对象时才创建对象。这对于处理复杂或资源密集型的对象特别有用,可以优化资源使用。例如,在加载大图或处理大量数据时,可以使用虚拟代理来延迟对象的实例化。
-
权限控制:
代理模式可以用于增强安全性,通过代理对象来加入客户端的授权和验证(authorization & authentication),从而保护敏感资源。例如,在数据库访问控制中,可以使用代理模式来检查用户是否具有执行特定SQL查询的权限。
-
远程访问:
在分布式系统中,对象可能位于不同的地址空间。使用远程代理可以让客户端以访问本地资源的方式访问远程对象,从而简化客户端与远程对象之间的交互。远程代理负责管理连接技术细节,如建立网络连接、序列化和反序列化等。
-
增强功能:
代理模式可以在不改变目标对象的前提下,通过代理类来增强目标对象的功能。例如,可以在代理中记录每次操作的日志、统计性能数据、缓存查询结果等。这些额外的功能对于调试、监控和优化系统非常有帮助。
-
智能引用:
代理模式还可以用于释放不再使用的重型对象。代理跟踪活跃用户并周期检查,如果没有客户端使用对象,则释放对象和系统资源。这有助于减少内存占用和提高系统性能。
Proxy: Controlling access to RealSubject.
RealSubject: Handling Request.
*/
### 使用场景
1. **控制对象访问**:
通过代理对象可以控制对目标对象的访问权限。例如,在某些应用中,可能需要对某些敏感资源或操作进行访问控制,这时可以使用代理模式来检查客户端是否具有适当的访问权限。
2. **延迟实例化**:
代理模式可以实现延迟加载(Lazy Loading),即在真正需要对象时才创建对象。这对于处理复杂或资源密集型的对象特别有用,可以优化资源使用。例如,在加载大图或处理大量数据时,可以使用虚拟代理来延迟对象的实例化。
3. **权限控制**:
代理模式可以用于增强安全性,通过代理对象来加入客户端的授权和验证(authorization & authentication),从而保护敏感资源。例如,在数据库访问控制中,可以使用代理模式来检查用户是否具有执行特定SQL查询的权限。
4. **远程访问**:
在分布式系统中,对象可能位于不同的地址空间。使用远程代理可以让客户端以访问本地资源的方式访问远程对象,从而简化客户端与远程对象之间的交互。远程代理负责管理连接技术细节,如建立网络连接、序列化和反序列化等。
5. **增强功能**:
代理模式可以在不改变目标对象的前提下,通过代理类来增强目标对象的功能。例如,可以在代理中记录每次操作的日志、统计性能数据、缓存查询结果等。这些额外的功能对于调试、监控和优化系统非常有帮助。
6. **智能引用**:
代理模式还可以用于释放不再使用的重型对象。代理跟踪活跃用户并周期检查,如果没有客户端使用对象,则释放对象和系统资源。这有助于减少内存占用和提高系统性能。