Агрегация / Состав Инкапсуляции - PullRequest
2 голосов
/ 29 августа 2009

Статья в Википедии о состояниях инкапсуляции:

«Инкапсуляция также защищает целостность компонента, не давая пользователям переводить внутренние данные компонента в недопустимое или несовместимое состояние»

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

Один участник дискуссии утверждал, что в этом вопросе вы должны проводить различие между агрегацией и композицией. В основном я думаю, что он таков:

  • Если вы хотите вернуть объект, являющийся частью композиции (например, точку прямоугольника), клонируйте его.
  • Если вы хотите вернуть объект, который является частью агрегации (например, Пользователь как часть UserManager), просто верните его, не прерывая ссылку.

Это имело смысл и для меня. Но сейчас я немного растерялся. И хотел бы узнать ваше мнение по этому вопросу.

Строго говоря, всегда ли инкапсуляция требует клонирования?

PS .: Я программирую на PHP, где управление ресурсами может быть немного более актуальным, поскольку это язык сценариев.

Ответы [ 2 ]

3 голосов
/ 14 июня 2011

Строго говоря, всегда ли инкапсуляция требует клонирования?

Нет, это не так.

Упоминаемый вами человек, вероятно, путает защиту состояния объекта с защитой деталей реализации объекта.

Запомните: инкапсуляция - это метод увеличения гибкости нашего кода. Хорошо инкапсулированный класс может изменить свою реализацию, не влияя на его клиентов . Это суть инкапсуляции.

Предположим, следующий класс:

class PayRoll {
   private List<Employee> employees;

   public void addEmployee(Employee employee) {
       this.employees.add(employee);
   }
   public List<Employee> getEmployees() {
       return this.employees;
   }
}

Теперь этот класс имеет низкую инкапсуляцию. Можно сказать, что метод getEmployees нарушает инкапсуляцию, потому что, возвращая тип List, вы больше не можете изменить эту деталь реализации, не затрагивая клиентов класса. Я не мог изменить его, например, для коллекции Map, не влияя на код клиента.

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

public List<Employee> getEmployees() {
    return this.employees.clone();
}

Можно сказать, что приведенный выше код улучшает инкапсуляцию в том смысле, что теперь addEmployee - единственное место, где можно изменить внутренний список. Поэтому, если у меня есть дизайнерское решение добавить новые элементы Employee в начало списка, а не в хвост. Я могу сделать эту модификацию:

public void addEmployee(Employee employee) {
    this.employees.insert(employee);  //note "insert" is used instead of "add"
}

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

Реализация с более высокой инкапсуляцией будет делать что-то вроде этого:

class PayRoll {
   private List<Employee> employees;

   public void addEmployee(Employee employee) {
       this.employees.add(employee);
   }
   public Employee getEmployee(int position) {
       return this.employees.get(position);
   }
   public int count() {
       return this.employees.size();
   }
}

Теперь, если я хочу изменить Список для Карты, я могу сделать это свободно. Кроме того, я не нарушаю поведение, которого, вероятно, ожидают клиенты: при изменении объекта Employee из PayRoll эти изменения не теряются.

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

0 голосов
/ 29 августа 2009

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

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

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

...