Страницы

Поиск по вопросам

пятница, 13 декабря 2019 г.

Какие реализации паттерна проектирования “Декоратор” вы знаете?

#c_sharp #ооп #шаблоны_проектирования


Хотелось бы узнать о том, как данный паттерн может быть реализован, но не по "стандартной"
схеме, предложенной GOF.
Привожу пример "стандартной" реализации.

class Component
{ Component() { ... } }
class ConcreteComponent : Component
{ ConcreteComponent() { ... } }
class Decorator : Component
{ 
    Component c; 
    int newState; 
    public void SetComponent(Component component) { ... }
    Decorator() { ... } 
}


Можно ли декорировать объекты как-то по другому, не совмещая агрегацию и наследование,
но чтобы при этом сохранялись все преимущества паттерна? Желательно на C#.
    


Ответы

Ответ 1



Я обычно использую декораторы вместе с интерфейсом и композицией. Например, есть некоторый источник данных о погоде: public interface IWeatherProvider { public WeatherInfo GetWeather(string city); } Есть реализация по умолчанию. Например, она ходит по http к провайдеру данных: public class DefaultWeatherProvider : IWeatherProvider { public WeatherInfo GetWeather(string city) { // обращение по HTTP } } В некоторых сценариях нам необязательно запрашивать погоду каждый раз у провайдера, а достаточно кэшировать ее: public class CachingWeatherProvider : IWeatherProvider { private readonly Dictionary _cache; private readonly IWeatherProvider _provider; public CachingWeatherProvider(IWeatherProvider provider) { _provider = provider; _cache = new Dictionary(); } public WeatherInfo GetWeather(string city) { WeatherInfo weather; if (!_cache.TryGetValue(city, out weather)) { weather = _provider.GetWeather(city); _cache[city] = weather; } return weather; } } Используем: var provider = new CachingWeatherProvider(new DefaultWeatherProvider()); Или вдруг источник ненадежный, часто лежит, и вы захотели добавить ретрай: public class RetryingWeatherProvider : IWeatherProvider { private readonly IWeatherProvider _provider; private int _maxRetries; public RetryingWeatherProvider(IWeatherProvider provider, int maxRetries) { _provider = provider; _maxRetries = maxRetries; } public WeatherInfo GetWeather(string city) { int retry = 0; do { try { return _provider.GetWeather(city); } catch (WebException) { if (retry == _maxRetries) { throw; } else { // подождем немного } } } while (++retry < _maxRetries) } } И тогда можно наворотить провайдер с кэшем и ретраем: var provider = new CachingWeatherProvider( new RetryingWeatherProvider(new DefaultWeatherProvider(), 3)); Как именно реализуется декоратор в принципе неважно, отличия имеют право на жизнь. Важно лишь помнить про open-closed принцип: декоратор очень полезен, когда вам нужно расширить функциональность кода, не трогая при этом основной код. У вас есть провайдер данных. Чтобы добавить функциональность ретрая, вы не лезете в основной провайдер, а создаете декоратор. Чтобы добавить кэширование, вы опять же не лезете в провайдер, а создаете еще один декоратор и т.д. P.S. Весь приведенный код не production ready, а только иллюстрация.

Комментариев нет:

Отправить комментарий