Сначала предыстория. Всё дело идёт в вебе, но это не столь важно.
Допустим, есть класс Dialog — окошко. У нас есть 3 типа отображения (не просто стилизация CSS, а даже разная структура HTML — это действительно требуется).
Сейчас код разных методов (buildDialogHtml(), getCssClass() и т. д.) представляют собой ветвистые if'ы:
protected buildDialogHtml() {
if (ui == 0)
this.buildDialogHtmlType0();
else if (ui == 1)
this.buildDialogHtmlType1();
else
this.buildDialogHtmlType2();
Парадигма ООП говорит, что такой код можно и нужно разделить на иерархию классов:
Как видите, получается, мы не можем вклиниться нашим ООП в такую структуру. Т. е. такое ветвление классов подходит только когда тип UI является гарантированно последним звеном в иерархии.
Как реализовать такую штуку, чтобы было красиво и расширяемо?
Может есть какой-то паттерн? Пока на ум приходит только паттерн делегата: создание какого-то объекта UIType внутри Dialog, которому и делегируются данные методы. Но это плодит кучу кода, навигация по которому так себе, и кажется, что уж лучше if'ы. Кроме того ведь пользователь нашей библиотеки может случайно переопределить метод, который вызывает делегата, и не будет понимать, почему всё сломалось (или сломалось только на конкретном типе UI).
Здравствуйте, sharez, Вы писали:
S>Как реализовать такую штуку, чтобы было красиво и расширяемо?
Используйте фабрику и заставляёте её создавать вам инстансы диалогов.
При добавлении новых диалогов просто регистрируйте их в фабрике.
factory.register("DialogType0",()=> new DialogType0() );
...
factory.register("DialogTypeN",()=> new DialogTypeN() );
...
protected buildDialogHtml() {
return factory.create(features).buildDialogHtml();
}
Re: (ищу паттерн) Подмешивание фичи в дерево классов
Здравствуйте, sharez, Вы писали:
S>Всем привет!
S>Сначала предыстория. Всё дело идёт в вебе, но это не столь важно.
Вмесито наследования сделать агрегацию. Паттерны композит, декоратор, и наверное приспособленец — взгляды на эту идею с разных сторон.
Re[2]: (ищу паттерн) Подмешивание фичи в дерево классов
Здравствуйте, kov_serg, Вы писали:
_>Здравствуйте, sharez, Вы писали:
S>>Как реализовать такую штуку, чтобы было красиво и расширяемо? _>Используйте фабрику и заставляёте её создавать вам инстансы диалогов. _>При добавлении новых диалогов просто регистрируйте их в фабрике.
_>
_>factory.register("DialogType0",()=> new DialogType0() );
_>...
_>factory.register("DialogTypeN",()=> new DialogTypeN() );
_>...
_>protected buildDialogHtml() {
_> return factory.create(features).buildDialogHtml();
_>}
_>
Взять нужный инстанс будет не проблема. Но наследование классов после этого будет несколько сомнительным...
Re[2]: (ищу паттерн) Подмешивание фичи в дерево классов
Здравствуйте, itslave, Вы писали:
I>Здравствуйте, sharez, Вы писали:
S>>Всем привет!
S>>Сначала предыстория. Всё дело идёт в вебе, но это не столь важно. I>Вмесито наследования сделать агрегацию. Паттерны композит, декоратор, и наверное приспособленец — взгляды на эту идею с разных сторон.
Спасибо, посмотрю ещё раз на них. Но сколько не смотрел — выходит слишком сложно. Наследование самое то, но накладывает ограничения.
Re[3]: (ищу паттерн) Подмешивание фичи в дерево классов
Здравствуйте, sharez, Вы писали:
S>Спасибо, посмотрю ещё раз на них. Но сколько не смотрел — выходит слишком сложно. Наследование самое то, но накладывает ограничения.
Да чего там сложного то?
А серьезней, наследование накладывает слишком много ограничений и привносит очень сильную связность в код, что чаще плохо чем хорошо
Re[4]: (ищу паттерн) Подмешивание фичи в дерево классов
Здравствуйте, itslave, Вы писали:
I>Здравствуйте, sharez, Вы писали:
S>>Спасибо, посмотрю ещё раз на них. Но сколько не смотрел — выходит слишком сложно. Наследование самое то, но накладывает ограничения. I>Да чего там сложного то?
Сложно потом всё это читать. Особенно через год после написания.
I>А серьезней, наследование накладывает слишком много ограничений и привносит очень сильную связность в код, что чаще плохо чем хорошо
Об чем и речь
Re[5]: (ищу паттерн) Подмешивание фичи в дерево классов
Здравствуйте, sharez, Вы писали:
S>Здравствуйте, itslave, Вы писали:
I>>Здравствуйте, sharez, Вы писали:
S>>>Спасибо, посмотрю ещё раз на них. Но сколько не смотрел — выходит слишком сложно. Наследование самое то, но накладывает ограничения. I>>Да чего там сложного то?
S>Сложно потом всё это читать. Особенно через год после написания.
Дело привычки. Помнится, на одном проекте у нас при виде STL Заказчик вздыхал "читать тяжело". Ничего, затем обвыкся. Сам я тоже при виде LINQ сперва впадал в состояние "ШТА?". Ничего, тоже обвыкся.
Re: (ищу паттерн) Подмешивание фичи в дерево классов
Здравствуйте, sharez, Вы писали:
S>Всем привет!
S>Сначала предыстория. Всё дело идёт в вебе, но это не столь важно. S>Допустим, есть класс Dialog — окошко. У нас есть 3 типа отображения (не просто стилизация CSS, а даже разная структура HTML — это действительно требуется).
S>Как видите, получается, мы не можем вклиниться нашим ООП в такую структуру. Т. е. такое ветвление классов подходит только когда тип UI является гарантированно последним звеном в иерархии.
Мне кажется, у вас проблема в том, что вы пытаетесь запихнуть в единую иерархию два совершенно разных аспекта: способ отображения и состав диалога.
S>Как реализовать такую штуку, чтобы было красиво и расширяемо?
Стандартным решением будет вынести "способ отображения" в отдельную иерархию классов, например DialogRenderer. Вся информация о том, как генерируется диалог, будет сосредоточена в каждом из потомков. S>Может есть какой-то паттерн? Пока на ум приходит только паттерн делегата: создание какого-то объекта UIType внутри Dialog, которому и делегируются данные методы. Но это плодит кучу кода, навигация по которому так себе, и кажется, что уж лучше if'ы. Кроме того ведь пользователь нашей библиотеки может случайно переопределить метод, который вызывает делегата, и не будет понимать, почему всё сломалось (или сломалось только на конкретном типе UI).
Это скорее не делегат, а стратегия. Впрочем, возможны варианты:
1. Вся связь диалога и отображения запихана в один виртуальный метод Dialog.Render(DialogRenderer renderer)
2. Рендерер скармливается диалогу при конструировании, и вызывается по мере необходимости dialog = new VeryCustomDialog(renderer)
Уйдемте отсюда, Румата! У вас слишком богатые погреба.