Аллен Голуб писал: "Вы никогда не должны использовать функции get/set", правильно ли он?

Аллен Голуб написал следующее,

У вас не может быть программы без какой-либо связи. Тем не менее, вы можете свести к минимуму сцепление значительно с помощью раскольнических предписаний OO (объектно-ориентированных) (самое главное, что реализация объекта должна быть полностью скрыта от объектов, которые его используют). Например, переменные экземпляра объекта (поля-члены, которые не являются константами) всегда должны быть частными. Период. Без исключений. Когда-либо. Я серьезно. (Иногда вы можете эффективно использовать защищенные методы, но защищенные переменные экземпляра являются мерзостью.)

Звучит разумно, но затем он продолжает:

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

Который, честно говоря, просто звучит безумно для меня.

Я понимаю принцип скрытия информации, но без аксессуаров и мутаторов вы вообще не можете использовать Java beans. Я не знаю, как вы будете следовать проекту MVC без аксессуаров в модели, поскольку модель не может нести ответственность за визуализацию представления.

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

Статьи Allen Holub для справки


Вопросы, относящиеся:

Ответ 1

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

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

Голуб не доверяет вам знать разницу. Я думаю, что знание разницы - это то, что делает вас профессионалом.

Ответ 2

Я думаю, что Аллен Голуб попытался сказать, перефразированный в этой статье, является следующим.

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

У программистов проблем есть, и Аллен Голуб был прав, указывая на то, что они иногда используют геттеры/сеттеры для всех переменных. И цель инкапсуляции теряется.

Ответ 3

Внимательно прочитайте эту статью. Голуб настаивает на том, что геттеры и сеттеры - это злой "антипаттерн по умолчанию", плохая привычка, которую мы вставляем при разработке системы; потому что мы можем.

Мыслительный процесс должен идти по строкам; Что делает этот объект? Каковы его обязанности? Каково его поведение? Что он знает? Думать долго и упорно по этим вопросам ведет естественным образом в направлении разработки классов, которые вскрывают интерфейс на самом высоком уровне возможно.

Хорошим примером является автомобиль. Он предоставляет четко определенный, стандартизованный интерфейс высокого уровня. Я не забочусь о setSpeed(60)... о том, что MPH или km/h? Я просто ускоряюсь, круиз, замедляюсь. Мне не нужно думать о деталях в setSteeringWheelAngle(getSteeringWheelAngle()+Math.rad(-1.5)), я просто turn(-1.5), и детали позаботятся о под капотом.

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

Иногда фактические требования к классу непознаваемы раньше времени. Это круто, просто разобраться и использовать antipattern getter/setter, но когда вы знаете, на основе опыта, для чего используется класс, вы, вероятно, захотите вернуться и очистить грязный интерфейс низкого уровня. Рефакторинг на основе "материала, который вы хотели бы знать, когда вы пишете присоску в первую очередь" является номиналом для курса. Вам не нужно все знать, чтобы начать, просто потому, что чем больше вы знаете, тем меньше может потребоваться переделка по пути.

Это менталитет, который он продвигает. Геттеры и сеттеры - легкая ловушка.

Да, beans в основном требуют геттеры и сеттеры, но для меня bean - особый случай. beans представляют существительные, вещи, ощутимые идентифицируемые (если не физические) объекты. Не так много объектов фактически имеют автоматическое поведение; в большинстве случаев все манипулируется внешними силами, в том числе людьми, чтобы сделать их продуктивными.

daisy.setColor(Color.PINK) имеет смысл. Что еще можно сделать? Может быть, вулканское сознание, чтобы цветок хотел быть розовым? Ммм?

У геттеров и сеттеров есть свое? зло? место. Это просто, как и все действительно хорошие вещи ОО, мы склонны их чрезмерно использовать, потому что они безопасны и знакомы, не говоря уже о простых, и поэтому было бы лучше, если бы ниобы не увидели или не услышали о них, по крайней мере, d овладел мыслями.

Ответ 4

