Зачем использовать геттеры и сеттеры/аксессоры? [закрытый]
в чем преимущество использования геттеров и сеттеров - которые только получают и устанавливают - вместо того, чтобы просто использовать открытые поля для этих переменных?
Если геттеры и сеттеры когда-либо делают больше, чем просто получить/установить, я могу понять это очень быстро, но я не на 100% ясно, как:
public String foo;
хуже, чем:
private String foo;
public void setFoo(String foo) { this.foo = foo; }
public String getFoo() { return foo; }
в то время как первый занимает гораздо меньше шаблонного кода.
30 ответов:
на самом деле много веских причин рассмотреть возможность использования методов доступа вместо того, чтобы напрямую раскрывать поля класса - помимо просто аргумента инкапсуляции и упрощения будущих изменений.
вот некоторые из причин я знаю:
- инкапсуляция поведения, связанного с получением или установкой свойства - это позволяет легко добавлять дополнительные функции( например, проверку) позже.
- скрытие внутреннего представления свойства при предоставлении свойства с использованием альтернативного представления.
- изоляция вашего публичного интерфейса от изменений-позволяет публичному интерфейсу оставаться постоянным, в то время как реализация изменяется, не затрагивая существующих потребителей.
- управление семантикой времени жизни и управления памятью (утилизация) свойства-особенно важно в неуправляемых средах памяти (например, C++ или Цель-С).
- предоставление точки перехвата отладки для того, когда свойство изменяется во время выполнения - отладка, когда и где свойство изменяется на определенное значение, может быть довольно сложно без этого в некоторых языках.
- улучшена совместимость с библиотеками, которые предназначены для работы с геттером/сеттерами свойств-насмешка, сериализация и WPF приходят на ум.
- разрешение наследникам изменять семантику поведения свойства и предоставляется путем переопределения методов getter / setter.
- позволяет передавать геттер / сеттер в виде лямбда-выражений, а не значений.
- геттеры и сеттеры могут разрешать разные уровни доступа - например, get может быть общедоступным, но набор может быть защищен.
потому что через 2 недели (месяцы, годы), когда вы поймете, что ваш сеттер должен сделать больше чем просто установить значение, вы также поймете, что свойство было использовано непосредственно в 238 других классов : -)
открытое поле не хуже пары геттер / сеттер, которая ничего не делает, кроме возврата поля и назначения ему. Во-первых, ясно, что (в большинстве языков) нет функциональной разницы. Любая разница должна быть в других факторах, таких как ремонтопригодность или читаемость.
часто упоминаются многочисленные пары геттер/сеттер, нет. Там утверждают, что вы можете изменить реализацию, и ваши клиенты не должны быть перекомпилированы. Предположительно, сеттеры позволяют добавлять функции, такие как проверка позже и ваши клиенты даже не должны знать об этом. Однако добавление проверки к сеттеру-это изменение его предварительных условий,нарушение предыдущего договора, который был, довольно просто, "вы можете положить что-нибудь здесь, и вы можете получить то же самое позже от добытчика".
Итак, теперь, когда вы нарушили контракт, изменение каждого файла в кодовой базе-это то, что вы должны хотеть сделать, а не избегать. Если вы избегаете этого, вы делаете предположение, что весь код предполагал контракт для этих методов, было другим.
Если это не должно было договора, то интерфейс позволяет клиентам помещать объект в поврежденном состоянии. это полная противоположность инкапсуляции если это поле не может быть действительно установлено на что-либо с самого начала, почему не было проверки с самого начала?
этот же аргумент применяется к другим предполагаемым преимуществам этих сквозных пары геттер / сеттер: если вы позже решите изменить установленное значение, вы нарушаете контракт. Если вы переопределяете функциональность по умолчанию в производном классе, выходя за рамки нескольких безвредных изменений (таких как ведение журнала или другое ненаблюдаемое поведение), вы нарушаете контракт базового класса. Это является нарушением принципа Подстановочности Лискова, который рассматривается как один из постулатов ОО.
Если класс имеет эти тупые геттеры и сеттеры для всех полей, то это это класс, который не имеет инвариантов, ни нет договора. Это действительно объектно-ориентированный дизайн? Если весь класс имеет эти геттеры и сеттеры, это просто тупой держатель данных, а тупые держатели данных должны выглядеть как тупые держатели данных:
class Foo { public: int DaysLeft; int ContestantNumber; };
добавление проходных пар геттер / сеттер к такому классу не добавляет никакого значения. Другие классы должны предоставлять значимые операции, а не только операции, которые уже предоставляют поля. Вот как вы можете определить и сохранить полезные инварианты.
клиент: "что я могу сделать с объектом этого класса?"
дизайнер: "вы можете читать и писать многих переменных."
клиент: "ой... круто, наверное?"есть причины использовать геттеры и сеттеры, но если эти причины не существуют, создание пар геттер/сеттер во имя ложных богов инкапсуляции не является хорошей вещью. Допустимые причины для создания геттеров или сеттеров включают вещи часто упоминаются как потенциальные изменения, которые вы можете сделать позже, например, проверка или различные внутренние представления. Или, может быть, значение должно быть читаемым клиентами, но не доступным для записи (например, чтение размера словаря), поэтому простой геттер-хороший выбор. Но эти причины должны быть там, когда вы делаете выбор, а не только как потенциальная вещь, которую вы можете захотеть позже. Это экземпляр YAGNI (он тебе не понадобится).
многие люди говорят о преимуществах геттеров и сеттеров, но я хочу играть адвоката дьявола. Сейчас я отлаживаю очень большую программу, где программисты решили сделать все геттеры и сеттеры. Это может показаться приятным, но это кошмар обратной инженерии.
скажем, вы просматриваете сотни строк кода, и вы сталкиваетесь с этим:
person.name = "Joe";
это красиво просто кусок кода, пока вы не поймете его сеттер. Теперь, ты следуйте за этим сеттером и обнаружите, что он также устанавливает человека.имя, персона.фамилия, человек.ишуман, человек.hasReallyCommonFirstName, и вызывает человека.update (), который отправляет запрос в базу данных и т. д. О, вот где произошла утечка вашей памяти.
понимание локального фрагмента кода на первый взгляд является важным свойством хорошей читаемости, которое геттеры и сеттеры имеют тенденцию нарушать. Вот почему я стараюсь избегать их, когда могу, и минимизировать то, что они делают, когда я использую их.
в чистом объектно-ориентированном мире геттеров и сеттеров это Грозный анти-шаблон. Читайте эту статью:Геттеры/Сеттеры. Зло. Точка. В двух словах, они побуждают программистов думать об объектах как о структурах данных, и этот тип мышления является чисто процедурным (например, в COBOL или C). В объектно-ориентированном языке нет структур данных, а только объекты, которые предоставляют поведение (не атрибуты/свойства!)
вы можете узнать больше о в разделе 3.5 Элегантный Объекты (моя книга об объектно-ориентированном программировании).
есть много причин. Мой любимый - когда вам нужно изменить поведение или отрегулировать то, что вы можете установить на переменную. Например, допустим, у вас был метод setSpeed(int speed). Но вы хотите, что вы можете установить только максимальную скорость 100. Вы бы сделали что-то вроде:
public void setSpeed(int speed) { if ( speed > 100 ) { this.speed = 100; } else { this.speed = speed; } }
теперь что, если везде в вашем коде вы использовали публичное поле, а затем вы поняли, что вам нужно вышеуказанное требование? Получайте удовольствие от охоты на каждое использование публичного поля вместо просто модифицируйте свой сеттер.
мои 2 цента :)
одним из преимуществ методов доступа и мутаторов является то, что вы можете выполнять проверку.
например, если
foo
был публичным, я мог легко установить его вnull
а потом кто-то другой может попытаться вызвать метод на объект. Но его там больше нет! С помощьюsetFoo
метод, я мог бы гарантировать, чтоfoo
никогда не был установлен доnull
.аксессоры и мутаторы также позволяют инкапсулировать - если вы не должны видеть значение после его установки (возможно, оно установлено в конструктор, а затем используется методами, но никогда не должен быть изменен), он никогда не будет замечен никем. Но если вы можете позволить другим классам видеть или изменять его, вы можете предоставить правильный метод доступа и/или мутатор.
зависит от вашего языка. Вы отметили этот "объектно-ориентированный", а не" Java", поэтому я хотел бы указать, что ответ ChssPly76 зависит от языка. Например, в Python нет причин использовать геттеры и сеттеры. Если вам нужно изменить поведение, вы можете использовать свойство, которое обертывает геттер и сеттер вокруг доступа к базовому атрибуту. Что-то вроде этого:
class Simple(object): def _get_value(self): return self._value -1 def _set_value(self, new_value): self._value = new_value + 1 def _del_value(self): self.old_values.append(self._value) del self._value value = property(_get_value, _set_value, _del_value)
Ну я просто хочу добавить, что даже если иногда они необходимы для инкапсуляции и безопасности ваших переменных / объектов, если мы хотим закодировать реальную объектно-ориентированную программу, то нам нужно ПРЕКРАТИТЕ ЗЛОУПОТРЕБЛЯТЬ АКСЕССОРАМИ, потому что иногда мы сильно зависим от них, когда это действительно не нужно, и это делает почти то же самое, как если бы мы поставили переменные общедоступными.
Я знаю, это немного поздно, но я думаю, что есть некоторые люди, которые заинтересованы в работе.
Я сделал небольшой тест производительности. Я написал класс "NumberHolder", который, ну, содержит целое число. Вы можете прочитать это число с помощью метода добытчик
anInstance.getNumber()
или путем прямого доступа к номеру с помощьюanInstance.number
. Моя программа читает число 1,000,000,000 раз, с помощью обоих способов. Этот процесс повторяется пять раз, а время печати. У меня есть следующее результат:Time 1: 953ms, Time 2: 741ms Time 1: 655ms, Time 2: 743ms Time 1: 656ms, Time 2: 634ms Time 1: 637ms, Time 2: 629ms Time 1: 633ms, Time 2: 625ms
(время 1-это прямой путь, Время 2-это геттер)
вы видите, геттер (почти) всегда немного быстрее. Затем я попробовал с разным количеством циклов. Вместо 1 миллиона я использовал 10 миллионов и 0,1 миллиона. Результаты:
10 млн. циклов:
Time 1: 6382ms, Time 2: 6351ms Time 1: 6363ms, Time 2: 6351ms Time 1: 6350ms, Time 2: 6363ms Time 1: 6353ms, Time 2: 6357ms Time 1: 6348ms, Time 2: 6354ms
С 10 миллионов циклами, времена почти такие же. Вот 100 тысяч (0,1 миллиона) циклов:
Time 1: 77ms, Time 2: 73ms Time 1: 94ms, Time 2: 65ms Time 1: 67ms, Time 2: 63ms Time 1: 65ms, Time 2: 65ms Time 1: 66ms, Time 2: 63ms
также с различным количеством циклы, геттер немного быстрее, чем обычный способ. Я надеюсь, что это помогло вам.
Спасибо, это действительно прояснило мое мышление. Теперь вот (почти) 10 (почти) веских причин не использовать геттеры и сеттеры:
- когда вы поймете, что вам нужно сделать больше, чем просто установить и получить значение, вы можете просто сделать поле приватным, что мгновенно сообщит вам, где вы непосредственно обращались к нему.
- любая проверка, которую вы выполняете, может быть только контекстно-свободной,что редко бывает на практике.
- вы можете изменить устанавливаемое значение-это абсолютный кошмар, когда вызывающий абонент передает вам значение, которое они [shock horror] хотят, чтобы вы сохранили как есть.
- вы можете скрыть внутреннее представление-фантастический, так что вы убедитесь, что все эти операции симметричны правильно?
- вы изолировали свой публичный интерфейс от изменений под листами - если вы разрабатывали интерфейс и не были уверены, что прямой доступ к чему-то был в порядке, то вы должны были продолжал проектировать.
- некоторые библиотеки ожидают этого, но не многие - отражение, сериализация, макетные объекты все прекрасно работают с публичными полями.
- наследуя этот класс, вы можете переопределить функциональность по умолчанию - другими словами, вы можете действительно запутать вызывающих абонентов, не только скрывая реализацию, но и делая ее непоследовательной.
последние три я просто ухожу (N/A или D/C)...
Не используйте геттеры сеттеры, если это не требуется для вашей текущей доставки т. е. не думайте слишком много о том, что произойдет в будущем, если что-либо будет изменено его запрос на изменение в большинстве производственных приложений, систем.
думайте просто, легко, добавить сложность, когда это необходимо.
Я бы не воспользовался незнанием владельцев бизнеса глубокого технического ноу-хау только потому, что я думаю, что это правильно или мне нравится подход.
У меня крупная система написана без геттеров сеттеры только с модификаторами доступа и некоторые методы для проверки выполнения Биз логики. Если вам совершенно необходимы. Используй что угодно.
Я провел довольно много времени, думая об этом для случая Java, и я считаю, что реальные причины:
- код интерфейса, а не реализация
- интерфейсы определяют только методы, а не поля
эти методы печально известный геттер и сеттер....
мы используем геттеры и сеттеры:
- для повторного использования
- для выполнения проверки на более поздних этапах программирования
методы Getter и setter являются открытыми интерфейсами для доступа к закрытым членам класса.
инкапсуляция мантру
мантра инкапсуляции заключается в том, чтобы сделать поля частными и методы общедоступными.
Getter Методы:мы можем получить доступ к закрытая переменная.
Сеттер Методы:мы можем изменить частные поля.
несмотря на то, что методы getter и setter не добавляют новую функциональность, мы можем передумать возвращаться позже, чтобы сделать этот метод
- больше;
- безопасный; и
- быстрее.
в любом месте значение может быть использовано, метод, который возвращает это значение может быть добавлен. Вместо:
int x = 1000 - 500
использовать
int x = 1000 - class_name.getValue();
в терминах непрофессионала
Предположим, нам нужно сохранить детали этого
Person
. ЭтоPerson
с полямиname
,age
иsex
. Для этого необходимо создать методы дляname
,age
иsex
. Теперь, если нам нужно создать другого человека, становится необходимым создать методы дляname
,age
,sex
все снова.вместо этого мы можем создать в зернах
class(Person)
С геттер и сеттер. Так что завтра мы можем просто создать объекты этого Бобаclass(Person class)
всякий раз, когда нам нужно добавить нового человека (см. рисунок). Таким образом, мы повторно используем поля и методы класса bean, что намного лучше.
это может быть полезно для ленивой загрузки. Скажем, объект, о котором идет речь, хранится в базе данных, и вы не хотите его получать, если он вам не нужен. Если объект извлекается геттером, то внутренний объект может быть null, пока кто-то не попросит его, тогда вы можете получить его при первом вызове геттера.
У меня был базовый класс страницы в проекте, который был передан мне, который загружал некоторые данные из нескольких разных вызовов веб-службы, но данные в этих вызовах веб-службы не всегда используется на всех дочерних страницах. Веб-службы, для всех преимуществ, Пионер новых определений "медленный", так что вы не хотите, чтобы сделать вызов веб-службы, если вы не должны.
я перешел из общедоступных полей в геттеры, и теперь геттеры проверяют кэш, и если его там нет, вызовите веб-службу. Таким образом, с небольшим обертыванием было предотвращено множество вызовов веб-служб.
таким образом, геттер спасает меня от попыток выяснить, на каждой дочерней странице, что мне нужно. Если Мне это нужно, я звоню добытчику, и он идет, чтобы найти его для меня, если у меня его еще нет.
protected YourType _yourName = null; public YourType YourName{ get { if (_yourName == null) { _yourName = new YourType(); return _yourName; } } }
один аспект, который я пропустил в ответах до сих пор, спецификация доступа:
- для членов, у вас есть только одна спецификация доступа для установки и получения
- для сеттеров и геттеров вы можете настроить его и определить его отдельно
в языках, которые не поддерживают "свойства" (C++, Java) или требуют перекомпиляции клиентов при изменении полей на свойства (C#), использование методов get/set легче изменить. Например, добавление логики проверки в метод setFoo не потребует изменения открытого интерфейса класса.
в языках, которые поддерживают" реальные " свойства (Python, Ruby, возможно Smalltalk?) нет смысла методы get и set.
EDIT: я ответил на этот вопрос, потому что есть куча людей, изучающих Программирование, задающих этот вопрос, и большинство ответов очень технически компетентны, но их не так легко понять, если вы новичок. Мы все были новичками, поэтому я решил попробовать свои силы в более дружелюбном ответе новичка.
два основных из них-полиморфизм и валидация. Даже если это просто глупая структура данных.
допустим, у нас это просто класс:
public class Bottle { public int amountOfWaterMl; public int capacityMl; }
очень простой класс, который содержит, сколько жидкости в нем, и какова его емкость (в миллилитрах).
что происходит, когда я делаю:
Bottle bot = new Bottle(); bot.amountOfWaterMl = 1500; bot.capacity = 1000;
Ну, вы же не ожидали, что это сработает, верно? Вы хотите, чтобы была какая-то проверка. И что еще хуже, если я никогда не указывал максимальную мощность? О боже, у нас проблема.
но есть и другая проблема. Что, если бы бутылки были только одним типом контейнера? Что, если бы мы имели несколько контейнеров, все с емкостями и количеством заполненной жидкости? Если бы мы могли просто сделать интерфейс, мы могли бы позволить остальной части нашей программы принять этот интерфейс, а бутылки, канистры и всевозможные вещи просто работали бы взаимозаменяемо. Разве это не было бы лучше? Поскольку интерфейсы требуют методов, это тоже хорошо.
мы бы в конечном итоге с чем-то вроде:
public interface LiquidContainer { public int getAmountMl(); public void setAmountMl(int amountMl); public int getCapacityMl(); public void setCapcityMl(int capacityMl); }
великолепно! А теперь мы просто меняем бутылку на это:
public class Bottle extends LiquidContainer { private int capacityMl; private int amountFilledMl; public Bottle(int capacityMl, int amountFilledMl) { this.capacityMl = capacityMl; this.amountFilledMl = amountFilledMl; checkNotOverFlow(); } public int getAmountMl() { return amountFilledMl; } public void setAmountMl(int amountMl) { this.amountFilled = amountMl; checkNotOverFlow(); } public int getCapacityMl() { return capacityMl; public void setCapcityMl(int capacityMl) { this.capacityMl = capacityMl; checkNotOverFlow(); } private void checkNotOverFlow() { if(amountOfWaterMl > capacityMl) { throw new BottleOverflowException(); } }
Я оставлю определение понятию BottleOverflowException в качестве упражнения для читателя.
теперь обратите внимание, насколько это надежнее. Теперь мы можем иметь дело с любым типом контейнера в нашем коде, принимая LiquidContainer вместо бутылки. И как эти бутылки справляются с такого рода вещами, все может отличаться. У вас могут быть бутылки, которые записывают свое состояние на диск при его изменении, или бутылки, которые экономят на базах данных SQL или GNU знает, что еще.
и все это может иметь различные способы обработки различные возгласы. Бутылка просто проверяет, и если она переполнена, она выбрасывает исключение RuntimeException. Но это может быть неправильно. (Есть полезная дискуссия об обработке ошибок, но я держу ее очень простой здесь специально. Люди в комментариях, скорее всего, укажут на недостатки этого упрощенного подхода. ;))
и да, кажется, что мы идем от очень простой идеи, чтобы получить гораздо лучшие ответы быстро.
есть и третья вещь, которой нет все обратились: геттеры и сеттеры используют вызовы методов. Это значит, что они выглядят как обычные методы везде делает. Вместо того, чтобы иметь странный специфический синтаксис для DTOs и прочее, у вас есть то же самое везде.
один из основных принципов ООП: инкапсуляция!
Это дает вам много преимуществ, одним из которых является то, что вы можете изменить реализацию геттера/сеттера за кулисами, но любой потребитель этого значения будет продолжать работать до тех пор, пока тип данных остается прежним.
с точки зрения объектно-ориентированного проектирования обе альтернативы могут нанести ущерб поддержанию кода путем ослабления инкапсуляции классов. Для обсуждения вы можете заглянуть в эту прекрасную статью:http://typicalprogrammer.com/?p=23
вы должны использовать геттеры и сеттеры, когда:
- Вы имеете дело с чем-то концептуально атрибут, но:
- ваш язык не имеет свойств (или какой-то подобный механизм, например, трассировки переменных Tcl), или
- поддержка свойств вашего языка недостаточно для этого варианта использования, или
- идиоматические соглашения вашего языка (или иногда вашей структуры) поощряют геттеров или сеттеров для этого использования случай.
так что это очень редко общий вопрос OO; это вопрос, специфичный для языка, с разными ответами для разных языков (и разных случаев использования).
С точки зрения теории ОО, геттеры и сеттеры бесполезны. Интерфейс вашего класса - это то, что он делает, а не то, что его состояние. (Если нет, то вы написали не тот класс.) В очень простых случаях, когда то, что делает класс, просто, например, представляет точку в прямоугольные координаты, * атрибуты являются частью интерфейса; геттеры и сеттеры просто облачают это. Но в любом случае, кроме очень простых случаев, ни атрибуты, ни геттеры и сеттеры не являются частью интерфейса.
другими словами: если вы считаете, что потребители вашего класса даже не должны знать, что у вас есть
spam
атрибут, а тем более быть в состоянии изменить его волей-неволей, то давая имset_spam
способ последнее, что вы хотите сделать.* даже для этого простого класса вы можете не обязательно разрешить установку
x
иy
значения. Если это действительно класс, разве он не должен иметь такие методы, какtranslate
,rotate
и т. д.? Если это только класс, потому что ваш язык не имеет записей/структур/именованных кортежей, то это на самом деле не вопрос ОО...
но никто никогда не делает общий дизайн ОО. Они занимаются проектированием и реализацией на определенном языке. И на некоторых языках, геттеры и сеттеры далеко не бесполезны.
если ваш язык не имеет свойств, то единственный способ представить что-то, что концептуально является атрибутом, но на самом деле вычисляется или проверяется и т. д., через геттеров и сеттеров.
даже если ваш язык имеет свойства, могут быть случаи, когда они недостаточны или неуместны. Например, если вы хотите разрешить подклассам управлять семантикой атрибута в языках без динамического доступа, a подкласс не может заменить вычисленное свойство для атрибута.
что касается "Что делать, если я хочу изменить свою реализацию спустя?"вопрос (который повторяется несколько раз в разных формулировках как в вопросе OP, так и в принятом ответе): если это действительно чистое изменение реализации, и вы начали с атрибута, вы можете изменить его на свойство, не затрагивая интерфейс. Если, конечно, ваш язык не поддерживается. Так что это действительно тот же самый случай снова.
кроме того, важно следить за идиомами языка (или фреймворка), который вы используете. Если вы пишете красивый код в стиле Ruby на C#, любой опытный разработчик C#, кроме вас, будет иметь проблемы с его чтением, и это плохо. Некоторые языки имеют более сильные культуры вокруг своих конвенций, чем другие.-и это не может быть совпадением, что Java и Python, которые находятся на противоположных концах спектра, как идиоматические добытчиками, есть два сильнейших культуры.
помимо человеческих читателей, будут библиотеки и инструменты, которые ожидают, что вы будете следовать соглашениям и сделаете свою жизнь сложнее, если вы этого не сделаете. подключение виджетов Interface Builder к чему-либо, кроме свойств ObjC, или использование определенных библиотек Java mocking без геттеров, просто усложняет вашу жизнь. Если инструменты важны для вас, не боритесь с ними.
методы Getter и setter являются методами доступа, что означает, что они обычно являются открытым интерфейсом для изменения частных членов класса. Для определения свойства используются методы getter и setter. Вы получаете доступ к методам getter и setter как к свойствам вне класса, даже если вы определяете их внутри класса как методы. Эти свойства вне класса могут иметь другое имя, отличное от имени свойства в классе.
есть некоторые преимущества в использовании геттер и сеттер методы, такие как возможность создавать элементы со сложной функциональностью, к которым можно получить доступ, например свойства. Они также позволяют создавать свойства только для чтения и только для записи.
несмотря на то, что методы getter и setter полезны, вы должны быть осторожны, чтобы не злоупотреблять ими, потому что, среди прочего, они могут затруднить обслуживание кода в определенных ситуациях. Кроме того, они предоставляют доступ к реализации вашего класса, например к открытым членам. ООП практика обескураживает прямой доступ к свойствам внутри класса.
когда вы пишете классы, вам всегда рекомендуется сделать как можно больше ваших переменных экземпляра частными и добавить методы getter и setter соответственно. Это потому, что есть несколько раз, когда вы не хотите позволить пользователям изменять определенные переменные внутри класса. Например, если у вас есть частный статический метод, который отслеживает количество экземпляров, созданных для определенного класса, вы не хотите, чтобы пользователь изменял это счетчик с использованием кода. Только оператор конструктора должен увеличивать эту переменную всякий раз, когда она вызывается. В этом случае можно создать частный экземпляр переменной и разрешить метод getter только для переменной counter, что означает, что пользователи могут получить текущее значение только с помощью метода getter, и они не смогут установить новые значения с помощью метода setter. Создание геттера без сеттера-это простой способ сделать определенные переменные в вашем классе доступными только для чтения.
код эволюционирует.
private
это здорово, когда вам нужна защита элемента данных. В конечном итоге все классы должны быть своего рода "минипрограммами", которые имеют четко определенный интерфейс что вы не можете просто винт с внутренностями.что сказал:разработка программного обеспечения не о том, чтобы установить эту окончательную версию класса, как будто вы нажимаете на какую-то чугунную статую с первой попытки. В то время как вы работаете с код больше похож на глину. он эволюционирует как вы разрабатываете его и узнать больше о проблемной области, которую вы решаете. Во время разработки классы могут взаимодействовать друг с другом больше, чем должны (зависимость, которую вы планируете разложить), объединяться или разделяться. Поэтому я думаю, что дебаты сводятся к тому, что люди не хотят религиозно писать
int getVar() const { return var ; }
так что у вас есть:
doSomething( obj->getVar() ) ;
вместо
doSomething( obj->var ) ;
не только
getVar()
визуально шумно, это дает такую иллюзию, чтоgettingVar()
это как-то более сложный процесс, чем это есть на самом деле. Как вы (как классный писатель) относитесь к святостиvar
особенно смущает пользователя вашего класса, если у него есть задатчик passthru - тогда похоже, что вы ставите эти ворота, чтобы" защитить " то, что вы настаиваете, ценно (святостьvar
) но все же даже вы уступаетеvar
's защита не стоит много по способности для любого, чтобы просто войти иset
var
to какую бы ценность они ни хотели, даже не глядя на то, что они делают.поэтому я программирую следующим образом (предполагая "гибкий" подход типа-т. е. когда я пишу код, не зная ровно что он будет делать/у вас нет времени или опыта для планирования сложного набора интерфейсов в стиле водопада):
1) Начните со всех открытых членов для основных объектов с данными и поведением. Вот почему во всех моих c++ "пример" кода Вы заметите, что я использую из
class
везде.2), когда внутреннее поведение объекта для элемента данных становится достаточно сложными (например, он любит держать внутренний
std::list
в некотором порядке), функции типа доступа записываются. Поскольку я программирую сам, я не всегда устанавливаю членprivate
сразу же, но где-то вниз по эволюции класса член будет "повышен" до любогоprotected
илиprivate
.3) классы, которые полностью конкретизированы и есть строгие правила о своих внутренних органах (т. е. они точно знать, что они делают, и вы не должны "ебать" (технический термин) с его внутренностями) даны
class
обозначение, частные члены по умолчанию, и только некоторые избранные члены могут бытьpublic
.Я нахожу, что этот подход позволяет мне избегать сидеть там и религиозно писать геттер/сеттеры, когда много членов данных мигрируют, перемещаются и т. д. на ранних стадиях эволюция класса.
есть веская причина рассмотреть возможность использования методов доступа - нет наследования свойств. См. следующий пример:
public class TestPropertyOverride { public static class A { public int i = 0; public void add() { i++; } public int getI() { return i; } } public static class B extends A { public int i = 2; @Override public void add() { i = i + 2; } @Override public int getI() { return i; } } public static void main(String[] args) { A a = new B(); System.out.println(a.i); a.add(); System.out.println(a.i); System.out.println(a.getI()); } }
выход:
0 0 4
еще одно использование (в языках, поддерживающих свойства) заключается в том, что сеттеры и геттеры могут подразумевать, что операция нетривиальна. Как правило, вы хотите, чтобы избежать делать то, что невыгодно в собственность.
геттеры и сеттеры используются для реализации двух основных аспектов объектно-ориентированного программирования, которые являются:
- абстрагирование
- инкапсуляция
предположим, что у нас есть класс Employee:
package com.highmark.productConfig.types; public class Employee { private String firstName; private String middleName; private String lastName; public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getMiddleName() { return middleName; } public void setMiddleName(String middleName) { this.middleName = middleName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public String getFullName(){ return this.getFirstName() + this.getMiddleName() + this.getLastName(); } }
здесь детали реализации полного имени скрыты от пользователя и не доступны непосредственно пользователю, в отличие от общедоступного атрибута.
одним из относительно современных преимуществ геттеров / сеттеров является то, что он упрощает просмотр кода в редакторах помеченных (индексированных) кодов. Например, если вы хотите увидеть, кто устанавливает элемент, вы можете открыть иерархию вызовов сеттера.
с другой стороны, если элемент является общедоступным, инструменты не позволяют фильтровать доступ для чтения/записи к элементу. Так что вы должны тащиться, хотя все использует члена.
в объектно-ориентированном языке методы и их модификаторы доступа объявляют интерфейс для этого объекта. Между конструктором и методами доступа и мутатора разработчик может управлять доступом к внутреннему состоянию объекта. Если переменные просто объявлены общедоступными, то нет никакого способа регулировать этот доступ. И когда мы используем сеттеры, мы можем ограничить пользователя для ввода, который нам нужен. Означает, что подача для этой самой переменной будет проходить через a правильный канал и канал предопределены нами. Так что безопаснее использовать сеттеры.
кроме того, это "будущее" вашего класса. В частности, переход от поля к свойству-это разрыв ABI, поэтому, если вы позже решите, что вам нужно больше логики, чем просто "установить/получить поле", тогда вам нужно сломать ABI, что, конечно же, создает проблемы для всего остального, уже скомпилированного против вашего класса.
Я просто хотел бы бросить идею аннотации: @getter и @setter. С помощью @getter вы должны иметь возможность obj = class.поле, но не класс.поле = кадриров. С @setter, наоборот. С @геттер и сеттер, вы должны быть в состоянии сделать оба. Это позволит сохранить инкапсуляцию и сократить время, не вызывая тривиальные методы во время выполнения.