Понимание шаблона проектирования декоратора в С#
Я только начал изучать 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 );