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

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

Как правило, мнения расходятся касательно того, хорошей ли практикой является использование публичных свойств в 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 Вот так!

Источник

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

Геттеры и сеттеры - 1Но сейчас я вынужден сообщить тебе неприятную правду. Мы создавали наши классы не совсем правильно!

Почему?

Никаких ошибок, на первый взгляд, в этом классе нет:

public class Cat {

public String name;
public int age;
public int weight;

public Cat(String name, int age, int weight) {
this.name = name;
this.age = age;
this.weight = weight;
}

public Cat() {
}

public void sayMeow() {
System.out.println(“Мяу!”);
}
}

На самом деле — есть. Представь себе, что сидя на работе ты написал вот такой класс Cat, обозначающий кошек. И ушел домой.

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

public class Main {

public static void main(String[] args) {

Cat cat = new Cat();
cat.name = “”;
cat.age = -1000;
cat.weight = 0;
}
}

Неважно, зачем он это сделал и как так получилось: может, устал человек или не выспался.

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

Какую же ошибку мы в итоге допустили?

При создании класса мы открыли его данные.

Поля name, age и weight находятся в публичном доступе. К ним можно обратиться в любом месте программы: достаточно просто создать объект Cat — и все, у любого программиста есть доступ к его данным напрямую через оператор “.”

Cat cat = new Cat();
cat.name = “”;

Здесь мы напрямую получаем доступ к полю name и устанавливаем для него значение.

Нам нужно как-то защитить наши данные от некорректного вмешательства извне.

Что же для этого нужно?

Во-первых, все переменные экземпляра (поля) необходимо помечать модификатором private. Private — самый строгий модификатор доступа в Java. Если ты его используешь, поля класса Cat не будут доступны за его пределами.

public class Cat {

private String name;
private int age;
private int weight;

public Cat(String name, int age, int weight) {
this.name = name;
this.age = age;
this.weight = weight;
}

public Cat() {
}

public void sayMeow() {
System.out.println(“Мяу!”);
}
}

public class Main {

Читайте также:  Какими химическими свойствами обладают следующие оксиды

public static void main(String[] args) {

Cat cat = new Cat();
cat.name = “”;
}
}

Компилятор это видит и сразу выдает ошибку.

Теперь поля вроде как защищены. Но получается, что доступ к ним закрыт “намертво”: в программе нельзя даже получить вес существующей кошки, если это понадобится. Это тоже не вариант: в таком виде наш класс практически нельзя использовать.

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

  • Другие программисты должны иметь возможность создавать объекты Cat
  • У них должна быть возможность считывать данные из уже существующих объектов (например, получить имя или возраст уже существующей кошки)
  • Также должна быть возможность присваивать значения полей. Но при этом — только корректные значения. От неправильных наши объекты должны быть защищены (никакого “возраст = -1000 лет” и тому подобного).

Список требований приличный! Но на самом деле, все это легко достигается с помощью специальных методов — геттеров и сеттеров.

Геттеры и сеттеры - 2

Название происходит от английского “get” — “получать” (т.е. “метод для получения значения поля”) и set — “устанавливать” (т.е. “метод для установки значения поля”).

Давай посмотрим, как они выглядят на примере нашего класса Cat:

public class Cat {

private String name;
private int age;
private int weight;

public Cat(String name, int age, int weight) {
this.name = name;
this.age = age;
this.weight = weight;
}

public Cat() {
}

public void sayMeow() {
System.out.println(“Мяу!”);
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

public int getWeight() {
return weight;
}

public void setWeight(int weight) {
this.weight = weight;
}
}

Как видишь, всё довольно просто 🙂 Их названия чаще всего состоят из слова get/set + названия поля, за которое они отвечают.

Например, метод getWeight() возвращает значение поля weight у того объекта, для которого он был вызван.

Вот как это выглядит в программе:

public class Main {

public static void main(String[] args) {

Cat barsik = new Cat(“Барсик”, 5, 4);
String barsikName = barsik.getName();
int barsikAge = barsik.getAge();
int barsikWeight = barsik.getWeight();

System.out.println(“Имя кота: ” + barsikName);
System.out.println(“Возраст кота: ” + barsikAge);
System.out.println(“Вес кота: ” + barsikWeight);
}
}

Вывод в консоль:

Имя кота: Барсик
Возраст кота: 5
Вес кота: 4

Теперь из другого класса (Main) есть доступ к полям Cat, но только через геттеры. Обрати внимание — у геттеров стоит модификатор доступа public, то есть они доступны из любой точки программы.

А как обстоят дела с присваиванием значений? За это отвечают методы-сеттеры

public void setName(String name) {
this.name = name;
}

Их работа, как видишь, тоже проста. Мы вызываем метод setName() у объекта Cat, передаем ему в качестве аргумента строку, и эта строка присваивается в поле name нашего объекта.

public class Main {

public static void main(String[] args) {

Cat barsik = new Cat(“Барсик”, 5, 4);

System.out.println(“Изначальное имя кота — ” + barsik.getName());
barsik.setName(“Василий”);
System.out.println(“Новое имя кота — ” + barsik.getName());
}
}

Здесь мы использовали и геттеры, и сеттеры. Вначале с помощью геттера мы получили и вывели в консоль первоначальное имя кота. Потом с помощью сеттера назначали его полю name новое значение — “Василий”. И потом с помощью геттера получили имя снова, чтобы проверить, действительно ли оно изменилось.

Вывод в консоль:

Изначальное имя кота — Барсик
Новое имя кота — Василий

Казалось бы, в чем разница? Мы также можем присваивать полям объекта некорректные значения, даже если у нас есть сеттеры:

public class Main {

public static void main(String[] args) {

Cat barsik = new Cat(“Барсик”, 5, 4);
barsik.setAge(-1000);

System.out.println(“Возраст Барсика — ” + barsik.getAge() + ” лет”);
}
}

Вывод в консоль:

Возраст Барсика — -1000 лет

Разница в том, что сеттер — это полноценный метод. А в метод, в отличие от поля, ты можешь заложить необходимую тебе логику проверки, чтобы не допустить неприемлемых значений. Например, можно легко не позволить назначение отрицательного числа в качестве возраста:

public void setAge(int age) {
if (age >= 0) {
this.age = age;
} else {
System.out.println(“Ошибка! Возраст не может быть отрицательным числом!”);
}
}

И теперь наш код работает корректно!

public class Main {

public static void main(String[] args) {

Cat barsik = new Cat(“Барсик”, 5, 4);
barsik.setAge(-1000);

System.out.println(“Возраст Барсика — ” + barsik.getAge() + ” лет”);
}
}

Вывод в консоль:

Ошибка! Возраст не может быть отрицательным числом!
Возраст Барсика — 5 лет

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

Представь себе ситуацию: ты и твои коллеги пишете программу вместе. Ты создал класс Cat с публичными полями, и все программисты пользуются ими, как хотят. И тут в один прекрасный день до тебя доходит: “Блин, а ведь рано или поздно кто-то может нечаянно присвоить отрицательное число в переменную weight! Надо создать сеттеры и сделать все поля приватными!”

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

Читайте также:  Какое свойство не относится к свойству систем

cat.name = “Бегемот”;

А теперь поля стали приватными и компилятор выдает кучу ошибок!

cat.name = “Бегемот”;

В такой ситуации лучше было бы скрыть поля и создать геттеры-сеттеры с самого начала. Все твои коллеги пользовались бы ими, и если бы тебя поздно “осенило”, что надо ограничить значения полей, ты бы просто дописал проверку внутри сеттера. И ни у кого не сломался бы уже написанный код.

Конечно, если ты хочешь, чтобы доступ к какому-то полю был только “на чтение”, можно создать для него один геттер.

“Снаружи”, то есть за пределами твоего класса, должны быть доступны только методы. Данные должны быть скрыты.

Геттеры и сеттеры - 4

Можно привести аналогию с мобильным телефоном. Представь, что вместо обычного включенного мобильника тебе дали телефон cо вскрытым корпусом, где все провода, схемы и т.д. торчат наружу. Телефон при этом работает: если сильно постараться и потыкаться в схемы, может даже получится совершить звонок. Но скорее ты его просто сломаешь.

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

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

Источник

Возвращает или задает значение, применяемое к свойству, определенному данным Setter.Gets or sets the value to apply to the property that is specified by this Setter.

public:
property System::Object ^ Value { System::Object ^ get(); void set(System::Object ^ value); };
[System.ComponentModel.TypeConverter(typeof(System.Windows.Markup.SetterTriggerConditionValueConverter))]
[System.Windows.Localizability(System.Windows.LocalizationCategory.None, Readability=System.Windows.Readability.Unreadable)]
[System.Windows.Markup.DependsOn(“Property”)]
[System.Windows.Markup.DependsOn(“TargetName”)]
public object Value { get; set; }
[System.Windows.Localizability(System.Windows.LocalizationCategory.None, Readability=System.Windows.Readability.Unreadable)]
[System.Windows.Markup.DependsOn(“Property”)]
[System.Windows.Markup.DependsOn(“TargetName”)]
public object Value { get; set; }
[<System.ComponentModel.TypeConverter(typeof(System.Windows.Markup.SetterTriggerConditionValueConverter))>]
[<System.Windows.Localizability(System.Windows.LocalizationCategory.None, Readability=System.Windows.Readability.Unreadable)>]
[<System.Windows.Markup.DependsOn(“Property”)>]
[<System.Windows.Markup.DependsOn(“TargetName”)>]
member this.Value : obj with get, set
[<System.Windows.Localizability(System.Windows.LocalizationCategory.None, Readability=System.Windows.Readability.Unreadable)>]
[<System.Windows.Markup.DependsOn(“Property”)>]
[<System.Windows.Markup.DependsOn(“TargetName”)>]
member this.Value : obj with get, set
Public Property Value As Object

Значение свойства

Object

Значение по умолчанию — UnsetValue.The default value is UnsetValue.

Атрибуты

Исключения

Примеры

В следующем примере определяется объект Style , который будет применен к каждому TextBlock элементу.The following example defines a Style that will be applied to every TextBlock element. Полный пример см. в разделе Общие сведения о примерах стилей и шаблонов.For the complete example, see Introduction to Styling and Templating Sample.

<Style TargetType=”{x:Type TextBlock}”>
<Setter Property=”FontFamily” Value=”Segoe Black” />
<Setter Property=”HorizontalAlignment” Value=”Center” />
<Setter Property=”FontSize” Value=”12pt” />
<Setter Property=”Foreground” Value=”#777777″ />
</Style>

В следующем примере определяется Style с помощью Setter s, ссылающегося на системные ресурсы.The following example defines a Style with Setters that reference system resources.

<Style x:Key=”SystemResStyle” TargetType=”{x:Type Button}”>
<Setter Property = “Background” Value=
“{DynamicResource {x:Static SystemColors.ControlLightBrushKey}}”/>
<Setter Property = “Foreground” Value=
“{DynamicResource {x:Static SystemColors.ControlDarkBrushKey}}”/>
<Setter Property = “FontSize” Value=
“{DynamicResource {x:Static SystemFonts.IconFontSizeKey}}”/>
<Setter Property = “FontWeight” Value=
“{DynamicResource {x:Static SystemFonts.MessageFontWeightKey}}”/>
<Setter Property = “FontFamily” Value=
“{DynamicResource {x:Static SystemFonts.CaptionFontFamilyKey}}”/>
</Style>

В следующем примере определяется Style для RepeatButton .The following example defines a Style for the RepeatButton.

<Style x:Key=”Slider_Thumb” TargetType=”{x:Type Thumb}”>
<Setter Property=”OverridesDefaultStyle” Value=”true” />
<Setter Property=”Width” Value=”14″ />
<Setter Property=”Height” Value=”14″ />
<Setter Property=”Template”>
<Setter.Value>
<ControlTemplate TargetType=”{x:Type Thumb}”>
<Grid Width=”14″ Height=”14″>
<Ellipse Fill=”{TemplateBinding Foreground}” />
<Ellipse Stroke=”{TemplateBinding BorderBrush}” StrokeThickness=”1″ x:Name=”ThumbCover” >
<Ellipse.Fill>
<LinearGradientBrush EndPoint=”0,1″ StartPoint=”0,0″>
<LinearGradientBrush.GradientStops>
<GradientStop Color=”#CCFFFFFF” Offset=”0″ />
<GradientStop Color=”#00000000″ Offset=”.5″ />
<GradientStop Color=”#66000000″ Offset=”1″ />
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
</Ellipse.Fill>
</Ellipse>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property=”IsDragging” Value=”true”>
<Setter TargetName=”ThumbCover” Property=”Fill”>
<Setter.Value>
<LinearGradientBrush EndPoint=”0,1″ StartPoint=”0,0″>
<LinearGradientBrush.GradientStops>
<GradientStop Color=”#CCFFFFFF” Offset=”1″ />
<GradientStop Color=”#00000000″ Offset=”.5″ />
<GradientStop Color=”#66000000″ Offset=”0″ />
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
</Setter.Value>
</Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

В следующем примере показано Value свойство, использующее Binding объявление.The following example shows a Value property that uses the Binding declaration. Полный пример см. в Примере проверки привязки.For the complete example, see Binding Validation Sample.

<Style x:Key=”textBoxInError” TargetType=”{x:Type TextBox}”>
<Style.Triggers>
<Trigger Property=”Validation.HasError” Value=”true”>
<Setter Property=”ToolTip”
Value=”{Binding RelativeSource={x:Static RelativeSource.Self},
Path=(Validation.Errors)/ErrorContent}”/>
</Trigger>
</Style.Triggers>
</Style>

Комментарии

Использование атрибута XAMLXAML Attribute Usage

<object Value=”value”/>

Использование элемента свойства XAMLXAML Property Element Usage

<object>
<object.Value>
value
</object.Value>
</object>

Значения XAMLXAML Values

valuevalue
Объект или расширение разметки.An object or a markup extension. См. раздел StaticResource Markup Extension.See StaticResource Markup Extension.

Обратите внимание, что необходимо указать Property оба Value Свойства и для, Setter или будет создано исключение.Note that you must specify both the Property and Value properties on a Setter or an exception will be thrown.

Привязка данных и динамические ресурсы в объекте поддерживаются, если указанное значение является Freezable объектом.Data binding and dynamic resources within the object is supported if the specified value is a Freezable object. Дополнительные сведения см DynamicResource Markup ExtensionSee Binding Markup Extension and DynamicResource Markup Extension.

Применяется к

См. также раздел

  • Property

Источник