Какую роль играет value в сеттерах свойств

Как правило, мнения расходятся касательно того, хорошей ли практикой является использование публичных свойств в PHP классах или всё же стоит использовать геттеры и сеттеры (и хранить свойства приватными или защищёнными). Ещё одно, компромиссное мнение, состоит в том, чтобы использовать магические методы __get() и__set().
У каждого из подходов существуют свои достоинства и недостатки, давайте взглянем на них…

Пример использования публичных свойств:

class Foo
{
public $bar;
}

$foo = new Foo();
$foo->bar = 1;
$foo->bar++;

В данном примере bar является публичным свойством класса Foo. При таком подходе мы можем манипулировать данным свойством как угодно и хранить в нём любые данные.

Достоинства публичных свойств

  • По сравнению с геттерами и сеттерами, нужно печатать на много меньше текста!
  • Код выглядит более читабельным и работать с ним проще, чем с вызовами геттеров и сеттеров.
  • Вызов публичного свойства (вместо set или get) работает быстрее и использует меньше памяти чем вызов метода, но выгода от этого будет почти незаметной до тех пор, пока вы не будете вызывать метод множество раз в длинном цикле.
  • Объекты с публичными свойствами могут использоваться в качестве параметров для некоторых PHP функций (таких как http_build_query).

Недостатки публичных свойств

  • Нет возможности осуществлять контроль за тем, какие именно данные содержатся в свойствах — их очень легко заполнить данными, некорректными для методов этого класса. Например, если класс будет использоваться сторонним разработчиком, отличным от автора класса, то могут возникать проблемы связанные с тем, что он может быть не в курсе (да он и не обязан) внутреннего устройства класса. В конце концов, ни для кого не секрет, что зачастую по прошествии несколько месяцев даже сам автор забывает логику написанного им кода.
  • Если вы хотите использовать публичные свойства в API, то этого нельзя будет сделать используя интерфейс, так как в PHP интерфейсы допускают только определения сигнатур методов.

Пример использования геттеров и сеттеров:

class Foo
{
private $bar;

public function getBar()
{
return $this->bar;
}

public function setBar($bar)
{
$this->bar = $bar;
}
}

$foo = new Foo();
$foo->setBar(1);
$foo->setBar($foo->getBar() + 1);

Свойство bar тут является приватным, в связи с этим, доступ к нему не может быть получен напрямую. Для того, чтобы получить значение свойства, вам придётся использовать метод getBar, или setBar для присвоения свойству значения. Чтобы вы могли быть уверенны в том, что входные данные полностью корректны, данные методы могут включать в себя соответствующий функционал для их валидации.

Достоинства геттеров и сеттеров

  • Используя геттеры и сеттеры вы можете осуществлять контроль за тем, какие именно данные содержатся в свойствах объекта, и отклонять любые некорректные значения.
  • Так же вы можете осуществлять дополнительные операции перед тем, как установить или получить значение свойства (например, если обновление данного свойства должно вызывать некоторое действие, такое как оповещение пользователя).
  • При установке значения, которое является объектом или массивом, вы можете явно указать тип переменной в сигнатуре функции(прим. public function setBar(Bar $bar)). К большому сожалению, PHP не позволяет проделывать тоже самое с типами int и string!
  • Если значение свойства должно получаться из внешнего источника или среды исполнения, вы можете использовать ленивую загрузку данных — таким образом ресурсы, требуемые для загрузки данных, будут задействованы непосредственно во время получения значения свойства. Разумеется, в данном случае нужно соблюдать осторожность, и не следует получать данные из внешнего источника при каждом обращении к свойству. Будет лучше сделать одно обращение к базе данных и заполнить значения всех свойств сразу, чем делать это для каждого в отдельности.
  • Вы можете сделать свойство доступным только на чтение или только на запись, путём создания только геттера или только сеттера.
  • Вы можете добавить геттеры и сеттеры в интерфейс для того, чтобы отобразить их в API.

Недостатки геттеров и сеттеров

  • Для разработчиков, которые используют прямой доступ к свойствам, геттеры и сеттеры кажутся настоящей головной болью! Для каждого свойства нужно определить само свойство, геттер и сеттер; и для того чтобы использовать данное свойство в коде, нужно осуществлять дополнительные вызовы метода — намного легче написать $foo->bar++; вместо $foo->setBar($foo->getBar() + 1); (хотя, конечно, можно добавить ещё один метод $foo->incrementBar();)
  • Как уже отмечалось выше, существуют небольшие дополнительные расходы, затрачиваемые на вызов метода.
  • Имена геттеров и сеттеров принято начинать с глаголов get и set, но данные глаголы так же могут использоваться и в других методах, которые ни коим образом не относятся к свойствам класса.

