Страницы

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

пятница, 19 октября 2018 г.

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

Хотелось бы узнать о том, как данный паттерн может быть реализован, но не по "стандартной" схеме, предложенной GOF. Привожу пример "стандартной" реализации.
class Component { Component() { ... } } class ConcreteComponent : Component { ConcreteComponent() { ... } } class Decorator : Component { Component c; int newState; public void SetComponent(Component component) { ... } Decorator() { ... } }
Можно ли декорировать объекты как-то по другому, не совмещая агрегацию и наследование, но чтобы при этом сохранялись все преимущества паттерна? Желательно на C#.


Ответ

Я обычно использую декораторы вместе с интерфейсом и композицией.
Например, есть некоторый источник данных о погоде:
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, а только иллюстрация.

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

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