Pull to refresh
8
0.6
Андрей Бычко @courage_andrey

инженер-программист

Send message
Согласен, я тоже на 100% уверен, что кто-то ещё уже делал так (или почти так). Если не секрет, какое решение использовали для локализации у себя Вы? Код можно глянуть?
Объявление раба двигателем — историческая шутка, отсылающая к латинскому выражению «говорящее орудие», употреблённое в отношении рабов философом Варроном в его трудах, посвящённых сельскому хозяйству. Особенности использования интерфейсов вместо абстрактных классов и наоборот — это тема, которой хватит на самостоятельную статью, пару холиваров и три программистские пьянки.
Допереписал. Ещё раз спасибо, что потратили своё терпение, чтобы донести до меня эту идею.
Понимаете, даже первый пример не является стратегией.… Поэтому получается, что статья о том, как из стратегии сделать не-стратегию.
Согласен на 100%. Постараюсь сегодня после работы дописать недостающее, чтобы сложить эту мозаику целиком, без «ну вы поняли, что я подразумевал».
Насчёт сделать generic-ом тип клиента надо подумать. Я попробую поиграться с этим use case в отвязке от транспорта, потому что с ним постановка выглядит действительно дико.
Сделать всё на основе прямых вызовов, когда проект не монолитный и допускает расширения, всё равно не получится.

WinForms-компоненты от DevExpress так работали лет 10 назад, если мне память не изменяет.
Исходя из логики, которая руководила действиями, — нет. Только выглядит похоже.
Цитирую свой ответ на один из предыдущих комментариев:
Дошло, наконец, откуда непонимание. То, что получается ближе к финалу, уже не напоминает стратегию. Это мне тоже очевидно. Стоит ли сменить название статьи на «Простой способ сократить код после применения паттерна «стратегия» с помощью использования generic-классов»? И, возможно, вставить в самое начало пример того, как отрабатывает сам паттерн? Потому что комментарии однозначно на это намекают.
От требований зависит, кто будет вызывать, деталей реализации и контекста вызова. Из нормальных примеров, что я встречал, вызывающий код перед использованием двигателей сам говорит, какие их типы он хочет использовать. Из кривых способов я видел заполнение этого списка статическим конструктором типа двигателя, который искал своих неабстрактных потомков в загруженных сборках, и статическими конструкторами дочерних типов, которые регистрировали себя в классе-родителе. Но эти два способа — откровенный выстрел себе в ногу, они работали без проблем только благодаря упоротости и перфекционизму человека, занимавшегося сопровождением кода.
Дошло, наконец, откуда непонимание. То, что получается ближе к финалу, уже не напоминает стратегию. Это мне тоже очевидно. Стоит ли сменить название статьи на «Простой способ сократить код после применения паттерна «стратегия» с помощью использования generic-классов»? И, возможно, вставить в самое начало пример того, как отрабатывает сам паттерн? Потому что комментарии однозначно на это намекают.
Всё, о чём Вы говорите — это (дополнительные) требования. Естественно, как только требований становится больше, решение начинает их учитывать и становится сложнее. Пример в статье простой, как грабли, и там ничего не сказано ни про количество двигателей, ни про количество владельцев, ни про потребляемые ими ресурсы. Когда понадобится усложнение, я думаю, каждый догадается, что и к чему надо прикрутить.
Согласен, привязывать базовый класс к потомкам — не лучшая идея. Пример, как можно заметить, сильно упрощён (и судя по комментариям, я зря опустил ряд шагов, посчитав их слишком очевидными). Я бы решал задачу заполнения словаря warehouse не с помощью рефлексии, а предоставив классам, имеющим на то причины, возможность вызвать примерно такой метод:
abstract class Engine
{
	...

	private static readonly IDictionary<Type, Engine> warehouse = new Dictionary<Type, Engine>();

	public /*internal, protected - выбрать по необходимости*/ static void RegisterEngine<EngineT>(EngineT engine)
		where EngineT : Engine
	{
		warehouse[typeof(EngineT)] = engine;
	}
}
Цитирую Ваше сообщение:
Паттерн Стратегия не определяет, каким образом контекст получает экземпляр стратегии. Контекст может получать ее в аргументах конструктора, через метод или свойство или получать ее у третьей стороны.
Ничто не мешает сделать двигатель параметром конструктора.
И в третий раз повторяю: можно «не прибивать». И подменять во время исполнения никто не мешает. Цитирую статью по ссылке:
Обратите внимание, что классический паттерн Стратегии весьма абстрактен.