Пример использования магических геттеров и сеттеров:

class Foo
{
protected $bar;

public function __get($property)
{
switch ($property)
{
case ‘bar’:
return $this->bar;
//etc.
}
}

public function __set($property, $value)
{
switch ($property)
{
case ‘bar’:
$this->bar = $value;
break;
//etc.
}
}
}

$foo = new Foo();
$foo->bar = 1;
$foo->bar++;

В данном случае свойство bar не является публичным, однако в коде оно используется так, как если бы было публичным. Когда PHP не может найти соответствующего публичного свойства он вызывает соответствующий магический метод (__get() для получения значения, __set() для установки значения). Данный подход может показаться золотой серединой, но у него есть существенный недостаток (см. недостатки ниже!). Следует также отметить, что __get() и __set() методы НЕ вызываются для публичных свойств, и вызываются в случае, если свойство помечено как protected или private и находится за пределами области видимости, или если свойство не определено.

Достоинства магических геттеров и сеттеров

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

Недостатки магических геттеров и сеттеров

  • Недостатком магических методов является то, что они не делают свойство доступным(«видимым»). Для того чтобы использовать или расширить класс, вам необходимо «просто знать» какие свойства он содержит. В большинстве случаев это невозможно (если только вы не один из хардкорных программистов, которые думают что notepad является IDE!), хотя бывают случаи, когда упомянутые выше преимущества, перевешивают это ограничение. Как заметил один из комментаторов, этот недостаток может быть устранён использованием phpDoc тегов @property, @property-read, и @property-write. Круто.

Какой подход использовать
Очевидно, что у геттеров и сеттеров есть ряд существенных преимуществ, и некоторые люди считают, что их стоит использовать всё время (особенно те, у кого прошлое связано с Java!). Но на мой взгляд, этим они нарушают естественное развитие языка, и их излишняя сложность и детализация принуждают меня работать с этим, даже если в этом нет надобности (меня раздражает, когда обычные геттеры и сеттеры НЕ делают ничего, кроме получения и установки свойства). В таких случаях я стараюсь использовать публичные свойства, а геттеры и сеттеры я использую для критических свойств, которые необходимо более строго контролировать, или если в них необходимо использовать отложенную загрузку данных.

Другие альтернативы?
До изучения PHP я использовал C#. В C# все свойства имеют методы доступа, но вам не нужно вызывать их как методы, вы можете манипулировать свойсвами напрямую и соответствующие методы будут вызываться магически. Это в некотором роде это похоже на магические методы __get() и __set() в PHP, однако свойства остаются определены и доступны. Это золотая середина и было бы очень хорошо увидеть аналогичную возможность в PHP.

Печально, но, RFC необходимый для реализации C# подобного синтаксиса определения методов доступа к свойствам не набрал необходимых две трети голосов: wiki.php.net/rfc/propertygetsetsyntax-v1.2 Вот так!

Источник

Свойства

Последнее обновление: 03.10.2019

Кроме обычных методов в языке C# предусмотрены специальные методы доступа, которые называют свойства.
Они обеспечивают простой доступ к полям классов и структур, узнать их значение или выполнить их установку.

Стандартное описание свойства имеет следующий синтаксис:

[модификатор_доступа] возвращаемый_тип произвольное_название
{
// код свойства
}

Например:

class Person
{
private string name;

public string Name
{
get
{
return name;
}

set
{
name = value;
}
}
}

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

Через это свойство мы можем управлять доступом к переменной name. Стандартное определение свойства содержит блоки get
и set. В блоке get мы возвращаем значение поля, а в блоке set устанавливаем. Параметр value представляет передаваемое значение.

Мы можем использовать данное свойство следующим образом:

Person p = new Person();

// Устанавливаем свойство — срабатывает блок Set
// значение «Tom» и есть передаваемое в свойство value
p.Name = «Tom»;

// Получаем значение свойства и присваиваем его переменной — срабатывает блок Get
string personName = p.Name;

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

class Person
{
private int age;

public int Age
{
set
{
if (value < 18)
{
Console.WriteLine(«Возраст должен быть больше 17»);
}
else
{
age = value;
}
}
get { return age; }
}
}

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

