JavaWorld на OO: Получатели / Установщики против Строителя - PullRequest
11 голосов
/ 03 августа 2011

Справочная информация:

Я нашел эту статью на JavaWorld, где Аллен Голуб объясняет альтернативу геттерам / сеттерам, которая поддерживает принцип, согласно которому реализация объекта должна быть скрыта (его примеркод также можно найти ниже).

Объясняется, что классы Name / EmployeeId / Money должны иметь конструктор, принимающий одну строку - причина в том, что если вы наберете его какint, а затем нужно изменить его на long, вам придется изменить все виды использования класса, и с этим шаблоном вам не нужно.

Вопрос 1:

Мне было интересно: разве это не переносит проблему на анализ разбрасываемых параметров String?Например, если весь код, использующий EmployeeId (полученный от Exporter), анализирует String в int, и внезапно вы начинаете экспортировать значения long, вам необходимо изменить столько же вариантов использования.... и если вы начнете анализировать его как long, возможно, его придется заменить на double (даже если для идентификаторов это не имеет смысла) ... и если вы не можете быть уверены, что анализироватьString в, вы не можете ничего реализовать .

Вопрос 2:

Помимо этого вопроса, у меня есть еще один: я понимаю, что статья более семи летСтарый, так может кто-нибудь указать мне на некоторые недавние обзоры, касающиеся ОО-дизайна, и, в частности, на идеи, касающиеся дебатов о методах получения / установки и скрытия реализации?

Листинг 1. Сотрудник: Контекст Builder


  public class Employee
  {   private Name        name;
      private EmployeeId  id;
      private Money       salary;

      public interface Exporter
      {   void addName    ( String name   );
          void addID      ( String id     );
          void addSalary  ( String salary );
      }

      public interface Importer
      {   String provideName();
          String provideID();
          String provideSalary();
          void   open();
          void   close();
      }

      public Employee( Importer builder )
      {   builder.open();
          this.name   = new Name      ( builder.provideName()     );
          this.id     = new EmployeeId( builder.provideID()       );
          this.salary = new Money     ( builder.provideSalary(),
                                    new Locale("en", "US") );
          builder.close();
      }

      public void export( Exporter builder )
      {   builder.addName  ( name.toString()   );
          builder.addID    ( id.toString()     );
          builder.addSalary( salary.toString() );
      }

      //...
  }

Ответы [ 4 ]

10 голосов
/ 03 августа 2011

Вопрос 1 : Разбор строк кажется странным.ИМХО, вы можете сделать так много, чтобы предвидеть будущие улучшения.Либо вы используете параметр long с самого начала, чтобы быть уверенным, либо рассмотрите возможность добавления дополнительных конструкторов позже.В качестве альтернативы вы можете ввести расширяемый класс параметров.См. Ниже.

Вопрос 2 : Есть несколько сценариев, в которых может быть полезен шаблон построителя.

  • Создание сложных объектов

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

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

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

  • Расширяемый API

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

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

  • Неизменность

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

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

  • Удобочитаемость ("Свободный API")

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

Как правило, сборщики являются полезным шаблоном, и в зависимости от используемого вами языка они либо очень просты в использовании (например, Groovy), либо немного более утомительны (например, в Java)для провайдера API.Для потребителей, однако, они могут быть такими же легкими.

2 голосов
/ 03 августа 2011

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

Последняя идея заключается в использовании " плавного интерфейса ". Он работает с сеттерами, которые возвращают this. Часто set опускается в имени метода. Теперь вы можете написать:

User user = new User()
.firstName( "John" )
.familyName( "Doe" )
.address( address1 )
.address( address2 )
;

Это имеет несколько преимуществ:

  1. Это очень читабельно.
  2. Вы можете изменить порядок параметров, ничего не нарушая
  3. Может обрабатывать однозначные и многозначные аргументы (address).

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

Решение состоит в том, чтобы иметь много модульных тестов или специально добавить метод "init ()" или "done ()", который выполняет все проверки и устанавливает флаг "этот экземпляр правильно инициализирован".

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

User user = new UserFactory()
.firstName( "John" )
.familyName( "Doe" )
.address( address1 )
.address( address2 )
.build()
;

Современные языки, такие как Groovy , превращают это в языковую функцию:

User user = new User( firstName: 'John', familyName: 'Doe',
     address: [ address1, address2 ] )
2 голосов
/ 03 августа 2011

Вы можете реализовать Builders более лаконично. ;) Я часто нахожу написание Builders от руки утомительным и подверженным ошибкам.

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

0 голосов
/ 03 августа 2011

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

...