Выберите методы класса, когда есть делегирование ООП

Это простой сценарий для понимания этой проблемы о людях и их доме.

Person могут менять цвет своих House

Я создал эту диаграмму UML:

введите здесь описание изображения

Как показано на диаграмме выше:

  • Person может изменить цвет своего дома. Чтобы показать это, я использую метод changeHouseColor() в классе Person.
  • В классе House метод changeColor() предназначен для изменения атрибута цвета.

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

class Person {
    private House house;

    Person(House house) {
        this.house = house;
    }

    public void changeHouseColor() {
        house.changeColor(); // Delegation call
    }
}
class House {
    private String color;

    public string getColor() {
        return color;
    }

    public void changeColor(String color) {
        this.color = color;
    }
}

Я хочу знать:

  • Чтобы показать, что человек может изменить цвет своего дома, я использовал метод Person.changeHouseColor(). Нарушает ли добавление этого метода инкапсуляцию?
  • Если в Person классе этот метод не нужен, то как показать, что человек может менять цвет своего дома?
  • Разве в OOAD не обязательно включать действия человека как методы в класс person?

person Susantha7    schedule 02.01.2020    source источник
comment
Вместо person.getHouse().changeColor(color)?   -  person Mr. Polywhirl    schedule 02.01.2020
comment
Если вы не можете получить доступ к House напрямую из Person, это допустимо.   -  person Mr. Polywhirl    schedule 02.01.2020
comment
person.getHouse().changeColor(color) но это не показывает, что класс человека меняет цвет   -  person Susantha7    schedule 02.01.2020
comment
разве не обязательно, чтобы действия человека включались в качестве методов в person класс   -  person Susantha7    schedule 02.01.2020
comment
Ваша ассоциация неверна. Это отношение Extends. Используйте открытую стрелку или нет / имена ролей и запись через точку.   -  person qwerty_so    schedule 03.01.2020
comment
Забавный у вас дом с операцией changeColor(). Как это? Различное освещение? Думаю, днем ​​не сработает.   -  person qwerty_so    schedule 03.01.2020
comment
Зачем вообще нужна была эта операция? Если это только изменение свойства, вы также можете сделать его общедоступным атрибутом. Это в любом случае публично. Любой опрыскиватель может прийти и доказать, что это правда (хотя это не законно).   -  person qwerty_so    schedule 03.01.2020
comment
ИМХО, это не нарушает инкапсуляцию, но иногда может нарушать принцип единой ответственности. На ваш вопрос нет точного ответа, потому что они сильно зависят от контекста, в котором используются классы. Хотелось бы спросить, насколько разумно использовать эти два слова вместе, не зная всего предложения и темы текста.   -  person edwgiz    schedule 09.01.2020


Ответы (4)


Вкратце мои ответы:

  1. Нет, инкапсуляция не нарушена.
  2. То, что Person может менять цвет, уже дано Ассоциацией.
  3. Действия Person могут включать вызов собственных операций, но возможны и многие другие действия.

Дополнительные пояснения:

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

пункт 2: когда класс имеет доступ к экземплярам другого класса, он потенциально может использовать все свои общедоступные функции. В вашем примере есть Ассоциация между Person и House. Следовательно, Person может вызывать changeColor, но не обязан это делать. В более полной модели House может иметь гораздо больше функций, и не все из них могут быть предназначены для использования Person. В этом случае полезно определить интерфейс для конкретного клиента, содержащий только функции, необходимые классу Person. Затем ассоциация будет направлена ​​на этот интерфейс, который, в свою очередь, реализуется классом House. Класс Person по-прежнему технически не требуется для использования всех функций интерфейса. Тем не менее, я бы рассматривал ассоциацию с интерфейсом как явное указание на намерение разработчиков моделей, что все функции в конечном итоге будут использованы.

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

В полной дизайнерской модели поведение класса Person будет определено каким-то образом. Это может быть с Activity. В этой Деятельности может быть много Действий. Один может быть CallOperationAction, который звонит changeColor. Это будет явная спецификация того, как класс Person использует класс House. Он также может вызвать свою собственную операцию changeHouseColor, которая, в свою очередь, может быть указана с помощью действия.

В UML определено множество других действий, таких как ReadStructuralFeatureAction или CreateObjectAction. Они используются не так часто, так как нет особого смысла моделировать на таком уровне детализации. Тем не менее, я думаю, что знать о них полезно, чтобы лучше понять, как связаны Операции и Действия.

person Axel Scheithauer    schedule 06.01.2020
comment
в пункте 2 [[В этом случае полезно определить интерфейс для конкретного клиента]] можете ли вы объяснить, что это этот интерфейс для класса Person - person Susantha7; 08.01.2020
comment
В этом случае полезно определить клиентский интерфейс, содержащий только те функции, которые необходимы классу Person. это означает интерфейс для класса House я прав? - person Susantha7; 09.01.2020
comment
@ Susantha7: Да, интерфейс, реализованный классом House и используемый через ассоциацию классом Person. У него будет только одна операция changeColor(), поэтому его можно будет назвать Colorable. - person Axel Scheithauer; 13.01.2020

В общем, ничего плохого и в person.changeHouseColor(color), и в person.getHouse().changeColor(color) нет. Какой подход лучше, зависит от вашего домена.

Мы можем сказать, что person.getHouse().changeColor(color) нарушает Закон Деметры (LOD) или принцип наименьшего знания. Идея состоит в том, чтобы иметь знания только о "близко" связанных классах. Классы, имеющие экземпляр Person, не должны зависеть от его внутренней структуры и вместо этого вызывать только методы Person: person.changeHouseColor(color).