Блоки set и get не обязательно одновременно должны присутствовать в свойстве. Если свойство определяют только блок get, то такое свойство доступно только для
чтеня — мы можем получить его значение, но не установить. И, наоборот, если свойство имеет только блок set, тогда это свойство доступно только для записи — можно
только установить значение, но нельзя получить:

class Person
{
private string name;
// свойство только для чтения
public string Name
{
get
{
return name;
}
}

private int age;
// свойство только для записи
public int Age
{
set
{
age = value;
}
}
}

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

Модификаторы доступа

Мы можем применять модификаторы доступа не только ко всему свойству, но и к отдельным блокам — либо get, либо set:

class Person
{
private string name;

public string Name
{
get
{
return name;
}

private set
{
name = value;
}
}
public Person(string name)
{
Name = name;
}
}

Теперь закрытый блок set мы сможем использовать только в данном классе — в его методах, свойствах, конструкторе, но никак не в другом классе:

Person p = new Person(«Tom»);

// Ошибка — set объявлен с модификатором private
//p.Name = «John»;

Console.WriteLine(p.Name);

При использовании модификаторов в свойствах следует учитывать ряд ограничений:

  • Модификатор для блока set или get можно установить, если свойство имеет оба блока (и set, и get)

  • Только один блок set или get может иметь модификатор доступа, но не оба сразу

  • Модификатор доступа блока set или get должен быть более ограничивающим, чем модификатор доступа свойства. Например,
    если свойство имеет модификатор public, то блок set/get может иметь только модификаторы protected internal, internal, protected, private

Автоматические свойства

Свойства управляют доступом к полям класса. Однако что, если у нас с десяток и более полей, то определять каждое поле и писать для него однотипное свойство
было бы утомительно. Поэтому в фреймворк .NET были добавлены автоматические свойства. Они имеют сокращенное объявление:

class Person
{
public string Name { get; set; }
public int Age { get; set; }

public Person(string name, int age)
{
Name = name;
Age = age;
}
}

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

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

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

Автосвойствам можно присвоить значения по умолчанию (инициализация автосвойств):

class Person
{
public string Name { get; set; } = «Tom»;
public int Age { get; set; } = 23;
}

class Program
{
static void Main(string[] args)
{
Person person = new Person();
Console.WriteLine(person.Name); // Tom
Console.WriteLine(person.Age); // 23

Console.Read();
}
}

И если мы не укажем для объекта Person значения свойств Name и Age, то будут действовать значения по умолчанию.

Стоит отметить, что в структурах мы не можем использовать инициализацию автосвойств.

Автосвойства также могут иметь модификаторы доступа:

class Person
{
public string Name { private set; get;}
public Person(string n)
{
Name = n;
}
}

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

class Person
{
public string Name { get;} = «Tom»
}

Сокращенная запись свойств

Как и методы, мы можем сокращать свойства. Например:

class Person
{
private string name;

// эквивалентно public string Name { get { return name; } }
public string Name => name;
}

Источник

Оператор set связывает свойство объекта с функцией, которая будет вызвана при попытке установить это свойство.

Синтаксис

{set prop(val) { . . . }}
{set [expression](val) { . . . }}

Параметры

prop
Имя свойства для привязки к заданной функции.
val
Псевдоним переменной, которая хранит значение, неудавшегося определения prop.
expression
Начиная с ECMAScript 6, вы также можете использовать выражения для вычисляемого имя свойства для привязки к данной функции.

Описание

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

Обратите внимание на следующие моменты при работе с синтаксисом set:

Сеттер может быть удален оператором delete.

Примеры

Определение сеттера при инициализации новых объектов

Это позволит определить псевдо-параметр current объекта o, который задает значение, обновляющее значение log:

var o = {
set current (str) {
this.log[this.log.length] = str;
},
log: []
}

обратите внимание, что current не определен и любые попытки доступа к нему вернут undefined.

Удаление сеттера оператором delete

Если вы хотите удалить сеттер, вы можете просто его удалить:

delete o.current;

Определение сеттера для существующих объектов используя defineProperty

Чтобы добавить сеттер на существующий объект в любое время, используйте Object.defineProperty().

var o = { a:0 };

Object.defineProperty(o, «b», { set: function (x) { this.a = x / 2; } });

o.b = 10; // Запускает сеттер, который присваивает 10 / 2 (5) свойству ‘a’
console.log(o.a) // 5

