了解C#中的装饰器设计模式

我刚开始学习装饰设计模式,不幸的是我不得不通过各种参考来更好地理解装饰器模式,这让我非常困惑。 所以,就我的理解而言,我相信这是一个装饰模式

interface IComponent { void Operation(); } class Component : IComponent { public void Operation() { Console.WriteLine("I am walking "); } } class DecoratorA : IComponent { IComponent component; public DecoratorA(IComponent c) { component = c; } public void Operation() { component.Operation(); Console.WriteLine("in the rain"); } } class DecoratorB : IComponent { IComponent component; public DecoratorB(IComponent c) { component = c; } public void Operation() { component.Operation(); Console.WriteLine("with an umbrella"); } } class Client { static void Main() { IComponent component = new Component(); component.Operation(); DecoratorA decoratorA = new DecoratorA(new Component()); component.Operation(); DecoratorB decoratorB = new DecoratorB(new Component()); component.Operation(); Console.Read(); } } 

但是下面的代码也可以是Decorator Pattern吗?

 class Photo { public void Draw() { Console.WriteLine("draw a photo"); } } class BorderedPhoto : Photo { public void drawBorder() { Console.WriteLine("draw a border photo"); } } class FramePhoto : BorderedPhoto { public void frame() { Console.WriteLine("frame the photo"); } } class Client { static void Main() { Photo p = new Photo(); p.Draw(); BorderedPhoto b = new BorderedPhoto(); b.Draw(); b.drawBorder(); FramePhoto f = new FramePhoto(); f.Draw(); f.drawBorder(); f.frame(); } } 

我的理解

从我给出的第二个例子中,我们可以调用所有这三个方法,但从第一个例子开始,我无法通过创建单个对象来访问所有这三个方法。

它应该是一个评论,但我有太多的话。

例如,您有一个对象和接口,如Repository : IRepository

 public interface IRepository { void SaveStuff(); } public class Repository : IRepository { public void SaveStuff() { // save stuff } } 

和客户,这可能不是你写的

 class RepoClient { public void DoSomethig(IRepository repo) { //... repo.SaveStuff(); } } 

一旦你决定,就应该记录对存储库的所有调用。 但是你有一个问题 – Repository – 来自外部库的类,你不想改变那些代码。 因此,您需要扩展您使用的Repository的行为。 你编写RepositoryLogDecorator : IRepository ,并在每个方法里面做日志记录,比如

 public class RepositoryLogDecorator : IRepository { public IRepository _inner; public RepositoryLogDecorator(IRepository inner) { _inner = inner; } public void SaveStuff() { // log enter to method try { _inner.SaveStuff(); } catch(Exception ex) { // log exception } // log exit to method } } 

所以,在你可以使用客户端之前

 var client = new RepoClient(); client.DoSomethig(new Repository()); 

但现在你可以使用

 var client = new RepoClient(); client.DoSomethig(new RepositoryLogDecorator(new Repository())); 

注意,这是一个非常简单的例子。 在实际项目中,对象使用DI容器创建主要对象,您可以通过更改某些配置来使用装饰器。

那么,使用什么装饰器:在不改变对象或客户端的情况下扩展对象的function。

装饰器的另一个好处是:装饰器不依赖于Repository实现。 仅取决于接口IRepository 。 为什么这是优势? 如果你决定写自己的实现

 public class MyAwesomeRepository : IRepository { public void SaveStuff() { // save stuff, but AWESOME! } } 

你可以自动使用已经存在的装饰器来装饰它

 var client = new RepoClient(); client.DoSomethig(new RepositoryLogDecorator(new MyAwesomeRepository())); 

想从真实软件中看到例子吗? (就像样本,代码很难看,我知道)=> 去这里

凉! 爱它! :d

Youtube上的这个PatternCraft系列解释了星际争霸的设计模式,你应该在这里查看关于装饰器的video 。

在上面的video中,作者给出了一个MarineWeaponUpgrade

在游戏中你将有一个Marine ,然后你可以升级其武器:

 marine = new WeaponUpgrade(marine); 

请注意,你仍然有一个海洋,它不是一个新的单位,它是与改变其属性的东西相同的单位。

 public class MarineWeaponUpgrade : IMarine { private IMarine marine; public MarineWeaponUpgrade(IMarine marine) { this.marine = marine; } public int Damage { get { return this.marine.Damage + 1; } // here set { this.marine.Damage = value; } } } 

您可以通过创建一个实现与您的单元相同的接口的类来访问您的单元属性来修改值。

CodeWars上有一个Kata,要求你为海军陆战队完成武器和装甲装饰。

装饰器模式允许您将特定行为添加到给定类型单个对象,而不会影响该相同类型的其他实例。

在您的第二个示例(正常inheritance)中,该类的所有实例都inheritance了已修改的行为。

每个GOF页面装饰器设计模式:

动态地将附加职责附加到对象。 装饰器为子类化提供了灵活的替代扩展function。

在你的第二个例子中,你使用inheritance来扩展类的行为,我相信这在技术上不是一个Decorator设计模式。

第二个例子不是装饰模式,因为装饰器模式的基本要素是对象接受其中一种并且可能增强它。

第一个例子中的实例是

public DecoratorA(IComponent c) { component = c; }

此外,装饰器模式的目标是创建“一个”对象,然后通过将其传递通过不同的filter或装饰器来装饰它。 因此这条线

DecoratorA decoratorA = new DecoratorA(new Component());

应该

DecoratorA decoratorA = new DecoratorA(component );