Паттерн Стратегия не определяет, как стратегия получит данные, необходимые для выполнения своей работы. Они могут передаваться в аргументах метода стратегии, или стратегия может получать ссылку на сам контекст и получать требуемые данные самостоятельно.
Паттерн Стратегия не определяет, каким образом контекст получает экземпляр стратегии. Контекст может получать ее в аргументах конструктора, через метод или свойство или получать ее у третьей стороны.

Если есть требование менять мотор прямо в полёте — не проблема, просто код станет чуточку длиннее.
Стратегию характеризует возможность замены этого поведения во время работы. Т.е. надо либо просунуть стратегию (двигатель) через конструктор конкретного клиента (машины, самолета), либо запросить его из клиента без указания конкретного типа стратегии.
По-моему, больше в сути стратегии всё-таки инкапсуляции выносимого поведения, чем в возможности горячей замены. Но одно другому не мешает. Говоря в терминах статьи, рикша вполне может пересесть на велосипед.
var car = new Car();
car.SetEngine(engine);

car.RemoveEngine();

Абстрагируйтесь, пожалуйста, от автомобилей. Если говорить про код, а не про автомобили, то никто и ничто в этом мире не мешает нам использовать один и тот же экземпляр дважды.
IБуксир буксир1 = new Пароход();
баржаСЗерном.ПрицепитьК(буксир1);
баржаСУглем.ПрицепитьК(буксир1);
паромСТуристами.ПрицепитьК(буксир1);

Пул — это здорово, это дальнейшее развитие идеи статьи с целью увеличить число поддерживаемых use case-ов. Можно, конечно, и так.
Стратегия в данном примере есть вынесение двигателя из транспортного средства (под катом), а дальше начинаются выкрутасы с generic-ами в попытках сократить и упростить код.
Заметьте, что в данном примере появилось ограничение new() на типе двигателя. Сделано это исключительно для краткости. ...
Да, действительно,
клиенту можно подсунуть любую стратегию
, на что и указывает данный комментарий.
Здесь должна быть… гарантия, что всегда существует не более одного инстанса любого типа, использующего данный двигатель.
Вы говорите верно с точки зрения логики реального мира, в котором один двигатель действительно нельзя засунуть в две кредитопомойки.
Видимо, топливо из общего глобального бензобака, и крутим глобальные колеса, которые прилеплены ко всем автомобилям мира?
Да, всё понято абсолютно верно. Другими словами: я родил не слишком удачный пример. Чтобы звучало чуть более логично, я попробую переформулировать задачу следующим образом: «Предположим, на складе есть один рабочий двигатель, который подходит к нескольким транспортным средствам. Таких в наличии имеется три: трактор для вспахивания земли, Волга председателя для выезда в райцентр и (внезапно) дизель-генератор для освещения сельского клуба. Трактор работает только днём по будням, председатель катается днём в выходные, а танцы в клубе происходят каждую ночь.» В таком случае метод GetFromWarehouse заменяется слесарем Петровичем в промасленной робе, который перекидывает движок из одного места в другое, а синхронизация потоков (случаи выездов в поле и в райцентр во время танцев) не нужна в связи с поставленной задачей. Но если председателю припрёт скататься в райцентр посреди уборочной (описанная Вами ситуация), то да, всё будет плохо и Петровичу придётся уходить в запой.
В чем проблема создавать двигатель заново для каждого конкретного автомобиля?
Это может быть накладно по ресурсам. Такой «двигатель» может быть не просто Console.WriteLine(«Hello, World»), а тяжеловесным ресурсом ОС, работающим через COM и маршалинг. Абстрагируйтесь от машин, они здесь мешают.
С фабричным методом я не согласен, потому что что ни Vehicle не служит для порождения Engine, ни наоборот. Про прототип — я бы сказал, не совсем 100%. Если честно, то судя по комментариям (весьма справедливым, надо признать), название статьи нужно сменить на что-то вроде «Я прицепил к стратегии generic, и смотрите сколько ещё паттернов повылазило.»
А в вашем примере Engine.GetFromWarehouse() это service locator почти в чистом виде.
Да, согласен. Я бы сказал, что последний абзац описывает процесс перехода от одного к другому.
Лучше уж
Вопросы «лучше бы ...» всегда попахивают началом священных войн.Я не люблю спорить на тему того, какой из двух подходов лучше, потому что in real life приходится, как правило, использовать оба. Всё зависит от конкретной ситуации: требования, среда выполнения, legacy, перспективы развития, командный пинок сверху и т.д.

Information

Rating
1,958-th
Location
Warszawa, Warszawa, Польша
Registered
Activity