Использование вычисляемого имени свойства 

var expr = «foo»;

var obj = {
baz: «bar»,
set [expr](v) { this.baz = v; }
};

console.log(obj.baz); // «bar»
obj.foo = «baz»; // запускает сеттер
console.log(obj.baz); // «baz»

Спецификации

Совместимость с браузерами

Update compatibility data on GitHub

КомпьютерыМобильныеServer
ChromeEdgeFirefoxInternet ExplorerOperaSafariAndroid webviewChrome для AndroidFirefox для AndroidOpera для AndroidSafari on iOSSamsung InternetNode.js
setChrome
Полная поддержка

1

Edge
Полная поддержка

12

Firefox
Полная поддержка

1.5

IE
Полная поддержка

9

Opera
Полная поддержка

9.5

Safari
Полная поддержка

3

WebView Android
Полная поддержка

1

Chrome Android
Полная поддержка

18

Firefox Android
Полная поддержка

4

Opera Android
Полная поддержка

14

Safari iOS
Полная поддержка

1

Samsung Internet Android
Полная поддержка

1.0

nodejs
Полная поддержка

Да

Computed property namesChrome
Полная поддержка

46

Edge
Полная поддержка

12

Firefox
Полная поддержка

34

IE
Нет поддержки

Нет

Opera
Полная поддержка

47

Safari
Нет поддержки

Нет

WebView Android
Полная поддержка

46

Chrome Android
Полная поддержка

46

Firefox Android
Полная поддержка

34

Opera Android
Полная поддержка

33

Safari iOS
Нет поддержки

Нет

Samsung Internet Android
Полная поддержка

5.0

nodejs
Полная поддержка

Да

Легенда

Полная поддержка
 
Полная поддержка

Нет поддержки
 
Нет поддержки

Смотрите также

  • getter
  • delete
  • Object.defineProperty()
  • __defineGetter__
  • __defineSetter__
  • Defining Getters and Setters в реководстве по JavaScript

Источник

  Обновл. 2 Янв 2020  | 

В предыдущем уроке мы узнали, что переменные-члены класса по умолчанию являются закрытыми. Новички, которые изучают объектно-ориентированное программирование, очень часто не всегда понимают, почему всё именно так.

Зачем делать переменные-члены класса закрытыми?

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

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

Такое разделение интерфейса и реализации чрезвычайно полезно, поскольку оно позволяет использовать объекты, без необходимости понимания их реализации. Это значительно снижает сложность использования этих устройств и значительно увеличивает их количество (устройства с которыми можно взаимодействовать).

По аналогичным причинам разделение реализации и интерфейса полезно и в программировании.

Инкапсуляция

В объектно-ориентированном программировании инкапсуляция (или ещё «сокрытие информации») — это процесс скрытого хранения деталей реализации объекта. Пользователи обращаются к объекту через открытый интерфейс.

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

Преимущество №1: Инкапсулированные классы проще в использовании и уменьшают сложность ваших программ.

С полностью инкапсулированным классом вам нужно только знать, какие методы являются доступными для использования, какие аргументы они принимают и какие значения возвращают. Не нужно знать, как класс реализован внутри. Например, класс, содержащий список имён, может быть реализован с использованием динамического массива, строк C-style, std::array, std::vector, std::map, std::list или любой другой структуры данных. Для использования этого класса, вам не нужно знать детали его реализации. Это значительно снижает сложность ваших программ, а также уменьшает количество возможных ошибок. Это является ключевым преимуществом инкапсуляции.

Все классы стандартной библиотеки C++ инкапсулированы. Представьте, насколько сложнее был бы процесс изучения C++, если бы вам нужно было бы знать реализацию std::string, std::vector или std::cout и других объектов для того, чтобы их использовать!

Преимущество №2: Инкапсулированные классы помогают защитить ваши данные и предотвращают их неправильное использование.

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

class MyString

{

    char *m_string; // динамически выделяем строку

    int m_length; // используем переменную для отслеживания длины строки

};

Эти две переменные связаны: m_length всегда должен соответствовать длине строки, удерживаемой m_string. Если бы m_length был бы открытым, то любой мог бы изменить длину строки без изменения m_string (или наоборот). Это точно привело бы к проблемам. Делая как m_length, так и m_string закрытыми, пользователи вынуждены использовать методы для взаимодействия с классом.

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

class IntArray

{

public:

    int m_array[10];

};

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

