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

Lij*_*raj 21 c# design-patterns

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

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();
    }
 }
Run Code Online (Sandbox Code Playgroud)

但是下面的代码也可以是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();
    }
}
Run Code Online (Sandbox Code Playgroud)

我的理解

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

tym*_*167 41

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

例如,你有一个对象和接口,比如Repository : IRepository.

public interface IRepository
{
    void SaveStuff();
}

public class Repository : IRepository
{
    public void SaveStuff()
    {
        // save stuff   
    }
}
Run Code Online (Sandbox Code Playgroud)

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

class RepoClient
{
    public void DoSomething(IRepository repo)
    {
        //...
        repo.SaveStuff();
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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
    }
}
Run Code Online (Sandbox Code Playgroud)

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

var client = new RepoClient();
client.DoSomething(new Repository());
Run Code Online (Sandbox Code Playgroud)

但现在你可以使用

var client = new RepoClient();
client.DoSomething(new RepositoryLogDecorator(new Repository()));
Run Code Online (Sandbox Code Playgroud)

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

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

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

public class MyAwesomeRepository : IRepository
{
    public void SaveStuff()
    {
        // save stuff, but AWESOME!
    }
}
Run Code Online (Sandbox Code Playgroud)

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

var client = new RepoClient();
client.DoSomethig(new RepositoryLogDecorator(new MyAwesomeRepository()));
Run Code Online (Sandbox Code Playgroud)

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

凉!爱它!:d

  • 那么它只是一个包装类? (2认同)
  • @ilans 它非常取决于用例和解决方法。例如,如果您希望有一种简单的方法来记录任何方法的任何类,而无需自己实现接口,那么您最终可以使用 AOP ( [one](https://docs.microsoft.com/en-us/archive/ msdn-magazine/2014/february/aspect-driven-programming-aspect-orient-programming-with-the-realproxy-class), [二](https://doc.postsharp.net/add-logging#s3) )这都是关于在编译/运行时生成装饰器的。但是,如果您想在单个小方法中引入一次日志记录,则没有必要创建装饰器,只需记录您需要的内容。 (2认同)

Bru*_*oLM 10

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

在上面的视频中,作者给出了一个带有Marine和的例子WeaponUpgrade.

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

marine = new WeaponUpgrade(marine);
Run Code Online (Sandbox Code Playgroud)

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

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; }
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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


bra*_*roo 5

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

动态地为对象附加额外的职责。装饰器提供了一种灵活的替代子类来扩展功能。

在您的第二个示例中,您使用继承来扩展类的行为,我相信这在技术上不是装饰器设计模式。