В информатике метод мутатора — это метод, используемый для управления изменениями переменной. Они также широко известны как методы сеттера . Часто сеттер сопровождается геттером , который возвращает значение закрытой переменной-члена. Они также известны вместе как аксессоры .
Метод мутатора чаще всего используется в объектно-ориентированном программировании в соответствии с принципом инкапсуляции . Согласно этому принципу, переменные -члены класса делаются закрытыми, чтобы скрыть и защитить их от другого кода, и могут быть изменены только открытой функцией-членом (методом мутатора), которая принимает желаемое новое значение в качестве параметра, опционально проверяет его и изменяет закрытую переменную -член . Методы мутатора можно сравнить с перегрузкой оператора присваивания , но они обычно появляются на разных уровнях иерархии объектов.
Методы мутаторов также могут использоваться в необъектно-ориентированных средах. В этом случае ссылка на изменяемую переменную передается мутатору вместе с новым значением. В этом сценарии компилятор не может запретить коду обходить метод мутатора и изменять переменную напрямую. Ответственность за то, чтобы переменная изменялась только через метод мутатора, а не напрямую, лежит на разработчиках .
В языках программирования, которые их поддерживают, свойства предлагают удобную альтернативу, не отказываясь от полезности инкапсуляции.
В приведенных ниже примерах полностью реализованный метод мутатора также может проверять входные данные или выполнять дальнейшие действия, например, инициировать событие .
Альтернативой определению методов мутатора и аксессора или блоков свойств является предоставление переменной экземпляра некоторой видимости, отличной от закрытой, и доступ к ней напрямую извне объектов. Гораздо более тонкий контроль прав доступа может быть определен с помощью мутаторов и аксессоров. Например, параметр может быть сделан доступным только для чтения, просто определив аксессор, но не мутатор. Видимость двух методов может быть разной; часто бывает полезно, чтобы аксессор был публичным, в то время как мутатор оставался защищенным, закрытым для пакета или внутренним.
Блок , в котором определен мутатор, предоставляет возможность для проверки или предварительной обработки входящих данных. Если весь внешний доступ гарантированно осуществляется через мутатор, то эти шаги нельзя обойти. Например, если дата представлена отдельными частными переменными year
, month
и , то входящие даты могут быть разделены мутатором, в то время как для согласованности к тем же частным переменным экземпляра обращаются и . Во всех случаях значения месяцев за пределами 1 - 12 могут быть отклонены тем же кодом.day
setDate
setYear
setMonth
Accessors, наоборот, позволяют синтезировать полезные представления данных из внутренних переменных, сохраняя их структуру инкапсулированной и скрытой от внешних модулей. Money getAmount
accessor может построить строку из числовой переменной с количеством десятичных знаков, определяемых скрытым currency
параметром.
Современные языки программирования часто предлагают возможность генерировать шаблон для мутаторов и аксессоров в одной строке — как, например, C# public string Name { get; set; }
и Ruby attr_accessor :name
. В этих случаях не создаются блоки кода для проверки, предварительной обработки или синтеза. Эти упрощенные аксессоры по-прежнему сохраняют преимущество инкапсуляции по сравнению с простыми публичными переменными экземпляра, но обычно по мере развития системного дизайна , поддержки программного обеспечения и изменения требований требования к данным становятся более сложными. Многие автоматические мутаторы и аксессоры в конечном итоге заменяются отдельными блоками кода. Преимущество их автоматического создания на ранних этапах реализации заключается в том, что публичный интерфейс класса остается идентичным независимо от того, добавляется ли большая сложность или нет, не требуя обширного рефакторинга, если он есть. [1]
Манипулирование параметрами, имеющими мутаторы и аксессоры внутри класса, где они определены, часто требует дополнительных размышлений. В первые дни реализации, когда в этих блоках мало или совсем нет дополнительного кода, не имеет значения, осуществляется ли прямой доступ к частной переменной экземпляра или нет. По мере добавления проверки, перекрестной проверки , проверок целостности данных , предварительной обработки или других усложнений могут появляться тонкие ошибки , когда некоторый внутренний доступ использует новый код, а в других местах он обходит его.
Функции доступа могут быть менее эффективными, чем непосредственное извлечение или сохранение полей данных, из-за дополнительных шагов [2], однако такие функции часто встраиваются , что устраняет накладные расходы на вызов функции.
структура студента возраст дд ? студент заканчивается
.code student_get_age proc object : DWORD mov ebx , object mov eax , student.age [ ebx ] ret student_get_age endp student_set_age proc object : DWORD , age : DWORD mov ebx , object mov eax , age mov student.age [ ebx ], eax ret student_set_age endp
В файле student.h:
#ifndef _СТУДЕНТ_H #определить _СТУДЕНТ_Hstruct student ; /* непрозрачная структура */ typedef struct student student ; student * student_new ( int age , char * name ); void student_delete ( student * s ); void student_set_age ( student * s , int age ); int student_get_age ( student * s ); char * student_get_name ( student * s ); #endif
В файле student.c:
#include <stdlib.h> #include <string.h> #include "student.h" struct student { int age ; char * name ; }; student * student_new ( int age , char * name ) { student * s = malloc ( sizeof ( student )); s -> name = strdup ( name ); s -> age = age ; return s ; } void student_delete ( student * s ) { free ( s -> name ); free ( s ); } void student_set_age ( student * s , int age ) { s -> age = age ; } int student_get_age ( student * s ) { return s -> age ; } char * student_get_name ( student * s ) { return s -> name ; }
В файле main.c:
#include <stdio.h> #include "student.h" int main ( void ) { student * s = student_new ( 19 , "Морис" ); char * name = student_get_name ( s ); int old_age = student_get_age ( s ); printf ( "Старый возраст%s = %i \n " , name , old_age ); student_set_age ( s , 21 ); int new_age = student_get_age ( s ); printf ( "Новый возраст%s = %i \n " , name , new_age ); student_delete ( s ); return 0 ; }
В файле Makefile:
все : out.txt ; cat $< out.txt : main ; ./$< > $@ main : main . o student . o main.o student.o : student . h очистить : ; $ ( RM ) *. o out . txt main
В файле Student.h:
#ifndef СТУДЕНТ_H #define СТУДЕНТ_H#include <строка> класс Студент { public : Студент ( const std :: string & name ); const std :: string & name () const ; void name ( const std :: string & name ); private : std :: string name_ ; }; #endif
В файле Student.cpp:
#include "Студент.h" Студент :: Студент ( const std :: string & name ) : name_ ( name ) { } const std :: string & Student :: name () const { return name_ ; } void Student :: name ( const std :: string & name ) { name_ = name ; }
Этот пример иллюстрирует идею C# о свойствах , которые являются особым типом члена класса . В отличие от Java, явные методы не определены; публичное «свойство» содержит логику для обработки действий. Обратите внимание на использование встроенной (необъявленной) переменной value
.
публичный класс Student { частная строка имя ; /// <summary> /// Получает или задает имя студента /// </summary> public string Name { get { return name ; } set { name = value ; } } }
В более поздних версиях C# (.NET Framework 3.5 и выше) этот пример можно сократить следующим образом, не объявляя закрытую переменную name
.
публичный класс Студент { публичная строка Имя { получить ; установить ; } }
Использование сокращенного синтаксиса означает, что базовая переменная больше недоступна изнутри класса. В результате часть set
свойства должна присутствовать для назначения. Доступ может быть ограничен с помощью set
модификатора доступа -specific.
публичный класс Student { публичная строка Имя { получить ; частный набор ; } }
В Common Lisp Object System спецификации слотов в определениях классов могут указывать любые из параметров :reader
, :writer
и :accessor
(даже несколько раз) для определения методов чтения, методов установки и методов доступа (метод чтения и соответствующий setf
метод). [3] Слоты всегда доступны напрямую через их имена с использованием with-slots
и slot-value
, а параметры доступа слотов определяют специализированные методы, которые используют slot-value
. [4]
Сам CLOS не имеет понятия о свойствах, хотя расширение MetaObject Protocol определяет средства доступа к именам функций чтения и записи слота, включая те, которые генерируются с помощью этой :accessor
опции. [5]
В следующем примере показано определение класса учащихся с использованием этих параметров слота и прямого доступа к слоту:
( defclass student () (( name :initarg :name :initform "" :accessor student-name ) ; student-name можно задать ( birthdate :initarg :birthdate :initform 0 :reader student-birthdate ) ( number :initarg :number :initform 0 :reader student-number :writer set-student-number ))) ;; Пример вычисляемого свойства getter (это просто метод) ( defmethod student-age (( self student )) ( - ( get-universal-time ) ( student-birthdate self ))) ;; Пример прямого доступа к слоту в сеттере вычисляемого свойства ( defmethod ( setf student-age ) ( new-age ( self student )) ( with-slots ( birthdate ) self ( setf birthdate ( - ( get-universal-time ) new-age )) new-age )) ;; Параметры доступа к слоту генерируют методы, тем самым позволяя определять дальнейшие методы ( defmethod set-student-number :before ( new-number ( self student )) ;; Вы также можете проверить, существует ли уже студент с новым номером. ( check-type new-number ( integer 1 * )))
D поддерживает синтаксис функций getter и setter. В версии 2 языка методы класса/структуры getter и setter должны иметь @property
атрибут. [6] [7]
class Student { private char [] name_ ; // Getter @property char [] name () { return this . name_ ; } // Setter @property char [] name ( char [] name_in ) { return this . name_ = name_in ; } }
Экземпляр Student
можно использовать следующим образом:
auto student = new Student ; student . name = "David" ; // тот же эффект, что и student.name("David") auto student_name = student . name ; // тот же эффект, что и student.name()
Это простой класс на языке Delphi, который иллюстрирует концепцию открытого свойства для доступа к закрытому полю.
интерфейсtype TStudent = class strict private FName : string ; procedure SetName ( const Value : string ) ; public /// <summary> /// Получить или задать имя студента. /// </summary> property Name : string read FName write SetName ; end ; // ...выполнениеprocedure TStudent.SetName ( const Value : string ) ; begin FName : = Value ; end ; конец .
В этом примере простого класса, представляющего студента, в котором хранится только его имя, можно увидеть, что переменная name является закрытой, т. е. видимой только из класса Student, а «setter» и «getter» являются открытыми, а именно методы « getName()
» и « setName(name)
».
публичный класс Student { закрытое имя строки ; public String getName () { return name ; } public void setName ( String newName ) { name = newName ; } }
В этом примере функция-конструктор Student
используется для создания объектов, представляющих студента, у которого сохранено только его имя.
функция Студент ( имя ) { var _name = имя ; это.getName = function ( ) { return _name ; } ; это.setName = функция ( значение ) { _name = значение ; } ; }
Или (используя устаревший способ определения средств доступа в веб-браузерах): [8]
функция Студент ( имя ) { var _name = имя ; это . __defineGetter__ ( 'имя' , функция () { return _name ; }); это . __defineSetter__ ( 'имя' , функция ( значение ) { _name = значение ; }); }
Или (используя прототипы для наследования и синтаксис методов доступа ES6 ):
функция Студент ( имя ){ это . _имя = имя ; } Студент.прототип = { получить имя ( ) { вернуть это._имя ; } , установить имя ( значение ) { это._имя = значение ; } } ;
Или (без использования прототипов):
var Student = { get name ( ) { return this._name ; } , set name ( value ) { this._name = value ; } } ;
Или (используя defineProperty):
function Student ( name ) { this._name = name ; } Object.defineProperty ( Student.prototype , ' name ' , { get : function ( ) { return this._name ; } , set : function ( value ) { this._name = value ; } } ) ;
пакет { public class Student { private var _name : String ; public function get name () : String { return _name ; } имя набора публичных функций ( значение : строка ) : void { _name = значение ; } } }
Используя традиционный синтаксис Objective-C 1.0 с ручным подсчетом ссылок, как это работает в GNUstep в Ubuntu 12.04 :
@interface Студент : NSObject { NSString * _name ; } - ( NSString * ) имя ; - ( void ) setName: ( NSString * ) имя ; @конец@реализация Студент- ( NSString * ) имя { return _name ; } - ( void ) setName: ( NSString * ) name { [ _name release ]; _name = [ name remain ]; } @конец
Используя более новый синтаксис Objective-C 2.0, используемый в Mac OS X 10.6 , iOS 4 и Xcode 3.2, генерируем тот же код, что описан выше:
@interface Студент : NSObject@property ( неатомарный , сохранить ) NSString * имя ; @конец@реализация Студент@synthesize имя = _имя ; @конец
А начиная с OS X 10.8 и iOS 6 , при использовании Xcode 4.4 и выше, синтаксис можно еще упростить:
@interface Студент : NSObject@property ( неатомарный , сильный ) NSString * имя ; @конец@реализация Студент//Сюда ничего не попадает, и это нормально.@конец
пакет Студенческий ; под новый { благослови {}, сдвиг ; } sub set_name { мой $self = сдвиг ; $self -> { имя } = $_ [ 0 ]; } sub get_name { мой $self = сдвиг ; вернуть $self -> { имя }; } 1 ;
Или, используя Class::Accessor
пакет Student ; использовать базовый qw(Class::Accessor) ; __PACKAGE__ -> follow_best_practice ; Студент -> mk_accessors ( qw(имя) );1 ;
Или, используя систему объектов Moose :
пакет Student ; использовать Moose ; # Moose использует имя атрибута в качестве сеттера и геттера, свойств чтения и записи # позволяет нам переопределить это и указать наши собственные имена, в этом случае get_name и set_name имеют 'name' => ( is => 'rw' , isa => 'Str' , reader => 'get_name' , writer => 'set_name' ); 1 ;
PHP определяет «магические методы» __get
и __set
свойства объектов. [9]
В этом примере простого класса, представляющего студента, в котором хранится только его имя, можно увидеть, что имя переменной является закрытым, т. е. оно видимо только из класса Student, а «сеттер» и «геттер» являются открытыми, а именно методы и .getName()
setName('name')
класс Student { частная строка $name ; /** * @return string Имя. */ public function getName () : string { return $this -> name ; } /** * @param string $newName Имя для установки. */ public function setName ( string $newName ) : void { $this -> name = $newName ; } }
В этом примере используется класс Python с одной переменной, геттером и сеттером.
class Student : # Инициализатор def __init__ ( self , name : str ) -> None : # Переменная экземпляра для хранения имени студента self . _name = name # Метод получения @property def name ( self ): return self . _name # Метод сеттера @ name.setter def name ( self , new_name ) : self._name = new_name
>>> bob = Student ( "Боб" ) >>> bob . name Боб >>> bob . name = "Алиса" >>> bob . name Алиса >>> bob . _name = "Чарли" # обойти сеттер >>> bob . _name # обойти геттер Чарли
В Racket система объектов — это способ организации кода, который идет в дополнение к модулям и модулям. Как и в остальной части языка, система объектов имеет значения первого класса, а лексическая область действия используется для управления доступом к объектам и методам.
#lang racket ( define student% ( class object% ( init-field name ) ( define/public ( get-name ) name ) ( define/public ( set-name! new-name ) ( set! name new-name )) ( super-new ))) ( define s ( new student% [ name "Alice" ])) ( send s get-name ) ; => "Alice" ( send s set-name! "Bob" ) ( send s get-name ) ; => "Bob"
Определения структур — это альтернативный способ определения новых типов значений, при этом мутаторы присутствуют, когда это явно требуется:
#lang racket ( struct student ( name ) #:mutable ) ( define s ( student "Алиса" )) ( set-student-name! s "Боб" ) ( student-name s ) ; => "Боб"
В Rubyattr_reader
могут быть определены отдельные методы доступа и мутатора, а также могут использоваться конструкции метапрограммирования attr_accessor
как для объявления закрытой переменной в классе, так и для предоставления открытого доступа к ней только для чтения или чтения и записи соответственно.
Определение отдельных методов доступа и мутатора создает пространство для предварительной обработки или проверки данных.
класс Студент def имя @имя конец def name= ( value ) @name = value end end
Простой публичный доступ только для чтения к подразумеваемой @name
переменной
класс Студент attr_reader :имя конец
Простой публичный доступ для чтения и записи к подразумеваемой @name
переменной
класс Студент attr_accessor :имя конец
структура Student { имя : Строка , } impl Student { fn name ( & self ) -> & String { & self . name } fn name_mut ( & mut self ) -> & mut String { & mut self . name } }
age: aNumber " Установить возраст получателя равным aNumber, если он больше 0 и меньше 150 " ( aNumber между: 0 и: 150 ) ifTrue: [ age := aNumber ]
класс Студент { частная переменная _имя : Строка = "" имя переменной : строка { получить { вернуть self._name } установить { self._name = newValue } } }
Этот пример иллюстрирует идею свойств VB.NET, которые используются в классах. Подобно C#, здесь есть явное использование методов Get
и Set
.
Студент государственного класса Личное _имя как строка Имя публичного свойства () Получить Возврат _name End Получить Установить ( ByVal значение ) _name = значение End Установить End Свойство Конец класса
В VB.NET 2010 свойства Auto Implemented могут использоваться для создания свойства без необходимости использования синтаксиса Get и Set. Обратите внимание, что компилятор создает скрытую переменную, называемую _name
, для соответствия Property name
. Использование другой переменной в классе с указанным именем _name
приведет к ошибке. Привилегированный доступ к базовой переменной возможен из класса.
Public Class Student Имя публичного свойства As String End Class