int main()

{

    IntArray array;

    array.m_array[16] = 2; // некорректный индекс, вследствие чего перезаписываем память, которой мы не владеем

}

Однако, если мы сделаем массив закрытым, то сможем заставить пользователя использовать функцию, которая, первым делом, проверяет корректность индекса:

class IntArray

{

private:

    int m_array[10]; // пользователь не имеет прямого доступа к этому члену

public:

    void setValue(int index, int value)

    {

        // Если индекс недействителен, то не делаем ничего

        if (index < 0 || index >= 10)

            return;

        m_array[index] = value;

    }

};

Таким образом, мы защитим целостность нашей программы.

Примечание: Функция at() в std::array и std::vector делает что-то похожее!

Преимущество №3: Инкапсулированные классы легче изменить.

Рассмотрим следующий простой пример:

#include <iostream>

class Values

{

public:

    int m_number1;

    int m_number2;

    int m_number3;

};

int main()

{

    Values value;

    value.m_number1 = 7;

    std::cout << value.m_number1 << ‘n’;

}

Хотя эта программа работает нормально, но что произойдёт, если мы решим переименовать m_number1 или изменить тип этой переменной? Мы бы сломали не только эту программу, но и большую часть программ, которые используют класс Values!

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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

#include <iostream>

class Values

{

private:

    int m_number1;

    int m_number2;

    int m_number3;

public:

    void setNumber1(int number) { m_number1 = number; }

    int getNumber1() { return m_number1; }

};

int main()

{

    Values value;

    value.setNumber1(7);

    std::cout << value.getNumber1() << ‘n’;

}

Теперь давайте изменим реализацию класса:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

#include <iostream>

class Values

{

private:

    int m_number[3]; // здесь изменяем реализацию этого класса

public:

    // Нам нужно обновить переменные методов, чтобы заработала новая реализация

    void setNumber1(int number) { m_number[0] = number; }

    int getNumber1() { return m_number[0]; }

};

int main()

{

    // Но наша программа продолжает работать как прежде

    Values value;

    value.setNumber1(7);

    std::cout << value.getNumber1() << ‘n’;

}

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

Аналогично, если бы ночью гномы пробрались в ваш дом и заменили внутреннюю часть вашего пульта от телевизора на другую (совместимую) технологию, вы, вероятно, даже не заметили бы ничего!

Преимущество №4: С инкапсулированными классами легче проводить отладку.

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

Функции доступа (Геттеры и Сеттеры)

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

Функция доступа — это короткая открытая функция, задачей которой является получение или изменение значения закрытой переменной-члена класса. Например:

class MyString

{

private:

    char *m_string; // динамически выделяем строку

    int m_length; // используем переменную для отслеживания длины строки

public:

    int getLength() { return m_length; } // функция доступа для получения значения m_length

};

Здесь getLength() является функцией доступа, которая просто возвращает значение m_length.

Функции доступа обычно бывают двух типов:

   Геттеры — это функции, которые возвращают значения закрытых переменных-членов класса.

   Сеттеры — это функции, которые позволяют присваивать значения закрытым переменным-членам класса.

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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

class Date

{

private:

    int m_day;

    int m_month;

    int m_year;

public:

    int getDay() { return m_day; } // геттер для day

    void setDay(int day) { m_day = day; } // сеттер для day

    int getMonth() { return m_month; } // геттер для month

    void setMonth(int month) { m_month = month; } // сеттер для month

    int getYear() { return m_year; } // геттер для year

    void setYear(int year) { m_year = year; } // сеттер для year

};

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

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

Хотя иногда вы можете увидеть, что геттер возвращает неконстантную ссылку на переменную-член — этого следует избегать, так как в таком случае нарушается инкапсуляция, позволяя caller-у изменять внутреннее состояние класса вне этого же класса. Лучше, чтобы ваши геттеры использовали тип возврата по значению или по константной ссылке.

Правило: Геттеры должны использовать тип возврата по значению или по константной ссылке. Не используйте для геттеров тип возврата по неконстантной ссылке.

Заключение

Инкапсуляция имеет много преимуществ, основное из которых — это использовать класс без необходимости понимания его реализации. Инкапсулированные классы:

   проще в использовании;

   уменьшают сложность программы;

   защищают и предотвращают данные от неправильного использования;

   легче изменять;

   легче отлаживать.

Это значительно облегчает использование классов.

Оценить статью:

Загрузка…

Источник