Понимание шаблона проектирования декоратора в С#

Я только начал изучать Designator Design Pattern, к сожалению, мне пришлось пройти через различные рефренсы, чтобы лучше понять шаблон Decorator, который привел меня в большое замешательство. поэтому, насколько я понимаю, это проблема, я считаю, что это образец декоратора.

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();
        }
    }

но может ли нижеследующий код также быть шаблоном декоратора?

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();
    }
}

Мое понимание

из второго приведенного мной примера, мы можем вызвать все три метода, но из первого примера я не смогу получить доступ ко всем трем методам, создав один объект.

Ответ 1

Это должен быть комментарий, но у меня слишком много слов.

Например, у вас есть объект и интерфейс, например Repository: IRepository.

public interface IRepository
{
    void SaveStuff();
}

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

и клиент, который, вероятно, был написан кем-то еще

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

И как только вы решили, что ВСЕ обращения к хранилищу должны быть зарегистрированы. Но у вас есть проблема: класс Repository из внешней библиотеки, и вы не хотите изменять этот код. Поэтому вам необходимо расширить поведение Repository которое вы используете. Вы пишете RepositoryLogDecorator: IRepository, и внутри каждого метода 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.DoSomething(new Repository());

но теперь вы можете использовать

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

Обратите внимание, что это очень простой пример. В реальных проектах, где объект создан первично с DI-контейнером, вы сможете использовать декоратор, изменив некоторые настройки.

Таким образом, декоратор используется для расширения функциональности объекта без изменения объекта или клиента.

Еще одно преимущество декоратора: ваш декоратор не зависит от реализации Repository. Только зависит от интерфейса IRepository. Почему это преимущество? Если вы решили написать собственную реализацию IRepository

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

Вы сможете автоматически украсить это с помощью декоратора, который уже существует

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

Хотите увидеть пример из реального программного обеспечения? (как пример, код уродлив, я знаю) => иди сюда

Ответ 2

На Youtube есть серия PatternCraft, в которой рассказывается о разработке шаблонов с помощью Starcraft. Видео о декораторах можно посмотреть здесь.

В видео выше автор приводит пример с Marine и WeaponUpgrade.

В игре у вас будет 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 есть Ката, которая заставляет вас завершить оформление оружия и доспехов для морского пехотинца.

Ответ 3

Per Страница GOF Рисунок декоратора:

Прилагайте дополнительные обязанности к объекту динамически. Декораторы обеспечивают гибкую альтернативу подклассу для расширения функциональности.

В вашем втором примере вы используете наследование для расширения поведения класса, я считаю, что это технически не шаблон дизайна Decorator.

Ответ 4

Шаблон декоратора позволяет добавить конкретное поведение к отдельному объекту данного типа, не затрагивая другие экземпляры того же типа.

В вашем втором примере, который является обычным наследованием, все экземпляры класса наследуют измененное поведение.

Ответ 5

Второй пример - это не шаблон украшения, так как существенным элементом декоратора является тот факт, что объект принимает один из его видов и, возможно, улучшает его.

В этом примере экземплярами этого примера являются

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

Кроме того, цель шаблона декоратора - создать "один" объект, а затем украсить его, передав его через различные фильтры или декораторы. Следовательно, прямая

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

Должно быть

DecoratorA decoratorA = new DecoratorA(component );