2010-04-01 3 views
10

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

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

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

+8

Вы должны найти лучший источник материала для чтения, мой друг. Не все, что вы читаете на межтрубках, верно. Getters/Setters - это противоположность злу. Если бы вы разместили источник этой жемчужины мудрости, мы могли бы разобрать ее, но, увы, ссылки не было. уже критиковал. Нооп. –

+4

@Sky Все зависит от контекста, некоторые использования Getters/Setters хороши, а другие - «злые» (хотя я бы не использовал это слово). Я стараюсь не быть таким догматичным. –

+0

@ Andreas - понятный. Неправильное использование любой конструкции может быть злым, но охарактеризовать геттеры/сеттеры как зло в том же контексте, что, например, eval просто глупо, ИМО. –

ответ

11

Аллен Голуб сделал большой всплеск с "Why getter and setter methods are evil" еще в 2003

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

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

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

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

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

Крайний вид не практичен.

+0

На самом деле - статья г-на Голуба совершенно правильная. Просто название звучит экстремально. Печально, что после этого люди перестают читать:/ – MaR

+0

@MaR Полностью согласен, он бесполезно противодействует большому количеству людей, которые в противном случае были бы открыты, чтобы услышать, что вы хотите сказать. –

+0

Я прочитал статью. Аллен Холуб - очень умный парень, но факт в том, что никто не следит за его советами. Объекты все еще записываются, код запущен, и любые проблемы, которые могут возникнуть, не могут быть помещены под ноги написания геттеров и сеттеров. На данный момент это академический аргумент. – duffymo

1

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

Purpose of private members in a class

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

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

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

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

int getSomeField(); 

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

+0

Он не сказал, что думает, что они злые, но что он читал, что они есть. – lugte098

+0

Я не имею в виду, что вы, должно быть, используете публичные поля, что я, и я не хочу раскрывать какие-либо подробности о реализации класса, в этом случае, какие поля у него есть, если это абсолютно необходимо. Статья здесь http://www.javaworld.com/javaworld/jw-09-2003/jw-0905-toolbox.html – kane77

+0

@ lugte098 «Я недавно прочитал, что геттеры/сеттеры злы, и я должен сказать, что это имеет смысл " –

4

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

Python люди не делают этого. Тем не менее, они все еще выполняют OO-программирование. Ясно, что суетливые геттеры и сеттеры не являются существенными.

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

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

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

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

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

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

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

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

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

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

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

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

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

+0

С Python против Java это также проблема синтаксиса - с Python синтаксис доступа к полю такой же, как синтаксис для чтения из геттера. Это означает, что вы можете изменить реализацию, не затрагивая весь код, который использует класс. С Java различие в реализации подвержено потребителям класса, они либо делают instance.foo, либо instance.getFoo(), поэтому изменение в реализации рябит через зависимости. С Python (и Ruby и C#) это всегда instance.foo, поэтому никакого догматического правила «всегда инкапсулировать ваши поля» не требуется. – Douglas

+0

Спасибо, что упомянул шаблон «Data Access Object»! –

0

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

+0

Я думаю, что это то, к чему стремится дискуссия. вы оставите его общедоступным и откройте его до такого злоупотребления или бессмысленно создайте геттеры и сеттеры, а затем откройте его для такого злоупотребления. – Anurag

4

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

+2

Я согласен с вами, хотя я не думаю, что лучший пример - 'std :: string'. Два 'operator []' в строке фактически являются формой функций get/set. –

1

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

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

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

1

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

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

2

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

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

1

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

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

Смежные вопросы