Как реализовать только часть интерфейса

14

При разработке в ООП интерфейс / контракт иногда дается библиотекой, которую вы не можете изменить. Давайте назовем этот интерфейс J.

Теперь у вас есть объект класса A, который потребляет объекты, которые реализуют этот интерфейс. Внутри Необходима только небольшая часть определений интерфейса. Некоторые из классов объектов созданы мной во время проекта (давайте назовем один из них типом D), поэтому в реализации всего внутри интерфейса J. есть издержки.

Я хочу реализовать подмножество функциональных возможностей в интерфейсе J, но мои решения пока не удовлетворяют меня:

  • реализация каждого аспекта J с последующим выбрасыванием «notImplementedExceptions» дезинформирует пользователя моих объектов: казалось бы, мои объекты типа D соответствуют интерфейсу J, но они - и другие потребители моих объектов (которые принимают объекты, реализующие интерфейс) J) не может полагаться на целостность моих объектов.
  • Реализация нового определенного интерфейса запрещает мне использовать объекты, которые реализуют только интерфейс J, хотя интерфейс J полностью совместим с моим собственным интерфейсом.
  • Разрешение моим пользовательским объектам реализовать интерфейс J создаст значительные накладные расходы, потому что им не нужны все эти функции.

Когда я смог изменить интерфейс J, я бы создал «суперинтерфейс» K, который имел бы это подмножество функциональности интерфейса J, и заставил бы интерфейс J наследовать от интерфейса K. Но я не могу изменить интерфейс J.

Что такое объектно-ориентированное решение этой проблемы? Лучшее решение все еще реализует "просто" интерфейс J? Или существуют ООП-способы «суперкласса» интерфейса без его изменения?

vstrien
источник
1
Посмотрите, как классы * Adapter в Swing делают это.

Ответы:

9

если вы не контролируете интерфейс J, вы застряли.

для ясности, вы можете реализовать свой собственный интерфейс subJ и интерфейс J, и использовать subJ в своем собственном коде, чтобы прояснить, что дополнительные методы в интерфейсе J не требуются, но я не думаю, что это действительно приносит вам большую пользу

если возможно, полностью реализовать весь интерфейс J

если возможно, свяжитесь с владельцем интерфейса J и попросите его / ее изменить его, чтобы он лучше соответствовал вашим целям.

Стивен А. Лоу
источник
2
В качестве компромисса: Вы можете попросить владельца интерфейса J наследовать ваш меньший интерфейс SubJ.
k3b
27

Весь смысл реализации интерфейса заключается в том, чтобы заключить между вызывающим абонентом и вызываемым абонентом надежный договор, который никогда не изменится, а детали реализации могут отличаться.

Реализуя интерфейс J, вы рассказываете внешнему миру, что вы можете сделать.

Если вам не нужна половина того, что делает J, то либо интерфейс должен быть действительно подразделен, так как он не такой маленький, как мог бы быть, либо вам нужно добавить NotImplementedExceptionметоды и свойства, которые вам не нужны. Однако это не лучшее решение, так как оно вводит в заблуждение людей в отношении того, что может сделать ваш код.

ChrisF
источник
5

Если вашему потребительскому классу A не требуется весь интерфейс J, я бы предложил создать новый интерфейс (назовите его K), который подробно описывает все, что ему требуется.

Это дает четкий сигнал любому, кто использует класс A, о том, какова его сторона договора. Тем самым улучшаются шансы на повторное использование. Если кому-то нужно предоставить объект, который реализует большой и сложный интерфейс, для того, чтобы сделать что-то относительно простое, он, скорее всего, в конечном итоге напишет то, что класс А делает сам.

Чтобы класс A мог использовать объекты, которые реализуют только интерфейс J, вы можете предоставить класс-оболочку, который реализует интерфейс K и передает любые соответствующие вызовы члену интерфейса J.

Пол Бучер
источник
3

Хотя я согласен с существующими ответами, в которых говорится, что вам действительно нужно либо полностью реализовать J (за исключением не реализованных методов), либо не совсем, возможный обходной путь заключается в следующем:

  • Создайте меньший интерфейс K, который является подмножеством J и реализует необходимые методы.
  • Создайте оболочку для A, которая принимает объекты, реализующие J и K.
  • В случае передачи в J, просто протолкните его к экземпляру A.
  • В случае передачи в K создайте анонимную реализацию J, связав только те методы из K в J, которые там есть. Передать результат в экземпляр A.

Обратите внимание, что это довольно уродливое решение, так как для него требуется обертка, которая принимает несколько вещей, которые по сути одинаковы. Но это достигает следующего:

  • Никаких изменений в J или A.
  • Нет "открытых" реализаций J, которые являются неполными.

Если ваш ОО-язык не позволяет создавать анонимные интерфейсы, вы можете создать фиктивную реализацию интерфейса и реализовать ее.

Декард
источник
1

Сколько времени займет реализация всего интерфейса?

Если это займет у вас значительное количество времени, это указывает на проблему с дизайном, и я бы посоветовал вам (как это делал Стивен А. до меня) связаться с владельцем и посмотреть, можно ли его изменить в будущем.

Используете ли вы интерфейс в своем собственном коде, независимо от библиотеки интерфейса?

Как предположил Стивен А., вы можете использовать свой собственный интерфейс так, как вы хотели бы видеть его в своем собственном коде. Это, по крайней мере, сохраняет ваш внутренний код в чистоте. Вы также можете отправить это владельцу в качестве интерфейса, который вы ожидаете найти. Возможно, он согласен с вами, или он может объяснить вам, почему интерфейс не должен быть разделен.

Нужна ли вашей реализации неиспользуемые элементы интерфейса?

Если вы можете ожидать, что они никогда не будут вызваны, поскольку они «не поддерживаются», я предпочитаю использовать NotSupportedException. Это дает четкое указание, что вы никогда не будете поддерживать этот интерфейс, а не реализовали его.

Стивен Джеурис
источник
Хорошая идея NotSupportedExceptionего использования дает понять, что вы не поддерживаете этот метод.
ChrisF
@ChrisF: я использую только NotImplementedExceptionдля производственного кода, где я бы написал «TODO: реализовать это». К сожалению, они не появляются в списке задач Visual Studio . Но на практике я едва ли оставляю метод нереализованным более двух дней. Resharper также показывает эти исключения жирным шрифтом (по крайней мере, с моей настройкой). :)
Стивен Джеурис
0

Реализуйте то, что вам нужно, и добавьте исключение NoImplementedException в другие.

Это вопрос использования. Если вы не используете интерфейс или знаете, что ваш код не будет использовать интерфейс, не тратьте время на это, поскольку вам не нужно тратить время на избыточный код.

Работайте над поставленной задачей и следите за тем, чтобы другие следовали, если они хотят использовать интерфейс.

Многие интерфейсы в Java не реализованы в полной мере.

Это подход Apache к вещам: http://commons.apache.org/lang/api-2.4/org/apache/commons/lang/NotImplementedException.html

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