У меня есть ситуация, когда у меня есть много классов моделей (~ 1000), которые реализуют любое количество 5 интерфейсов. Поэтому у меня есть классы, которые реализуют один и другие, которые реализуют четыре или пять.
Это означает, что я могу иметь любую перестановку этих пяти интерфейсов. В классической модели мне пришлось бы реализовать 32-5 = 27 "meta interfaces", которые "соединяются" с интерфейсами в комплекте. Часто это не проблема, потому что IB
обычно расширяет IA
и т.д., Но в моем случае пять интерфейсов ортогональны/независимы.
В моем коде кода у меня есть методы, которым нужны экземпляры, в которых реализовано любое количество этих интерфейсов. Поэтому давайте предположим, что у нас есть класс X
и интерфейсы IA
, IB
, IC
, ID
и IE
. X
реализует IA
, ID
и IE
.
Ситуация ухудшается, потому что некоторые из этих интерфейсов имеют формальные параметры типа.
Теперь у меня есть два варианта:
-
Я мог бы определить интерфейс
IADE
(или, скорее,IPersistable_MasterSlaveCapable_XmlIdentifierProvider
; подчеркивает только ваше удовольствие от чтения) -
Я мог бы определить общий тип как
<T extends IPersistable & IMasterSlaveCapable & IXmlIdentifierProvider>
, который дал бы мне удобный способ смешивания и сопоставления интерфейсов по мере необходимости. -
Я мог бы использовать такой код:
IA a = ...; ID d = (ID)a; IE e = (IE)e
, а затем использовать локальную переменную с правильным типом для вызова методов, даже если все три работают в одном экземпляре. Или используйте бросок в каждом вызове второго метода.
Первое решение означает, что я получаю много пустых интерфейсов с очень нечитаемыми именами.
Второй использует своего рода "ad-hoc" типизацию. И Oracle javac
иногда натыкается на них, а Eclipse получает это право.
Последнее решение использует броски. Сказал Нафф.
Вопросы:
-
Есть ли лучшее решение для смешивания любого количества интерфейсов?
-
Есть ли причины, чтобы избежать временных типов, которые предлагает решение №2 (за исключением недостатков в Oracle
javac
)?
Примечание. Я знаю, что писать код, который не компилируется с Oracle javac
, является риском. Мы знаем, что мы можем справиться с этим риском.
[Редактировать] Кажется, что я пытаюсь попытаться здесь смутить. Мои экземпляры модели могут иметь одну из следующих черт:
- Они могут быть "мастер-ведомыми" (думаю, клонирование)
- Они могут иметь XML-идентификатор
- Они могут поддерживать операции дерева (parent/child)
- Они могут поддерживать версии
- и т.д.. (да, модель еще сложнее)
Теперь у меня есть код поддержки, который работает на деревьях. Расширения деревьев - деревья с ревизиями. Но у меня также есть ревизии без деревьев.
Когда я вхожу в код для добавления дочернего элемента в менеджер дерева ревизий, я знаю, что каждый экземпляр должен реализовывать ITtree
и IRevisionable
, но нет единого интерфейса для обоих, поскольку они полностью независимы.
Но в реализации мне нужно вызвать методы на узлах дерева:
public void addChild( T parent, T child ) {
T newRev = parent.createNewRevision();
newRev.addChild( foo );
... possibly more method calls to other interfaces ...
}
Если createNewRevision
находится в интерфейсе IRevisionable
и addChild
находится в интерфейсе ITree
, каковы мои параметры для определения T
?
Примечание. Предположим, что у меня есть несколько других интерфейсов, которые работают аналогичным образом: существует много мест, где они независимы, но некоторый код должен видеть их сочетание. IRevisionableTree
- не решение, а другая проблема.
Я мог бы использовать тип для каждого вызова, но это кажется неуклюжим. Создание всех перестановок интерфейсов было бы скучным, и, похоже, нет разумного шаблона для сжатия огромных имен интерфейсов. Дженерики предлагают отличный выход:
public
<T extends IRevisionable & ITree>
void addChild( T parent, T child ) { ... }
Это не всегда работает с Oracle javac
, но кажется компактным и полезным. Любые другие варианты/комментарии?