Инициализация «окончательных» полей различных типов в подклассах - PullRequest
3 голосов
/ 26 февраля 2012

Моя проблема почти такая же, как и проблема, опубликованная здесь: Абстрактный класс с окончательным неинициализированным полем , и мне нравится решение. Однако моя проблема немного сложнее в том, что абстрактный класс имеет несколько конечных полей различных типов. Например, у меня есть четыре int, два int[] и два double. Каков наилучший способ заставить подклассы инициализировать эти переменные?

Опции, которые я рассмотрел:

  • Преобразовать все поля в строки и передать с Map
  • Имейте действительно длинный конструктор суперкласса
  • Создать вспомогательный класс, который будет действовать как оболочка и инкапсулировать все значения, а затем передать экземпляр этого класса в базовый класс

Первый вариант не очень элегантен и выглядит немного сложнее, особенно с массивами. Второй вариант очень утомителен, а третий вариант просто переусердствовал.

Есть ли "правильный" способ сделать это? Или, если нет, какой из трех предложенных вариантов будет наиболее элегантным?

Ответы [ 4 ]

5 голосов
/ 26 февраля 2012

Я бы пошел со вторым: «Иметь действительно длинный конструктор суперкласса». Если мы следуем подходу, подробно описанному в вопросе , на который вы ссылались , конструктор суперкласса будет protected и не должен вызываться чем-то внешним по отношению к иерархии классов или пакету. Я всегда чувствую, что если что-то не раскрывается за этой границей, то есть не является частью «API», то это не имеет значения, как оно выглядит. Пусть у него будет восемь разных параметров разных типов или даже больше. Да, это видно из пакета, но из исходного решения ясно, что этот конструктор не должен вызываться ничем, кроме подклассов. Это еще одна мотивация для не public видимости.

Конечно, ваши инстинкты делать что-то чище верны, когда дело касается public вещей. Тот факт, что вы задавали этот вопрос вообще, показывает, что у вас есть правильные инстинкты.

2 голосов
/ 26 февраля 2012

Вот еще один вариант, при условии, что у вас есть контроль над всеми участвующими классами: абстрагируйте поля в суперклассе и объявите их в подклассах, вроде как ...

abstract class SuperClass {
  abstract int[] getFooArray(); // not public!
  abstract int getBar();
}

, а затем просто определите поле в каждом из этих подклассов, переопределяя методы для их возврата.

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

1 голос
/ 26 февраля 2012

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

Я не уверен, что понимаю сложность вашего сценария, но я интерпретирую вашу проблему следующим образом: я не хочу иметь массу аргументов в моем абстрактном конструкторе. Один из возможных подходов - иметь Builder для абстрактного класса, который используется конкретными подклассами. Затем конструктор «передается» абстрактному конструктору для установки окончательных полей.

0 голосов
/ 26 февраля 2012

Когда мне нужен неизменный объект (все члены являются окончательными), который принимает в конструкторе много разных параметров, я обычно использую шаблон Builder.

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

В качестве примера вы можете увидеть API Guava для ImmutableCollection Builder . Или, если вам не требуется неизменяемость, вот пример CacheBuilder , также взятый из той же библиотеки:

Cache<Key, Graph> graphs = CacheBuilder.newBuilder()
   .concurrencyLevel(4)
   .weakKeys()
   .maximumSize(10000)
   .expireAfterWrite(10, TimeUnit.MINUTES)
   .build(
       new CacheLoader<Key, Graph>() {
         public Graph load(Key key) throws AnyException {
           return createExpensiveGraph(key);
         }
       });

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

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

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