Лучший подход к расширению функциональности класса в Java?

Asked
Viewd953

3

для краткости это можно перефразировать как «наследование или библиотека функций»

, например, я хотел бы добавить метод к javax.servlet.http.HttpServletRequest, который дает мне все тело, метод getBody (), который будет читать тело через метод getReader, просто для примера. .

На других языках, таких как ruby ​​или javascript, вы можете добавить метод к базовому классу или даже к конкретному экземпляру, но в java я вижу два варианта ...

  1. расширить HttpServletRequest (что-то вроде MyHttpServletRequest) и добавить метод

  2. или создайте статический класс HttpServeletHelper со статическими методами со следующим методом

общедоступная статическая строка HttpServeletHelper.getBody (запрос HttpServletRequest)

первый подход более объектно-ориентированный и элегантный, но вынуждает вас приводить свой объект каждый раз, когда он вам нужен, и каким-то образом вы должны указать jsp использовать ваш класс ...

второй подход - это просто старая добрая библиотека функций ... которая может быть хорошей или плохой, в зависимости от того, как вы на нее смотрите ...

Какие плюсы и минусы вы видите в каждом подходе и какой из них более рекомендуется для таких ситуаций?

4 ответов

2

В вашем конкретном примере есть по крайней мере одна проблема: движки сервлетов / JSP не знают и не заботятся о вашем подклассе. Они будут иметь дело только с HttpServletRequest. Я полагаю, вы можете обернуть его и отлить, но я бы не назвал это «элегантным».

Вы даже не упомянули другую возможность: композицию. У вас может быть нестатический класс, который принимает HttpServletRequest в качестве аргумента ctor и полагается на него как на реализацию методов ServletRequest. Вы добавляете дополнительный вызов getBody для выполнения вашего специального поведения.

Возможно, прочтение этого поможет.

Есть и другие SO-вопросы о предпочтении композиции перед наследованием. Посмотрите, что вы об этом думаете.

Я бы также использовал вспомогательный класс для этого конкретного примера, но не дай бог синглтон. В этом нет необходимости или желательности.

Этот пример кажется особенно неудобным. Вы действительно этого хотите?

  • до сих пор я решил эту проблему с помощью второго варианта, вспомогательного класса ... почему вы находите это таким странным ??? я что-нибудь упускаю?

    opensas23 июня 2009, 01:21
  • С другой стороны, я проверю вашу ссылку, похоже, я пропустил интересный вариант ... спасибо!

    opensas23 июня 2009, 01:22
1

Какой вариант я выберу, зависит от того, как вы планируете использовать класс. С одной стороны, если вы планируете вставить этот класс в существующий конвейер обработки, который уже использует HttpServletRequest, я бы выбрал метод наследования.

Для всех остальных ситуаций (которые я вижу сейчас) я бы выбрал вспомогательный класс. Однако я бы не стал делать его статическим, поскольку статические методы, как известно, сложно тестировать. Вместо этого я бы реализовал некоторую форму шаблона Singleton со статическим получателем.

  • Я согласен, это зависит от того, как вы планируете его использовать, но в большинстве случаев вспомогательный класс будет лучшим вариантом. Я бы рекомендовал создать один экземпляр помощника и внедрить его там, где это необходимо, в отличие от синглтона, но это мое предпочтение

    rojoca23 июня 2009, 01:16
  • @rojaca: О, я согласен, это, вероятно, лучший способ реализовать помощник; Синглтон очень часто используется IMO. Но… поскольку это для Java, и если один шаблон определяет Java, то это синглтон, шаблон синглтона, я подумал… Ну, вы знаете. :)

    Randolpho23 июня 2009, 02:21
2

См. GoF о композиции и наследовании . Единственное, что следует вынести из книги Шаблоны проектирования , - это всегда отдавать предпочтение композиции перед наследованием . Причина того, что наследование кажется более «элегантным», заключается в том, что система типов уже неявно преобразует подтипы в супертипы за вас. Но вы сталкиваетесь с ограничением в том, что запираетесь в определенной иерархии, которая становится чрезвычайно устойчивой к изменениям.

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

5

Я бы выбрал №2. HttpServletRequest является частью фреймворка, поэтому Tomcat (или что-то еще) собирается создать его экземпляр и передать в ваш код. Вы не сможете выбирать, использовать ли ваш подкласс. (На самом деле, с появлением таких фреймворков, как сервлеты, EJB и т. Д., Я считаю, что эта проблема довольно распространена.)

Кроме того, в последнее время много писали о чрезмерной зависимости от наследования как антипаттерна. В Java наследование - это «дефицитный ресурс»: для каждого класса вы можете сделать это только один раз. Вам следует зарезервировать наследование для вещей, которые действительно являются «подклассами», а не просто для добавления каких-то функций здесь и там.