Этот подход имеет как преимущества, так и недостатки. Основным преимуществом является возможность изменения внутренней структуры классов (Person class) без переделки их клиентов.

Это определенно не нарушает парадигму инкапсуляции. Ограничение прямого доступа к некоторым компонентам объекта можно даже рассматривать как лучшую инкапсуляцию.

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

Но если вы хотите показать, что изменить House цвет может только человек, то модель, вероятно, не идеальна. Потому что любой класс, имеющий доступ к экземпляру House, может напрямую вызывать house.changeColor(color), даже не имея экземпляра Person. И будет экземпляр House по крайней мере в методе, который его создает:

House house = new House(Color.WHITE);
Person person = new Person(house);
person.changeHouseColor(Color.ORANGE); //OK
house.changeColor(Color.WHITE); //should we allow it?

Этот аспект во многом зависит от домена. Важно определить агрегаты в вашем домене.

person Evgeniy Khyst    schedule 05.01.2020
comment
Согласен: LoD — еще один веский аргумент против такого дизайна :-) - person Christophe; 09.01.2020

В этом сценарии есть два основных варианта:

  • добавить метод getHouse() к Person, который требует, чтобы клиентский код соблюдал это и использовал интерфейс House соответствующим образом, или
  • как и раньше, скройте делегата (House) с помощью дополнительного метода для Person.

Это классический компромисс при проектировании, который обсуждался в книге Фаулера Рефакторинг: улучшение дизайна существующего кода в качестве Скрыть делегат рефакторинг. Чтобы ответить на ваш вопрос, в дополнение к преимуществам документации, которые вы упомянули, скрытие делегата фактически увеличивает инкапсуляцию:

Если делегат изменяет свой интерфейс, изменения распространяются на всех клиентов сервера, использующих делегат. Я могу удалить эту зависимость, разместив на сервере простой метод делегирования, который скрывает делегат. Затем любые изменения, которые я вношу в делегат, распространяются только на сервер, а не на клиентов.

person SDJ    schedule 02.01.2020
comment
разве не обязательно, чтобы действия человека включались в качестве методов в person класс - person Susantha7; 02.01.2020
comment
Вообще говоря, это не обязательно: это дизайнерский выбор со своими плюсами и минусами. Однако точный контекст, в котором вы это понимаете, может диктовать дополнительные ограничения. - person SDJ; 02.01.2020
comment
Каковы плюсы и минусы ? любой материал, доступный для дальнейшего чтения - person Susantha7; 02.01.2020
comment
Фаулер, Рефакторинг: улучшение дизайна существующего кода, с. 189 - person SDJ; 02.01.2020
comment
это глава 8. Организация данных - замена подкласса полями - person Susantha7; 03.01.2020

Короче говоря

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

Плюсы - Когда это делать?

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

Но это не единственный способ распределить обязанности между классами, и есть много причин оставить дизайн более открытым, например: Что, если Person не покрасит свой дом сама, а попросит Painer сделать эту работу? от ее имени? Что, если бы, гипотетически, мэр деревни имел право принимать решение об изменении цвета и посылал своих маляров через деревню?

Минусы - по возможности избегайте!

В вашей конструкции есть большой недостаток. Это связано с принципом единой ответственности (SRP):

У класса должна быть только одна причина для изменения

Добавление changeHouseColor означает, что Person имеет несколько причин для изменения: во-первых, оно может измениться вместе с самой концепцией Person. Но тогда, возможно, его также придется изменить в результате изменений в House. Например, если завтра в интерфейсе House потребуется дополнительный параметр толщины для changeColor, изменится не только ваша реализация Person (предоставляя дополнительный параметр в вызове делегирования), но и интерфейс Person должен будет измениться, так что указана и толщина. Таким образом, небольшое изменение в House распространится на весь класс, использующий Person. Скрытая связь может быстро превратиться в технический долг.

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

Вывод

Разделение задач должно управлять вашим дизайном. Если понятие Person не зависит от понятия House, не связывайте их без необходимости. Делайте это только в случае крайней необходимости (например, если дом частный и никто ничего о нем знать не должен).

Кроме того, в UML нет необходимости добавлять метод changeHouseColor(), чтобы показать, что человек может изменить цвет дома. Простая ассоциация между Person и House показывает, что Person может вызывать любой общедоступный метод House.

Если вы предоставите getHouse() в личном кабинете или добавите публичный +myHouse в конец ассоциации, вы также сообщите, что другие классы могут получить доступ к House Person.

person Christophe    schedule 05.01.2020
comment
[Простая ассоциация между Person и House показывает, что Person может вызывать любой общедоступный метод House], тогда человек может вызывать методы в классе House, которые могут рассматриваться как поведение других классов (другие классы, связанные с House class) - person Susantha7; 08.01.2020
comment
Пресон может изменить цвет дома, используя метод Preson .changeHouseColor(), связанный с ним метод в классе HousechangeColor(). Термит может уменьшить силу дома, используя метод Termite.decreaseHouseStrength(), связанный с ним метод класса HousechangeStength(). - person Susantha7; 08.01.2020
comment
по-вашему, тогда Person класс может получить доступ к changeStength() методу, но человек не имеет ничего общего с House силой. тогда как запретить доступ к классу человека House.changeStength() - person Susantha7; 08.01.2020
comment
@ Susantha7, и это еще одна причина не добавлять changeHouseColor человеку: на самом деле ответственность за установку цвета принадлежит дому, и другие сущности также могут его вызывать. Также будьте осторожны с аналогиями. В реальном мире цвет дома определяется не домом, а краской, а красить дом может любой, кто имеет желание красить, утилизирует краску и имеет доступ в дом независимо от законных прав. права на объект. - person Christophe; 08.01.2020