Что предпочтительнее в Java: хранить поле дважды или сохранять его защищенным в базовом классе? - PullRequest
2 голосов
/ 13 ноября 2010

Я редко использую наследование, но иногда я сталкиваюсь со следующей проблемой. Дано:

  • Я хочу добавить объект в конструктор родительского класса, скажем, Logger.
  • Учитывая, что родитель и подкласс должны использовать это поле.
  • Учитывая, что я не хочу выставлять поле для внешнего мира (нет добытчика).

Я видел, как это решалось двумя способами:

class ChildClass extends ParentClass {
  private Logger logger;
  public ChildClass(Logger logger) {
    super(logger);
    this.logger = logger;
  }
}

class ParentClass {
  private Logger logger;
  public ParentClass(Logger logger) {
    this.logger = logger;
  }
}

Или:

class ChildClass extends ParentClasss {
  public ChildClass(Logger logger) {
    super(logger);
  }
}

class ParentClass {
  protected Logger logger;
  public ParentClass(Logger logger) {
    this.logger = logger;
  }
}

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

Лично я предпочитаю второй подход с обоснованием, что меньший размер данных превосходит инкапсуляцию, но я часто встречал первую версию в дикой природе. Есть ли строго предпочтительный способ сделать это?

Ответы [ 4 ]

3 голосов
/ 13 ноября 2010

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

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

2 голосов
/ 13 ноября 2010

Второй вариант лучше с точки зрения дизайна и производительности, предполагая, что регистратор необходим в родительском классе.Наличие двух атрибутов, содержащих одно и то же значение, может сбивать с толку и занимать больше места.(Последнее имеет значение только в том случае, если создается большое количество экземпляров.)

Однако:

  • Я бы объявил его как private в родительском классе и предоставил protected final метод получения.
  • Если вы объявите его как атрибут protected родительского класса, вы должны также сделать его final.
  • Если вы используете один изСтандартные API-интерфейсы ведения журнала - это обычная практика, когда объект создает свой собственный регистратор;например, с Log4j

      this.logger = Logger.getLogger(this.getClass());
    

Это все относительно незначительные проблемы, но (IIRC) набор правил PMD по умолчанию включает в себя правило, содержащее атрибуты protected.

... но я часто сталкивался с первой версией в дикой природе.

Да, много кода в дикой природе не совсем идеально, и, как я полагаю, этоотносительно незначительные проблемы.

FOLLOWUP

говорят, что оба поля являются окончательными.Как вы думаете, в первом случае компилятор будет достаточно умен, чтобы выяснить, что указатели всегда будут одинаковыми и свернуть их в одно поле?

Нет.Я не думаю, что компилятор сделал бы это:

  • Любая оптимизация должна работать перед лицом отражающего доступа (и даже обновления) атрибутов.В этом случае код отражения должен был бы знать об оптимизации и отображать запросы для одной версии поля на другую.

  • В большинстве случаев выигрыш за такую ​​оптимизацию был бы небольшими стоимость ЦП компилятора JIT, выясняющего, что оптимизация допустима, будет значительной.

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

Однако я былиспользуя некоторое время Guice, похоже на стандартную практику внедрения логгера вместо того, чтобы полагаться на статическую фабрику.

Я думаю, что это специфическая для Guice идиома / практика.Обычные Java-приложения / библиотеки редко передают логгеры в качестве аргументов из моего опыта.

0 голосов
/ 13 ноября 2010

Если вы абсолютно не хотите открывать поле для внешнего мира, вам нужно сделать его private, так как даже поле protected видимо членам его пакета (а также любого подкласса).Тем не менее, это может быть излишним в случае Logger.Наследование может быть проще для понимания / поддержки реализации.

0 голосов
/ 13 ноября 2010

Я бы также использовал второй подход.

Я не уверен, почему вы так беспокоитесь об объеме памяти, но разница между этими двумя подходами незначительна.Технически, вы не храните logger дважды.В Java все объект переменные являются указателями .Таким образом, в первом сценарии и ParentClass, и ChildClass указывают на один и тот же экземпляр logger.

Второй подход - классический пример одного из преимуществ наличия наследования, то есть совместного использования функциональностии свойства.

Если честно, это дизайнерское решение, которое полностью зависит от вас, ни одно из них не окажет существенного влияния на объем памяти, но второй подход использует принципы ООП больше, чем первый.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...