Есть вопрос теоретического плана. Это касается методологий ООП и, в частности, средств, предоставляемых Java 1.5.
Как человек, занимающийся разработкой уже лет 12, и "влившийся" в ООП с самого начала, у меня сложилось свое представление о некоторых принципах грамотной разработки программных архитектур (не без влияния гуру типа Booch-Rumbaugh-Jacobson, GoF, и т.д.)
Насколько я понимаю, полиморфизм и наследование представляют базу ООП.
Возникшие уже позже технологии построения абстракций более высокого уровня,
а именно параметризованные классы (шаблоны, generics) являются усложнением
изначальной методологии и должны использоваться аккуратно, поскольку вполне
способны "загромоздить" архитектуру и в какой-то момент стать ощутимым препятствием к
развитию модели.
К сожалению, мне не всегда удается своими доводами убедить некоторых разработчиков.
Простой пример. Нужно реализовать базовую логику поддержки объектом набора состояний и реакций на их смену.
То есть имеем связку: State-StateFul-StateChangeEvent-StateChangeListener. И, конечно, большое количество наследников данных сущностей.
Какими доводами мне следует убеждать человека, что злоупотребление Generics в данном случае
действительно усложняет архитектуру системы... человек реализует это хозяйство примерно так:
interface Stateful<S, T> {
T getState();
void addStateChangeListener(StateChangeListener<? extends S, T> _listener);
void removeStateChangeListener(StateChangeListener<? extends S, T> _listener);
}
interface StateChangeListener<S, T>
extends EventListener {
void stateChange(StateChangeEvent<S, T> _event);
}
class StateChangeEvent<S, T>
extends EventObject {
public T getOldState();
public T getNewState();
public S getSource();
}
В итоге, на конечных уровнях иерархий классов мы видим нагромождение generics...
На мой взгляд, представленная логика должна быть реализована без использования generics вообще.
Возможные явные преобразования типов в конкретных подклассах (без которых можно даже и обойтись) не являются проблемой, которую надо обязательно решать вводя generics...
Или все-таки я не прав?
Свою точку зрения я основываю еще и на коде J2SE/J2EE, где таких конструкция я никогда не встречал.
МОжет ли кто-нибудь дать ссылки на методологию применения шаблонов/generics?
(Возможно, вопрос стоит перебросить в раздел "Архитектура...")
Здравствуйте, Cybernelly, Вы писали:
C>...
C>Насколько я понимаю, полиморфизм и наследование представляют базу ООП. C>Возникшие уже позже технологии построения абстракций более высокого уровня, C>а именно параметризованные классы (шаблоны, generics) являются усложнением C>изначальной методологии и должны использоваться аккуратно, поскольку вполне C>способны "загромоздить" архитектуру и в какой-то момент стать ощутимым препятствием к C>развитию модели.
Не согласен. Шаблоны — одно из проявлений полиморфизма, а именно static polymorphism
C>К сожалению, мне не всегда удается своими доводами убедить некоторых разработчиков. C>Простой пример. Нужно реализовать базовую логику поддержки объектом набора состояний и реакций на их смену. C>То есть имеем связку: State-StateFul-StateChangeEvent-StateChangeListener. И, конечно, большое количество наследников данных сущностей.
C>Какими доводами мне следует убеждать человека, что злоупотребление Generics в данном случае C>действительно усложняет архитектуру системы... человек реализует это хозяйство примерно так:
C>...
C>В итоге, на конечных уровнях иерархий классов мы видим нагромождение generics... C>На мой взгляд, представленная логика должна быть реализована без использования generics вообще. C>Возможные явные преобразования типов в конкретных подклассах (без которых можно даже и обойтись) не являются проблемой, которую надо обязательно решать вводя generics...
Все зависит от задачи. Если хочется применять представленные обертки на широком фронте работ, то почему бы не использовать типизацию? Если для реализации логики действительно нужны какие-то характерные особенности классов (т.е. если вместо типов-параметров S и T использовать Object, то потом придется даункастить к нужному типу), то решение оправдано.
C>Или все-таки я не прав? C>Свою точку зрения я основываю еще и на коде J2SE/J2EE, где таких конструкция я никогда не встречал. C>МОжет ли кто-нибудь дать ссылки на методологию применения шаблонов/generics? C>(Возможно, вопрос стоит перебросить в раздел "Архитектура...")
j2se — первое, что приходит в голову — коллекции, thread local. j2ee — думаю, что в новых технологиях будут использоваться дженерики (там, где это уместно). Вообще, имхо дженерики не панацея и не модная фишка, которую надо использовать где только можно. Это просто одна из возможностей, предоставляемых языком, и использовать ее надо только тогда, когда она реально удобна и полезна. Это то же самое, что прочитать GoF и начать тыркать шаблоны где надо и где не надо.
Рекомендую перечитать GoF, в частности вспомнить, что в списке механизмов повторного использования кода они приводят не только наследование и композицию, но также и делегирование, наследование и параметризованные типы (generics).
Тот же Hibernate пока не параметризован, но разве это о чем-то говорит? Java SE 5 относительно молодая.
C>Насколько я понимаю, полиморфизм и наследование представляют базу ООП.
Извините, что отвечаю не на весь пост, но именно эта фраза возбудила желание.
Базу ООП, имхо, составляет инкапсуляция, которую вы почему-то вообще выкинули из традиционного списка из трех пунктов. Однако благодаря инкапсуляции — суть связыванию данных и кода их обрабавающего — раскрывается прелесть ООП — возможность уменьшить количество элементов, с которыми приходится иметь дело во время разработки, значительно увеличивая производительность программистов.
DZ>Не согласен. Шаблоны — одно из проявлений полиморфизма, а именно static polymorphism
Я не спорю. Я просто говорю, что это более "продвинутое" средство, чем обычный полиморфизм.
И как в любом деле, усложнение должно быть оправдано. То есть должны быть веские причины, чтобы
использовать это средство.
DZ>Все зависит от задачи. Если хочется применять представленные обертки на широком фронте работ, то почему бы не использовать типизацию? Если для реализации логики действительно нужны какие-то характерные особенности классов (т.е. если вместо типов-параметров S и T использовать Object, то потом придется даункастить к нужному типу), то решение оправдано.
В том-то и дело, что сущности используемые в качестве State — перечисления...
Аргументация человека: "применение Generics дает мне возможность удобно использовать контекстные средства редактора Intellij Idea"
DZ>j2se — первое, что приходит в голову — коллекции, thread local. j2ee — думаю, что в новых технологиях будут использоваться дженерики (там, где это уместно). Вообще, имхо дженерики не панацея и не модная фишка, которую надо использовать где только можно. Это просто одна из возможностей, предоставляемых языком, и использовать ее надо только тогда, когда она реально удобна и полезна. Это то же самое, что прочитать GoF и начать тыркать шаблоны где надо и где не надо.
Да, я знаю, где в J2SE используются generics. Они используются в совершенно оправданных местах. Их назначение — очевидно.
А здесь мы получаем конструкции типа:
public abstract class AbstractSession<R extends Request>
implements Session, RequestSource<R>, Stateful<AbstractSession, AbstractSession.State>
Здравствуйте, dolor, Вы писали:
C>>Насколько я понимаю, полиморфизм и наследование представляют базу ООП.
D>Извините, что отвечаю не на весь пост, но именно эта фраза возбудила желание. D>Базу ООП, имхо, составляет инкапсуляция, которую вы почему-то вообще выкинули из традиционного списка из трех пунктов. Однако благодаря инкапсуляции — суть связыванию данных и кода их обрабавающего — раскрывается прелесть ООП — возможность уменьшить количество элементов, с которыми приходится иметь дело во время разработки, значительно увеличивая производительность программистов.
Я намеренно не указал инкапсуляцию, ибо она в рассматриваемом вопросе не особо играет роль.
И все думал, укажет ли мне кто-нибудь на отсутствие третьего кита или нет?
Как я понимаю, Generics в Java — просто инструмент для устранения большинства бессмысленных cast-ов. Ничего нового в плане обобщённого программирования они в Java-у не принесли.
vsb>Как я понимаю, Generics в Java — просто инструмент для устранения большинства бессмысленных cast-ов. Ничего нового в плане обобщённого программирования они в Java-у не принесли.
Согласен. Вопрос только в том, насколько в каждом конкретном случае убирание cast-ов оправдывает бесконтрольную параметризацию классов (сразу по нескольким аспектам поведения)... На определенном этапе это приводит к серьезным трудностям при построении подклассов.
R>Рекомендую перечитать GoF, в частности вспомнить, что в списке механизмов повторного использования кода они приводят не только наследование и композицию, но также и делегирование, наследование и параметризованные типы (generics).
Я не противник generics. Я противник их неоправданного использования.
Просто хочу для себя уяснить критерии, когда это использование оправданное, а когда нет.
Помнится в литературе часто приводятся примеры, когда нужно применять наследование,
а когда использовать делегирование. Никто при этом не утверждает, что одно лучше второго.
Здравствуйте, Cybernelly, Вы писали:
C>То есть имеем связку: State-StateFul-StateChangeEvent-StateChangeListener. И, конечно, большое количество наследников данных сущностей.
А для чего большое количество наследников?
C>Какими доводами мне следует убеждать человека, что злоупотребление Generics в данном случае C>действительно усложняет архитектуру системы... человек реализует это хозяйство примерно так:
Genercis в общем случае вообще никак не влияют на архитектуру. Они всего лишь Compile Time constraints. Способный как улучшать так и ухудшать читаемость кода.
Без пример того как это планируется использовать, не очень ясно надо оно здесь или нет.
C>В итоге, на конечных уровнях иерархий классов мы видим нагромождение generics... C>На мой взгляд, представленная логика должна быть реализована без использования generics вообще.
Generics не влияют на логику. Заисключением редких случаев.
C>Возможные явные преобразования типов в конкретных подклассах (без которых можно даже и обойтись) не являются проблемой, которую надо обязательно решать вводя generics...
Если можно обойтись без кастов, даункастов и генериков, то — да, стоит обойтись без обоих.
C>Или все-таки я не прав?
До конца не ясно.
Здравствуйте, Cybernelly, Вы писали:
C>Есть вопрос теоретического плана. Это касается методологий ООП и, в частности, средств, предоставляемых Java 1.5.
C>Как человек, занимающийся разработкой уже лет 12, и "влившийся" в ООП с самого начала, у меня сложилось свое представление о некоторых принципах грамотной разработки программных архитектур (не без влияния гуру типа Booch-Rumbaugh-Jacobson, GoF, и т.д.)
C>Насколько я понимаю, полиморфизм и наследование представляют базу ООП.
Сейчас прибегут товарищи из declarative, и популярно объяснят, что ООП — это всего лишь инкаспуляция состояния, не более того. Объекты с состоянием — единственный необходимый атрибут языка, чтобы он был ОО. Все остальное — рюшечки, которые необязательны, хотя и прсутствуют почти во всех языках, поддерживающих ООП.
Потом они обязательно скажут, что полиморфизм и наследование встречаются не только ООП, приведя в качестве примера, разумеется, Haskell.
Здравствуйте, Cybernelly, Вы писали:
R>>Рекомендую перечитать GoF, в частности вспомнить, что в списке механизмов повторного использования кода они приводят не только наследование и композицию, но также и делегирование, наследование и параметризованные типы (generics).
Там все это есть.
C>>На определенном этапе это приводит к серьезным трудностям при построении подклассов. B>Можно пример трудности?
Для меня речь пока идет о "затуманенности" архитектуры, сложности ее понимания.
Я пока стараюсь делать только review... Код пишет тот, кто и писал его изначально.
(Хотя даже он уже говорит о "кривизне").
Лично меня очень напрягают конструкции типа
public abstract class AbstractSession<R extends Request>
implements Session<AbstractSession, AbstractSession.State>, RequestSource<R> {
public abstract class AbstractClientSession<
P extends AbstractClientSessionProcessor<? extends AbstractClientSession, R>,
R extends Request>
extends AbstractSession<R>
когда есть стойкое чувство, что все это порождается исключительно применением generics на
уровне базовых (и достаточно тривиальных) классов и интерфейсов,
в которых можно было бы сделать только с помощью обычного наследования/полиморфизма.
Здравствуйте, Cybernelly, Вы писали:
vsb>>Как я понимаю, Generics в Java — просто инструмент для устранения большинства бессмысленных cast-ов. Ничего нового в плане обобщённого программирования они в Java-у не принесли. C>Согласен. Вопрос только в том, насколько в каждом конкретном случае убирание cast-ов оправдывает бесконтрольную параметризацию классов (сразу по нескольким аспектам поведения)... На определенном этапе это приводит к серьезным трудностям при построении подклассов.
Каким именно, можно примеры?
Мне, лично, единственное что не нравится — отсутствие typedef'ов.
C>>Согласен. Вопрос только в том, насколько в каждом конкретном случае убирание cast-ов оправдывает бесконтрольную параметризацию классов (сразу по нескольким аспектам поведения)... На определенном этапе это приводит к серьезным трудностям при построении подклассов. C>Каким именно, можно примеры?
C>>То есть имеем связку: State-StateFul-StateChangeEvent-StateChangeListener. И, конечно, большое количество наследников данных сущностей. B>А для чего большое количество наследников?
Потому что State в каждом случае — разный. State вообще не образует иерархии.
Поэтому в каждом конкретном случае имеем наследование с конкретизацией generics.
(Это я описываю то, что мне НЕ нравится).
B>Без пример того как это планируется использовать, не очень ясно надо оно здесь или нет.
State — перечисление. Все остальная логика — традиционная. Смотрим на state — порождаем event, и т.д.
Здравствуйте, Cybernelly, Вы писали:
C>когда есть стойкое чувство, что все это порождается исключительно применением generics на C>уровне базовых (и достаточно тривиальных) классов и интерфейсов, C>в которых можно было бы сделать только с помощью обычного наследования/полиморфизма.
Я не вижу в этом примере чего-то особенно плохого. Он просто заранее явно ставит constraint'ы на условия объектов. Если это все сделать без generic'ов — констрейнты останутся, но будут уже неявными.
Если код сложно читается — стоит подумать как его порефакторить. Но выбрасывать лишнюю безопасность из-за того, что она указывает на проблемы в дизайне? — Этого не надо делать.
C>Довело меня до написания в форум появление C>
Здравствуйте, Cybernelly, Вы писали:
C>Для меня речь пока идет о "затуманенности" архитектуры, сложности ее понимания.
Повторюсь, арихитеутра здесь не при чем. Просто код сложен для понимания, особено без практики работы с Generics
C>Лично меня очень напрягают конструкции типа
Если без нее можно обойтись, то стоит конечно же упростить.
C>когда есть стойкое чувство, что все это порождается исключительно применением generics на C>уровне базовых (и достаточно тривиальных) классов и интерфейсов, C>в которых можно было бы сделать только с помощью обычного наследования/полиморфизма.
Чувства чувствами, а аргументы нужны получше.
C>