Предположим, у меня есть интерфейс, который поддерживает несколько потенциальных операций:
interface Frobnicator {
int doFoo(double v);
int doBar();
}
Теперь некоторые экземпляры будут поддерживать только одну или другую из этих операций. Они могут поддерживать оба. Код клиента не обязательно будет знать, пока он на самом деле не получит его из соответствующей фабрики, путем внедрения зависимостей или откуда он получает экземпляры.
Я вижу несколько способов справиться с этим. Один из способов, который, по-видимому, является основной тактикой, принятой в Java API, состоит в том, чтобы просто иметь интерфейс, как показано выше, и использовать неподдерживаемые методы, чтобы поднять UnsupportedOperationException
. Недостатком этого является, однако, отсутствие быстрой сбои - клиентский код не может определить, будет ли doFoo
работать, пока не попытается вызвать doFoo
.
Это может быть дополнено методами supportsFoo()
и supportsBar()
, определенными для возврата true, если соответствующий метод do
работает.
Другая стратегия заключается в разделении методов doFoo
и doBar
на методы FooFrobnicator
и BarFrobnicator
соответственно. Эти методы затем вернут null
, если операция не поддерживается. Чтобы клиентский код не выполнял instanceof
проверки, я определяю Frobnicator
интерфейс следующим образом:
interface Frobnicator {
/* Get a foo frobnicator, returning null if not possible */
FooFrobnicator getFooFrobnicator();
/* Get a bar frobnicator, returning null if not possible */
BarFrobnicator getBarFrobnicator();
}
interface FooFrobnicator {
int doFoo(double v);
}
interface BarFrobnicator {
int doBar();
}
В качестве альтернативы FooFrobnicator
и BarFrobnicator
могут расширяться Frobnicator
, а методы get*
могут быть переименованы в as*
.
Одной из проблем с этим является именование: Frobnicator
на самом деле не является фробникатором, это способ получения фробникаторов (если я не использую as*
именование). Это также становится немного громоздким. Именование может быть еще более сложным, так как Frobnicator
будет получено из FrobnicatorEngine
службы.
Кто-нибудь имеет представление о хорошем, желательно хорошо принятом решении этой проблемы? Есть ли подходящий шаблон дизайна? В этом случае посетитель не подходит, так как клиентскому коду необходим интерфейс определенного типа (и желательно, чтобы он работал быстро, если он не может его получить), а не диспетчеризация того, какой объект он получил. Независимо от того, поддерживаются ли разные функции, это может зависеть от разных факторов - реализация Frobnicator
, конфигурация времени выполнения этой реализации (например, она поддерживает doFoo
, только если присутствует какая-то системная служба для включения Foo
) и т. д.
Обновление: Конфигурация во время выполнения - это еще один обезьяна в этом деле. Возможно избежать переноса типов FooFrobnicator и BarFrobnicator, чтобы избежать этой проблемы, особенно если я использую Guice-modules-as-configuration, но это усложняет другие окружающие интерфейсы (такие как фабрика / конструктор, производящий Frobnicators). на первом месте). По сути, реализация фабрики, производящей фробникаторы, настраивается во время выполнения (либо через свойства, либо через модуль Guice), и я хочу, чтобы пользователю было довольно легко сказать «подключите этого провайдера фробникаторов к этому клиенту». , Я признаю, что это проблема с потенциальными внутренними проблемами проектирования, и что я, возможно, также обдумываю некоторые из проблем обобщения, но я собираюсь для некоторой комбинации наименьшего уродства и наименьшего удивления.