Каковы лучшие практики для определения задач методов Конструктор, Инициализация и Сброс - PullRequest
2 голосов
/ 27 февраля 2009

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

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


Строительство

  1. Минимальные действия, необходимые для создать действительный объект, передает тест на существование
  2. Создавать и инициализировать только "один раз", никогда не перезаписывать, не переменные объекты, которые не будут меняться / изменяться в течение жизни Объекта
  3. Инициализация финальных членов
  4. По сути, заглушка времени выполнения

инициализация

  1. Сделайте объект полезным
  2. Создание и инициализация общедоступных членов
  3. Создание и инициализация закрытых членов, являющихся значениями переменных
  4. Объект должен теперь проходить внешние тесты без генерации исключений (при условии, что код верен)

Reset

  1. Ничего не создает
  2. Назначает значения по умолчанию всем переменным общедоступным / закрытым членам
  3. возвращает объект в точное состояние

Пример игрушки:

public class TestObject {
   private int priv_a;
   private final int priv_b;
   private static int priv_c;
   private static final int priv_d = 4;

   private Integer priv_aI;
   private final Integer priv_bI;
   private static Integer priv_cI;
   private static final Integer priv_dI = 4;  

   public int pub_a;
   public final int pub_b;
   public static int pub_c;
   public static final int pub_d = 4;

   public Integer pub_aI;
   public final Integer pub_bI;
   public static Integer pub_cI;
   public static final Integer pub_dI = 4;   

   TestObject(){
        priv_b = 2;
        priv_bI = new Integer(2);
        pub_b = 2;
        pub_bI = new Integer(2);
   }

   public void init() {
       priv_a = 1;
       priv_c = 3;
       priv_aI = new Integer(1);
       priv_cI = new Integer(3);

       pub_a = 1;
       pub_c = 3;
       pub_aI = new Integer(1);
       pub_cI = new Integer(3);
   }

   public void reset() {
       priv_a = 1;
       priv_c = 3;
       priv_aI = 1;
       priv_cI = 3;

       pub_a = 1;
       pub_c = 3;
       pub_aI = 1;
       pub_cI = 3;
   }  
}

Ответы [ 5 ]

1 голос
/ 05 мая 2009

Я пришел из C ++, где правила немного отличаются от Java, но я думаю, что эти принципы двухэтапной инициализации применимы в общем случае.

Строительство

  1. Все, что может быть сделано во время строительства должно быть сделано во время строительства.
  2. Сведите к минимуму количество "плохости", которое может возникнуть в результате попытки использовать ваш объект перед вызовом init().
  3. Все переменные-члены нуждаются в значении, даже если это обычно недопустимое значение часового (например, установите указатели на ноль). Я думаю, что Java по умолчанию инициализирует все переменные равными нулю, поэтому вам нужно выбрать что-то еще, если это допустимое число.

Initialization

  1. Инициализируйте те переменные-члены, которые зависят от существования других объектов. В основном, делайте то, что вы не могли делать во время строительства.
  2. Убедитесь, что ваш объект находится в завершенном, готовом к использованию состоянии. Подумайте об исключении, если это не так.

Reset

  1. Задумайтесь долго и усердно о том, в каком состоянии находится Система, когда вы захотите вызвать такую ​​функцию. Может быть, лучше создать новый объект с нуля, даже если эта операция кажется дорогой. Профилируйте свой код, чтобы узнать, если это проблема.
  2. Предполагая, что вы прошли элемент 1, подумайте над написанием метода для обработки того, что нужно сделать reset() и вашему конструктору. Это облегчает обслуживание и позволяет избежать дублирования кода.
  3. Верните ваш объект в то же состояние, в котором он находился после init().
1 голос
/ 29 июня 2009

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

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

0 голосов
/ 25 ноября 2009

Интересно. Я особенно нахожу эту конструкцию полезной, если у вас есть объект, который должен выполнять операции ввода-вывода. Я не хочу, чтобы что-либо выполняло операции ввода-вывода прямо или косвенно в своем конструкторе. Это делает объект кошмаром для использования.

0 голосов
/ 31 июля 2009

Есть ли причина, по которой init() и reset() должны быть разными? В этом простом примере трудно понять, почему важно правило «ничего не создавать».

Кроме того, я думаю, что объекты должны быть полезны, как только они будут построены. Если есть причина - некоторая круговая зависимость или проблема наследования - объект имеет , который должен быть «инициализирован» после построения, я бы скрыл конструктор и инициализацию за статическим методом фабрики. (И, вероятно, для правильной меры перенесите код инициализации в отдельный объект конфигуратора.)

В противном случае вы рассчитываете на то, что вызывающие всегда будут вызывать как конструктор, так и init(), и это нестандартный шаблон. У нас здесь есть какой-то старый, слишком полезный для выброса код, который делает это; это абстрактный класс диалога, и каждый раз, когда кто-то расширяет его, он забывает вызывать constructUI(), а затем тратит 15 минут на размышления, почему его новый диалог пуст.

0 голосов
/ 27 февраля 2009

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

В последнее время я перешел только к использованию конструкторов по умолчанию и использованию установщиков свойств для инициализации объекта. Новый синтаксис C #, который позволяет легко сделать это в «конструктороподобном» формате, делает все это простым в использовании, и я считаю, что необходимость поддержки параметризованных конструкторов исчезает.

...