Начинать
Для начала сделаем такое отступление, хочу начать цикл статей о шаблонах проектирования. Я думаю, что все хотят, чтобы их код был легко масштабируемым, верно. Все паттерны разделены на категории, и перед их рассмотрением лучше начать изучение с самих паттернов, иначе вы не поймете, почему Декоратор — составной паттерн, а Фабрика — генеративный паттерн. После изучения уже будет интуитивно понятно, почему тот или иной узор относится к той или иной категории.
Для изучения самих паттернов я постараюсь не углубляться в определения, а показать примеры, поставить задачу и решить задачу с помощью паттерна. Мы возьмем компанию «Сесла Моторс» и решим поставленные перед ней задачи. Не позволяйте имени заставить вас чувствовать, что вы где-то о нем слышали. Это не правильно. Ведь компания занимается только производством электромобилей. И такое редко встретишь в реальном мире. Это должно быть интересно. Пойдем!.
Пятно
Наша компания предлагает различные модели автомобилей. Основными из них являются «Модель А» и «Модель Б». Сами автомобили находятся в разных ценовых сегментах. Но есть опции, которые можно добавить в конфигурацию обеих машин. А компания «Цесла Моторс» хочет без труда получить окончательную цену и информацию о собранной комплектации электромобиля. Как они могут это сделать?
попробуем решить
Начнем с того, что паттерн декоратор на самом деле является удобной «оберткой» для основного класса, каждая обертка придает основному классу новые свойства. Чтобы иметь возможность «обернуть» основной класс, у вас должен быть базовый класс как для оболочки, так и для основного класса. Является ли это абстрактным классом Машина.
public abstract class Car
{
private string Description;
public Car()
{
Description = "Unknown car";
}
public Car(string description)
{
Description = description;
}
public virtual string GetDescription()
{
return Description;
}
public abstract decimal Price();
}
Обратите внимание, что свойства Цена И Описание иметь разные учетные данные для доступа. Этим я хотел показать гибкость этой модели, поэтому не имеет значения, используем ли мы абстрактный Или виртуальный. Главное уметь заменить имущество.
От Машина классы будут наследоваться Модель А И МодельB. Напишем их реализацию. Пока все хорошо и тривиально
public class ModelA : Car
{
public ModelA() : base("Default ModelA") { }
public ModelA(string description) : base(description) { }
public override decimal Price()
{
return 40_000.034m;
}
}
Вы можете сами написать реализацию ModelB.
По моему я про упаковку другое сказал. Хм… Тут мне кажется запустится основная логика декоратора. Давайте подумаем, как обернуть экземпляры класса Модель А Или МодельB новая возможность. Я бы написал дополнительный абстрактный класс, который содержал бы логику оболочки
public abstract class CarPartsDecorator : Car
{
public Car _car;
public CarPartsDecorator(Car car)
{
_car = car;
}
}
В будущем в этом классе можно будет написать абстрактный метод для реализации в подклассах. То есть добавить дополнительные свойства для опций автомобиля.
Теперь реализуем один из вариантов Автоматическая система парковки (детский класс АвтомобильЧастиДекоратор)
public class AutomaticParkingSystem : CarPartsDecorator
{
private readonly decimal _ownPrice;
private readonly string _description;
public AutomaticParkingSystem(Car car,
string description, decimal ownPrice = 1_500m) : base(car)
{
_ownPrice = ownPrice;
_description = description;
}
public override decimal Price()
{
return _car.Price() + _ownPrice;
}
public override string GetDescription()
{
return _car.GetDescription() + _description;
}
}
Вы также можете сами написать другие классы опционов.
Любой класс! Теперь давайте посмотрим, как выглядит наш сервис в итоге.
Car car = new ModelA();
car = new WheelDisk(car, ownPrice: 2_300.4m);
car = new AutomaticParkingSystem(car, description: ", new automatic parking system");
Console.WriteLine($"ModelA price: {car.Price()}");
Console.WriteLine($"ModelB price: {car.GetDescription()}");
На выходе получилось так:
ModelA price: 43800,434
ModelB price: Default ModelA, simple wheel disk, new automatic parking system