装饰器设计模式的“如何”和“在哪里”






4.88/5 (25投票s)
装饰器设计模式
引言
装饰器设计模式是 GOF 提出的行为模式之一。装饰器设计模式用于开发过程中,为现有类型提供额外功能。因此,装饰器设计模式允许开发人员实现 SOLID 原则。
- 单一职责原则 – 类/函数应该只做一件事,或者类/函数应该只有一个修改的原因。
- 开闭原则 – 类/函数对扩展开放,对修改关闭。
- 里氏替换原则 – 如果类型 S 是从类型 T 派生的,那么类型 T 的对象可以被类型 S 的对象替换。
装饰器意味着用装饰基础元素来实现额外功能。下图是装饰的一种展示。
|
|
基础礼物
|
礼品包装
|
图片展示了带有基础盒子的礼物,另一张图片是礼物上的精美包装装饰。这里实现的额外功能是,通过包装装饰,让基础礼物看起来更漂亮。
设计模式的简单示例
下图展示了基础装饰器设计模式的类图。
IBasicService
- 需要由派生类型实现的基接口。BasicServiceImplementation
– 它是派生自接口的基础/具体实现,作为基础提供基本功能。Decorator1OnBasic
&Decorator2OnBasic
– 它是派生自接口的装饰实现。它实际上在基本实现之上提供了额外功能。Client
– 使用具体实现,它创建Decorate
的实例并使用其功能。
下面的代码是上面讨论的装饰器设计模式和类图的实现。
namespace BasicDecoratorPattern
{
public interface IBaseService
{
void Print();
}
public class BasicServiceImplementaion : IBaseService
{
public void Print()
{
Console.WriteLine("Basic Item");
}
}
public class Decorator1OnBasic : IBaseService
{
private readonly IBaseService BasicRealTimeService;
public Decorator1OnBasic(IBaseService service)
{
BasicRealTimeService = service;
}
public void Print()
{
BasicRealTimeService.Print();
Console.WriteLine("Extra functionality from Decorator ONE");
}
}
public class Decorator2OnBasic : IBaseService
{
private readonly IBaseService BasicRealTimeService;
public Decorator2OnBasic(IBaseService service)
{
BasicRealTimeService = service;
}
public void Print()
{
BasicRealTimeService.Print();
Console.WriteLine("Extra functionality from Decorator SECOND");
}
}
public class Client
{
public Client()
{
IBaseService realTimeService = new BasicServiceImplementaion();
IBaseService basicRealTimeServiceDecorator1 = new Decorator1OnBasic(realTimeService);
IBaseService basicRealTimeServiceDecorator2 = new Decorator2OnBasic(realTimeService);
basicRealTimeServiceDecorator1.Print();
basicRealTimeServiceDecorator2.Print();
}
}
}
在上面的代码实现中需要注意的点如下所列:
DecoratorOnBasic
接收IBasicService
实例作为输入,以创建decorator
类的实例。- 客户端首先创建
BasicServiceImplementation
的实例,然后将该实例传递给decorator
。 Decorator
利用传递给它的基本实现实例来实现基本功能,并通过装饰它来实现额外功能。
输出
输出显示装饰器在基本功能之上添加了额外功能。为了进一步理解,下面是装饰器模式在现实世界中的另一个例子。
设计模式的现实世界示例
下图是现实世界设计模式的类图。下面的类图代表了不同种类的 Milkshake
(Mango
& Chocolate
),它们都基于基础的 Milkshake
。
与基础实现的映射
IMilkShake
等同于IBasicService
MilkShake
等同于BasicImplementation
MangoMilkshake
&ChoclateMilkShake
等同于Decorator1OnBasic
&Decorator2OnBasic
在此实现中请注意,MilkshakeDecorator
是一个 abstract
类,它派生自 IMilkShake
,而 MilkShake
的 Decorator
派生自这个 decorator
类。有一些公共功能,因此创建了这个类,但它不影响装饰器模式的实际实现。
namespace RealWorldDecoratorPattern
{
public interface IMilkShake
{
string Serve();
int Price();
}
public class MilkShake : IMilkShake
{
public string Serve()
{
return "MilkShake";
}
public int Price()
{
return 30;
}
}
public abstract class MilkshakeDecorator : IMilkShake
{
public readonly IMilkShake Milkshake;
public MilkshakeDecorator(IMilkShake milkShake)
{
Milkshake = milkShake;
}
public string Flavour { get; set; }
public int FlavourPrice { get; set; }
public abstract string Serve();
public abstract int Price();
}
public class MangoMilkShake : MilkshakeDecorator
{
public MangoMilkShake(IMilkShake milkShake)
: base(milkShake)
{
this.Flavour = "Mango";
this.FlavourPrice = 10;
}
public override string Serve()
{
return "Serving " + this.Flavour + " " + Milkshake.Serve();
}
public override int Price()
{
return this.FlavourPrice + Milkshake.Price();
}
}
public class ChoclateMilkShake : MilkshakeDecorator
{
public ChoclateMilkShake(IMilkShake milkShake)
: base(milkShake)
{
this.Flavour = "Choclate";
this.FlavourPrice = 20;
}
public override string Serve()
{
return "Serving " + this.Flavour + " " + Milkshake.Serve();
}
public override int Price()
{
return this.FlavourPrice + Milkshake.Price();
}
}
public class Client
{
public Client()
{
IMilkShake milkShake = new MilkShake();
IMilkShake mangoMilkshake = new MangoMilkShake(milkShake);
IMilkShake choclateMilkshake = new ChoclateMilkShake(milkShake);
Console.WriteLine(mangoMilkshake.Serve());
Console.WriteLine(mangoMilkshake.Price());
Console.WriteLine();
Console.WriteLine(choclateMilkshake.Serve());
Console.WriteLine(choclateMilkshake.Price());
}
}
}
输出
在上面的代码中,MilkShake Decorator
类(Mango
和 Chocolate
)使用了基础的 Mikshake
类。Decorator
类对基础 Milkshake
类进行装饰,并通过使用基本实现和额外功能来提供输出。
在应用程序中使用设计模式
以上两个示例有助于理解装饰器设计模式的 Basic
和 RealWorld
问题。但本节旨在帮助您了解如何在应用程序中使用设计模式,即开发人员可能在应用程序中使用它的地方。
装饰器设计模式对于实现横切关注点/面向切面编程概念非常有用,例如:
- 身份验证
- Authorization
- 日志记录
- 缓存
- 验证
- 异常管理
除了前面解释的横切关注点之外,它还可以用于用额外的功能装饰类,也就是说,不总是只能使用装饰器模式来实现横切关注点。
下面是通过 CachingDecorator
实现缓存横切关注点的类图。
IProvides
等同于IBasicService
– 在这个例子中,它是具有GetProviderList
的契约。- Provider 等同于
BasicServiceImplementation
– 在这个例子中,它是具体实现,用于获取提供商列表。 CacheProvider
等同于DecoratorOnBasic
– 在这个例子中,它是进行缓存获取的提供商的任务的装饰器,当请求时,它提供缓存数据,或者如果缓存数据不可用,它会从 Provider(基础)实现请求新数据。
namespace CacheDecoratorPattern
{
public interface IProviders
{
NameValueCollection GetProviderList();
}
public class Providers : IProviders
{
public NameValueCollection GetProviderList()
{
NameValueCollection providerList = new NameValueCollection();
providerList.Add("SQL", "SQLProvider");
providerList.Add("Oracle", "OracleProvider");
providerList.Add("MySQL", "MyProvider");
return providerList;
}
}
public class CacheProvider : IProviders
{
private readonly IProviders provider;
private NameValueCollection CachedProviderList;
public CacheProvider(IProviders provider)
{
this.provider = provider;
}
public NameValueCollection GetProviderList()
{
if(CachedProviderList == null)
CachedProviderList = provider.GetProviderList();
return CachedProviderList;
}
}
public class Client
{
public Client()
{
IProviders provider = new Providers();
CacheProvider cacheProvider = new CacheProvider(provider);
var providerlist = cacheProvider.GetProviderList();
}
}
}
在代码中,CacheProvider
是一个装饰 Provider
类的类,并将 IProvider
作为输入。由于这只是一个示例,目前缓存值存储在 CacheProvider
的 private
变量中,但在实际应用中,这可以被真实的缓存替换,也就是说,它可以是 Web 应用程序缓存类或 Enterprise Application Library 缓存块。
与依赖注入容器结合的装饰器
下面是如何将装饰器实例注册到 Microsoft Unity 容器的示例代码。
注册装饰器
var container = new UnityContainer();
container.RegisterType(
typeof( IProvider ),
typeof( Provider ),
"BasicProvider"
);
contract.RegisterType(
typeof( IProvider ),
typeof( CacheProvider ),
new InjectionConstructor(
new ResolvedParameter(
typeof( IProvider ),
"BasicProvider"
)
)
);
因此,一旦它与容器的 Resolve
方法一起注册,就可以轻松获取 Decorator
的实例。
var contract = container.Resolve<IProvider>();
最终实现了 SOLID 原则
- 单一职责原则 – 在示例中,基础实现 Provider 执行其负责的任务,例如:在最后一个示例中获取数据。而装饰器负责执行额外功能,例如
CacheProvider
负责缓存数据,而不是获取数据。 - 开闭原则 - 正如这里的规则所述,基础实现 Provider 对修改关闭,对扩展开放,这通过
CacheProvider
实现,它扩展了基础实现的功能。 - 里氏替换原则 – 正如这里的规则所述,基础实现 Provider 对象在
Decorator
构造函数中被父类型IProvider
接口替换,其中Provider
对象由客户端类注入。
注意
这是我对该模式的看法。请提供您的反馈,如果您发现此帖有任何错误,也请提供反馈。