Правильный ООП дизайн без геттеров? - PullRequest
10 голосов
/ 01 апреля 2010

Я недавно прочитал, что геттеры / сеттеры - это зло, и я должен сказать, что это имеет смысл, но когда я начал изучать ООП, одним из первых вещей, которые я узнал, было «Инкапсулировать ваши поля», поэтому я научился создавать класс, давая ему несколько полей. , создать геттеры, сеттеры для них и создать конструктор, где я инициализирую эти поля. И каждый раз, когда какой-то другой класс должен манипулировать этим объектом (или, например, отображать его), я передаю ему объект, и он манипулирует им, используя getters / setters. Я вижу проблемы с этим подходом.

Но как это сделать правильно? Например, отображение / рендеринг объекта, который является классом «data» - скажем, Person, у которого есть имя и дата рождения. Должен ли класс иметь метод для отображения объекта, в котором некоторый Renderer будет передан в качестве аргумента? Разве это не нарушает принципа, что у класса должна быть только одна цель (в данном случае состояние хранилища), поэтому он не должен заботиться о представлении этого объекта.

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

Ответы [ 9 ]

11 голосов
/ 01 апреля 2010

Аллен Холуб произвел большой всплеск с «Почему методы получения и установки злые» в 2003 году.

Здорово, что вы нашли и прочитали статью. Я восхищаюсь всеми, кто изучает и критически думает о том, что они делают.

Но возьми мистера Голуба с крошкой соли.

Это одна точка зрения, которая привлекла большое внимание из-за своей крайней позиции и использования слова «зло», но она не подожгла мир и не была принята как догма.

Посмотрите на C #: они фактически добавили синтаксический сахар в язык, чтобы упростить написание операций get / set. Либо это подтверждает чей-то взгляд на Microsoft как на империю зла, либо противоречит заявлению господина Голуба.

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

Экстремальный вид не практичен.

4 голосов
/ 01 апреля 2010

Если у вас есть геттеры и сеттеры, у вас нет инкапсуляции.И они не нужны.Рассмотрим класс std :: string.Это имеет довольно сложное внутреннее представление, но не имеет геттеров или сеттеров, и только один элемент представления (вероятно) открывается просто путем возврата его значения (т.е. size ()).Вот к чему вы должны стремиться.

4 голосов
/ 01 апреля 2010

«Инкапсулируйте ваши поля», поэтому я научился создавать класс, давать ему несколько полей, создавать методы получения, установки

Python люди не делают этого. Тем не менее, они все еще занимаются ОО-программированием. Очевидно, что суетливые добытчики и сеттеры не нужны.

Они распространены из-за ограничений в C ++ и Java. Но они не кажутся необходимыми.

Люди Python используют properties иногда для создания функций получения и установки, которые выглядят как простой атрибут.

Дело в том, что "Инкапсуляция" - это стратегия Design . Это не имеет ничего общего с реализацией. Вы можете иметь все общедоступные атрибуты и все же красиво оформленный дизайн.

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

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

В Python, где вы можете видеть весь исходный код, понять интерфейс довольно просто. Нам не нужно предоставлять так много подсказок. Как мы говорим: «Используй источник, Люк» и «Мы все здесь взрослые». Мы все можем видеть исходный код, нам не нужно суетиться по поводу накапливания геттеров и сеттеров, чтобы предоставить еще больше советов о том, как работает API.

Например, отображение / рендеринг объекта, который является классом «data» - скажем, Person, у которого есть имя и дата рождения. Должен ли класс иметь метод для отображения объекта, в котором некоторый Renderer будет передан в качестве аргумента?

Хорошая идея.

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

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

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

Если это действительно проблема (и это может быть в некоторых приложениях), вы можете ввести Helper классы. Таким образом, класс PersonRenderer выполняет рендеринг данных Person. Таким образом, изменение Person также требует изменений PersonRenderer - и ничего больше. Это объект доступа к данным шаблон проектирования.

Некоторые люди делают Render внутренним классом, содержащимся в Person, поэтому Person.PersonRenderer требует более серьезного сдерживания.

2 голосов
/ 01 апреля 2010

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

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

1 голос
/ 01 августа 2010

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

http://www.mockobjects.com/files/endotesting.pdf

1 голос
/ 01 апреля 2010

В некоторых языках, таких как C ++, есть понятие friend. Используя эту концепцию, вы можете сделать детали реализации класса видимыми только подмножеству других классов (или даже функций). Когда вы используете Get / Set без разбора, вы предоставляете всем доступ ко всему.

При экономном использовании friend является отличным способом увеличения инкапсуляции.

1 голос
/ 01 апреля 2010

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

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

Другими словами, спросите себя, зачем мне эти методы доступа, как мне манипулировать этими объектами? Затем примените эти манипуляции в классах Visitor, чтобы сохранить клиентский код в чистоте, а также расширить функциональность классов сущностей, не загрязняя их код.

1 голос
/ 01 апреля 2010

Почему вы думаете, что добытчики злые? Смотрите пост с ответами, доказывающими обратное:

Назначение частных членов в классе

ИМХО в нем содержится много того, что по праву можно назвать "ООП-лучшими практиками".

Обновление: Хорошо, читая статью, на которую вы ссылаетесь, я более четко понимаю, в чем проблема. И это совсем не та история, которую предлагает провокационное название статьи. Я еще не читал полную статью, но AFAIU суть в том, что не следует без необходимости публиковать поля классов через бездумно добавленные (или сгенерированные) методы получения и установки. И с этим я полностью согласен.

Тщательно продумывая и концентрируясь на том, что вы должны делать, а не на том, вы сделаете это, вы исключите подавляющее большинство методов получения / установки в ваша программа. Не спрашивайте, какая информация вам нужна для работы; попросите объект, у которого есть информация, выполнить работу за вас.

Пока все хорошо. Тем не менее, я не согласен с тем, что предоставление такого геттера

int getSomeField();

изначально компрометирует ваш класс. Что ж, это так, , если вы не спроектировали интерфейс класса хорошо . Тогда, конечно, может случиться так, что вы слишком поздно поймете, что поле должно быть long, а не int, и его изменение нарушит 1000 позиций в клиентском коде. ИМХО в таком случае виноват дизайнер, а не бедный добытчик.

0 голосов
/ 01 апреля 2010

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

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