(обратите внимание, что я прихожу к этому из угла "свойства".NET.

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

ОК, аргументы метода могут быть изменены как расширяющие преобразования, но.... только почему... Также обратите внимание, что в С# ключевое слово var может уменьшить значительную часть этой воспринимаемой боли.

Аксессоры не являются деталями реализации; они являются публичным API/контрактом. Да, если вы нарушите контркрафт, у вас проблемы. Когда это стало сюрпризом? Аналогичным образом, не редкость для аксессуаров быть нетривиальным - т.е. Они делают больше, чем просто обертывают поля; они выполняют вычисления, логические проверки, уведомления и т.д. И они позволяют использовать абстракции состояния на основе интерфейса. О, и полиморфизм - и т.д.

Повторите подробный характер аксессуаров (p3? 4?) - в С#: public int Foo {get; private set;} - выполненная работа.

В конечном счете, весь код является средством выражения нашего намерения компилятору. Свойства позволяют мне делать это в безопасном типе, на основе контракта, поддающемся проверке, расширяемом, полиморфном способе - спасибо. Зачем мне "исправлять" это?

Ответ 5

Я сделал несколько дополнительных Googling на Allen Holub, похоже, что у него есть доля противников в сообществе Java.

Последний особенно заострен. Текст комментатора выделен курсивом.

Хотя getIdentity начинается с "get", это не аксессор, потому что он не просто возвращает поле. Он возвращает сложный объект с разумным поведением

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

Кроме того, я замечаю, что Аллен радикально смягчил свою позицию со своей предыдущей колонны по той же теме, где мантра "Никогда не использовать аксессоров" не страдает одним исключением. Может быть, он понял через несколько лет, что аксессуры действительно служат цели...

Имейте в виду, что я фактически не ввел код пользовательского интерфейса в бизнес-логику. Я написал слой пользовательского интерфейса в терминах AWT (Abstract Window Toolkit) или Swing, которые являются одновременно абстракционными слоями.

Хороший. Что делать, если вы пишете свое приложение на SWT? Как "абстрактный" действительно является AWT в этом случае? Просто взгляните на это: этот совет просто заставляет вас писать код пользовательского интерфейса в своей бизнес-логике. Какой великий принцип. В конце концов, это было всего лишь по меньшей мере десять лет с тех пор, как мы определили эту практику как одно из худших проектных решений, которые вы можете сделать в проекте.

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

Ответ 6

Getters и seters используются как нечто большее, чем маска, чтобы сделать публичную переменную public.

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

Ответ 7

Когда мне представляются подобные идеи, я хотел бы взглянуть на библиотеки и фреймворки, которые я использую и которые мне нравится использовать.

Например, хотя некоторые из них не согласятся, мне нравится API Java Standard. Мне также нравится Spring Framework. Глядя на классы в этих библиотеках, вы заметите, что очень редко есть сеттеры и геттеры, которые есть только для того, чтобы выставить некоторую внутреннюю переменную. Существуют методы с именем getX, но это не значит, что это геттер в обычном смысле.

Итак, я думаю, что у него есть точка, и это так: каждый раз, когда вы нажимаете кнопку "Generate getters/setters" в Eclipse (или вашей IDE по выбору), вы должны сделать шаг назад и задаться вопросом, что вы делаете, Действительно ли целесообразно подвергать это внутреннее представление, или я испортил свой проект на каком-то этапе?

Ответ 8

Я не верю, что он никогда не использовал get/set, а скорее то, что использование get/set для поля не лучше, чем просто создание поля public (например, public string Name vs. public string Name {get; set; }).

Если используется get/set, это ограничивает скрытие информации OO, которое может потенциально заблокировать вас в плохом интерфейсе.

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

Однако, если вместо использования get/set у вас изначально был такой метод, как Add (имя строки), внутри вы могли бы обрабатывать имя сингулярно или добавлять в список, или то, что нет и извне вызывать метод Add столько раз, сколько вы хотите добавить другие имена.

Целью OO является проектирование с уровнем абстракции; не выставляйте больше деталей, чем вам абсолютно необходимо.

Скорее всего, если вы просто завернули примитивный тип с помощью get/set, вы нарушили этот принцип.

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

Ответ 9

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

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

Есть некоторые преимущества getters и seters над публичными переменными. Они обеспечивают некоторое разделение интерфейса и реализации. Просто потому, что точка имеет функцию .getX(), это не означает, что должен быть x, так как .getX() и .setX() можно сделать так, чтобы работать просто с радиальными координатами. Другим является то, что можно поддерживать инварианты классов, делая все необходимое, чтобы класс был совместим внутри сеттера. Другим является то, что возможно иметь функциональность, которая запускается на множестве, например, ведение журнала для баланса банковского счета.

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

Итак, я бы сказал, что использовать частные переменные почти исключительно, getters и seters, где они имеют реальное значение в поведении объекта, а не иначе.

Просто потому, что геттеры и сеттеры часто злоупотребляют, это не значит, что они бесполезны.

Ответ 10

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

Это разрушает инкапсуляцию, потому что, когда вы вызываете метод get/set, вам сначала нужно знать имя (или иметь хорошую идею) поля, которое вы хотите изменить, и, во-вторых, вы должны знать его тип, например. вы не можете позвонить

setPositionX("some string");

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

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

public void tryPositionX(int x) throws InvalidParameterException{
    if (x >= 0)
        this.x = x;
    else
        throw new InvalidParameterException("Holy Negatives Batman!");
}

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

tryPositionXAndLog(int x) throws InvalidParameterException{
    tryPositionX(x);
    numChanges++;
}

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

Вы также упоминаете MVC и говорите, что модель не может нести ответственность за просмотр, в этом случае Аллен Голуб дает пример создания слоя абстракции, имея класс "give-me-a-JComponent-that-present-your-identity" , который, по его словам, "изолирует тождество, которое будет отображаться от остальной части системы". Я недостаточно опыт, чтобы прокомментировать, будет ли это работать или нет, но на первый взгляд это звучит неплохо.

Ответ 11

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

Ответ 12

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

На всякий случай кто-то немного нечеткий по концепции инкапсуляции, прочитайте здесь:

Инкапсуляция (компьютерная наука)

... и если они действительно злы, построит ли .NET концепцию Property на языке? (Методы Getter и Setter, которые выглядят немного красивее)

ИЗМЕНИТЬ

В статье упоминается инкапсуляция:

"Getters и seters могут быть полезны для переменных, которые вы специально хотите инкапсулировать, но вам не нужно использовать их для всех переменных. Фактически, использование их для всех переменных - неприятный запах кода".

Использование этого метода приведет к чрезвычайно сложному поддержанию кода в долгосрочной перспективе. Если вы обнаружите половину пути проекта, который охватывает годы, когда поле должно быть инкапсулировано, вам нужно будет обновить КАЖДОЙ ССЫЛК этого поля в вашем программном обеспечении, чтобы получить выгоду. Звучит намного умнее, чтобы использовать правильную инкапсуляцию спереди и безопасно получить головную боль позже.

Ответ 13

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