Метод мутатора
В информатике — метод мутатора это метод, используемый для управления изменениями переменной. Они также широко известны как установки методы . Часто установщик сопровождается геттером , который возвращает значение частной переменной-члена. Они также известны под общим названием аксессоры .
Метод мутатора чаще всего используется в объектно-ориентированном программировании с соблюдением принципа инкапсуляции . Согласно этому принципу, переменные- члены класса проверяет делаются закрытыми, чтобы скрыть и защитить их от другого кода, и могут быть изменены только с помощью общедоступной функции-члена (метода мутатора), которая принимает желаемое новое значение в качестве параметра и при необходимости его и изменяет закрытую переменную-член . Методы-мутаторы можно сравнить с присваивания перегрузкой оператора , но они обычно появляются на разных уровнях иерархии объектов.
Методы-мутаторы также могут использоваться в необъектно-ориентированных средах. В этом случае ссылка на изменяемую переменную передается мутатору вместе с новым значением. В этом сценарии компилятор не может запретить коду обходить метод мутатора и напрямую изменять переменную. Ответственность ложится на разработчиков за то, чтобы переменная изменялась только с помощью метода мутатора, а не напрямую, .
В языках программирования, которые их поддерживают, свойства предлагают удобную альтернативу, не отказываясь от полезности инкапсуляции.
В приведенных ниже примерах полностью реализованный метод-мутатор также может проверять входные данные или предпринимать дальнейшие действия, такие как запуск события .
Подразумеваемое
[ редактировать ]Альтернативой определению методов-мутаторов и средств доступа или блоков свойств является предоставление переменной экземпляра некоторой видимости, отличной от частной, и доступ к ней непосредственно извне объектов. Гораздо более тонкий контроль прав доступа можно определить с помощью мутаторов и аксессоров. Например, параметр можно сделать доступным только для чтения, просто определив метод доступа, а не мутатор. Видимость этих двух методов может быть разной; часто бывает полезно, чтобы метод доступа был общедоступным, в то время как мутатор оставался защищенным, частным для пакета или внутренним.
Блок , в котором определен мутатор, предоставляет возможность проверки или предварительной обработки входящих данных. Если весь внешний доступ гарантированно осуществляется через мутатор, то эти шаги невозможно обойти. Например, если дата представлена отдельным приватным year
, month
и day
переменные, то входящие даты можно разделить по setDate
мутатор, в то время как для согласованности доступ к одним и тем же частным переменным экземпляра осуществляется setYear
и setMonth
. Во всех случаях значения месяца за пределами 1–12 могут быть отклонены одним и тем же кодом.
Аксессоры, наоборот, позволяют синтезировать полезные представления данных из внутренних переменных, сохраняя при этом их структуру инкапсулированной и скрытой от внешних модулей. Денежный getAmount
аксессор может построить строку из числовой переменной с количеством десятичных знаков, определенным скрытым currency
параметр.
Современные языки программирования часто предлагают возможность генерировать шаблон для мутаторов и средств доступа в одной строке, как, например, в C#. public string Name { get; set; }
и Руби attr_accessor :name
. В этих случаях блоки кода для проверки, предварительной обработки или синтеза не создаются. Эти упрощенные методы доступа по-прежнему сохраняют преимущество инкапсуляции перед простыми общедоступными переменными экземпляра, но обычно по мере развития системы программное обеспечение поддерживается , а требования изменяются, а требования к данным становятся более сложными. Многие автоматические мутаторы и средства доступа со временем заменяются отдельными блоками кода. Преимущество автоматического создания их на первых этапах реализации заключается в том, что общедоступный интерфейс класса остается идентичным независимо от того, добавляется ли большая сложность или нет, и в этом случае не требуется обширного рефакторинга. [1]
Манипулирование параметрами, имеющими мутаторы и средства доступа, изнутри класса, где они определены, часто требует некоторого дополнительного размышления. На ранних этапах реализации, когда в этих блоках мало или вообще нет дополнительного кода, не имеет значения, осуществляется ли прямой доступ к переменной частного экземпляра или нет. валидации, перекрестной проверки , проверки целостности данных По мере добавления , предварительной обработки или других усовершенствований могут возникать незначительные ошибки , когда при некотором внутреннем доступе используется более новый код, а в других местах он обходит.
Функции доступа могут быть менее эффективными, чем прямая выборка или сохранение полей данных, из-за дополнительных шагов, [2] однако такие функции часто являются встроенными , что устраняет накладные расходы на вызов функции.
Примеры
[ редактировать ]Сборка
[ редактировать ] студента, структура возраст дд ? студент заканчивает
.code Student_get_age процедуры объект : DWORD mov ebx , object mov eax , Student.age [ ebx ] ret Student_get_age endp Student_set_age процедуры объект : DWORD , age : DWORD mov ebx , object mov eax , age mov Student.age [ ebx ], eax ret Student_set_age конечная точка
С
[ редактировать ]В файле Student.h:
#ifndef _STUDENT_H #define _STUDENT_H struct Student ; /* непрозрачная структура */ typedef struct Student Student ; студент * Student_new ( int age , char * name ); void Student_delete ( студент * s ); void Student_set_age ( студент * s , int age ); int Student_get_age ( студент * s ); char * Student_get_name ( студент * s ); #endif
В файле Student.c:
#include <stdlib.h> #include <string.h> #include "student.h" struct Student { int age ; символ * имя ; }; студент * Student_new ( int age , char * name ) { Student * s = Malloc ( sizeof ( студент )); s -> name = strdup ( имя ); с -> возраст = возраст ; вернуть с ; } void Student_delete ( студент * s ) { свободно ( s -> имя ); бесплатно ( ы ); } void Student_set_age ( student * s , int age ) { s -> age = age ; } int Student_get_age ( студент * 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 , "Maurice" ); 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 ); студент_удалить ( ы ); вернуть 0 ; }
В файле Makefile:
все : выход . текст ; кот $< out.txt : основной ; ./$< > $@ главное : главное . о студент . o main.o Student.o : студент . ч чистый : ; $( РМ ) *. о вон . текстовый основной
С++
[ редактировать ]В файле Student.h:
#ifndef STUDENT_H #define STUDENT_H #include <string> class Student { public : Student ( const std :: string & name ); const std :: строка и имя () const ; void name ( const std :: string & name ); частный : std :: строка name_ ; }; #endif
В файле Student.cpp:
#include "Student.h" Student :: Student ( 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 ; } Установить { имя = значение ; } } }
В более поздних версиях C# (.NET Framework 3.5 и выше) этот пример можно сократить следующим образом, без объявления частной переменной name
.
публичный класс Student { public string Name { get ; набор ; } }
Использование сокращенного синтаксиса означает, что базовая переменная больше не доступна внутри класса. В результате set
часть имущества должна быть передана в распоряжение. Доступ можно ограничить с помощью set
-специфичный модификатор доступа.
публичный класс Student { public string Name { get ; частный набор ; } }
Общий Лисп
[ редактировать ]В Common Lisp Object System спецификации слотов в определениях классов могут указывать любой из :reader
, :writer
и :accessor
опции (даже несколько раз) для определения методов чтения, методов установки и методов доступа (метод чтения и соответствующий setf
метод). [3] Слоты всегда доступны напрямую через их имена с использованием with-slots
и slot-value
, а параметры доступа к слоту определяют специализированные методы, использующие slot-value
. [4]
Сам CLOS не имеет понятия свойств, хотя расширение протокола MetaObject определяет средства доступа к именам функций чтения и записи слота, включая те, которые генерируются с помощью :accessor
вариант. [5]
В следующем примере показано определение класса учащихся с использованием этих параметров слотов и прямого доступа к слотам:
( defclass Student () (( name :initarg :name :initform "" :accessor имя-ученика ) ; имя-ученика может быть установлено ( дата рождения :initarg :дата рождения :initform 0 :reader дата-рождения студента ) ( номер :initarg : число :initform 0 :читатель -номер-студента :писатель set-номер-студента ))) ;; Пример метода получения вычисляемого свойства (это просто метод) ( defmethod Student-age (( self Student )) ( - ( get-universal-time ) ( Student-birthdate self ))) ;; Пример прямого доступа к слотам в вычисляемом установщике свойств ( defmethod ( setf Student-age ) ( new-age self Student ) ) ( with-slots ( birthdate ) self ( setfbirthdate ( ( - ( get-universal-time ) new-age )) нью-эйдж )) ;; Параметры доступа к слотам генерируют методы, что позволяет определять дальнейшие методы ( defmethod set-student-number :before ( new-number ( self Student )) ;; Вы также можете проверить, существует ли уже студент с новым номером. ( check- введите новое число ( целое число 1 * )))
Д
[ редактировать ]D поддерживает синтаксис функций получения и установки. В версии 2 языка методы получения и установки класса/структуры должны иметь @property
атрибут. [6] [7]
класс Student { private char [] name_ ; // Получатель @property char [] name () { return this . имя_ ; } // Setter @property char [] name ( char [] name_in ) { return this . имя_ = имя_в ; } }
А Student
экземпляр можно использовать следующим образом:
автоматический студент = новый студент ; студент . имя = «Дэвид» ; // тот же эффект, что и Student.name("David") auto Student_name = Student . имя ; // тот же эффект, что и Student.name()
Дельфи
[ редактировать ]Это простой класс на языке Delphi, который иллюстрирует концепцию открытого свойства для доступа к частному полю.
интерфейса тип TStudent = класс strict Private FName : строка ; процедура SetName ( const Value : string ) ; public /// <summary> /// Получаем или устанавливаем имя студента. /// </summary> свойства имя : строки чтение FName запись SetName ; конец ; // ... реализации процедура TStudent . SetName ( константное значение : строка ) ; начать FName := Значение ; конец ; конец .
Ява
[ редактировать ]В этом примере простого класса, представляющего студента, в котором сохранено только имя, можно видеть, что переменной имя является частным, то есть видимым только из класса Student, а «установщик» и «получатель» являются общедоступными, а именно: getName()
" и " setName(name)
"методы.
публичный класс Student { частное строки имя ; public String getName () { возвращаемое имя ; } Public void setName ( String newName ) { name = newName ; } }
JavaScript
[ редактировать ]В этом примере функция-конструктор Student
используется для создания объектов, представляющих учащегося, сохраняя только имя.
функция Студент ( имя ) { var _name = имя ; этот . getName = функция () { return _name ; }; этот . setName = функция ( значение ) { _name = значение ; }; }
Или (используя устаревший способ определения средств доступа в веб-браузерах): [8]
функция Студент ( имя ) { var _name = имя ; этот . __defineGetter__ ( 'name' , function () { return _name ; }); этот . __defineSetter__ ( 'имя' , функция ( значение ) { _name = значение ; }); }
Или (используя прототипы для наследования и синтаксис средств доступа ES6 ):
функция Студент ( имя ) { это . _имя = имя ; } Студент . прототип = { получить имя () { вернуть это . _имя ; }, установите имя ( значение ) { this . _имя = значение ; } };
Или (без использования прототипов):
var Student = { получить имя () { вернуть это . _имя ; }, установите имя ( значение ) { this . _имя = значение ; } };
Или (используя defineProperty):
функция Студент ( имя ) { это . _имя = имя ; } Объект . defineProperty ( Student.prototype set { , 'name' , { get : function () { return this . _name ; }, : function ( value ) ) this . _name = value ; } } ;
ЭкшнСкрипт 3.0
[ редактировать ]пакет { общественный класс Student { частное var _name : String ; публичная функция get name () : String { return _name ; } общедоступных функций набора Имя ( значение : String ) : void { _name = value ; } } }
Цель-C
[ редактировать ]Использование традиционного синтаксиса Objective-C 1.0 с ручным подсчетом ссылок, как в GNUstep в Ubuntu 12.04 :
@interface Студент : NSObject { NSString * _name ; } - ( NSString * ) имя ; - ( void ) setName: ( NSString * ) name ; @end @implementation Student - ( NSString * ) name { return _name ; } - ( void ) setName: ( NSString * ) name { [ _name Release ]; _name = [ имя сохраняется ]; } @конец
Используя новый синтаксис Objective-C 2.0, используемый в Mac OS X 10.6 , iOS 4 и Xcode 3.2, генерируем тот же код, что описано выше:
@interface Student : NSObject @property ( неатомарный , сохранить ) NSString * name ; @end @implementation Студент @synthesize name = _name ; @конец
А начиная с OS X 10.8 и iOS 6 , при использовании Xcode 4.4 и выше синтаксис можно даже упростить:
@interface Student : NSObject @property ( неатомарный , сильный ) NSString * name ; @end @implementation Студент //Здесь ничего не происходит, и все в порядке. @конец
Перл
[ редактировать ]пакет «Студент» ; суб новый { благослови {}, сдвиг ; } Sub set_name { мой $self = сдвиг ; $self -> { имя } = $_ [ 0 ]; } Sub get_name { my $self = сдвиг ; вернуть $self -> { имя }; } 1 ;
Или, используя Class::Accessor
пакет «Студент» ; используйте базу qw(Class::Accessor) ; __PACKAGE__ -> Follow_best_practice ; Студент -> mk_accessors ( qw(имя) ); 1 ;
Или, используя объектную систему Moose :
пакет «Студент» ; используйте Муса ; # Moose использует имя атрибута в качестве установщика и получателя, свойства чтения и записи # позволяют нам переопределить это и предоставить наши собственные имена, в этом случае get_name и set_name имеют 'name' => ( is => 'rw' , isa => 'Str' , читатель => 'get_name' , писатель => 'set_name' ); 1 ;
PHP
[ редактировать ]PHP определяет «волшебные методы» __get
и __set
для свойств объектов. [9]
В этом примере простого класса, представляющего студента, в котором сохранено только имя, можно видеть, что переменной имя является частным, то есть видимым только из класса Student, а «установщик» и «получатель» являются общедоступными, а именно getName()
и setName('name')
методы.
класс Student { частная строка $name ; /** * @return string Имя. */ public function getName () : string { return $this -> name ; } /** * @param string $newName Имя, которое нужно установить. */ публичная функция setName ( строка $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 . сеттера определения имя ( self , new_name ): self . _имя = новое_имя
>>> боб = Студент ( «Боб» ) >>> боб . имя Боб >>> Боб . name = "Алиса" >>> Боб . имя Алиса >>> Боб . _name = "Чарли" # обойти установщик >>> bob . _name # обойти геттер Чарли
Ракетка
[ редактировать ]В Racket объектная система — это способ организации кода, который поставляется в дополнение к модулям и модулям. Как и в остальном языке, объектная система имеет первоклассные значения, а лексическая область видимости используется для управления доступом к объектам и методам.
#lang Racket ( define Student% ( class object% ( init-поля имя ) ( define/public ( get-name ) name ) ( define/public ( set-name! new-name ) ( set! name new-name )) ( супер-новый ))) ( define s ( new Student% [ name "Alice" ])) ( send s get-name ) ; => "Алиса" ( отправить set -name! "Bob" ) ( отправить name get- ) ; => "Боб"
Определения структур — это альтернативный способ определения новых типов значений, при этом мутаторы присутствуют, когда это явно требуется:
#lang Racket ( struct Student ( name ) #:mutable ) ( define s ( студент "Алиса" )) ( set-student-name! s "Bob" ) ( student-name s ) ; => "Боб"
Руби
[ редактировать ]В Ruby могут быть определены отдельные методы доступа и мутаторы или конструкции метапрограммирования. attr_reader
или attr_accessor
может использоваться как для объявления частной переменной в классе, так и для предоставления к ней публичного доступа только для чтения или для чтения и записи соответственно.
Определение отдельных методов доступа и мутаторов создает пространство для предварительной обработки или проверки данных.
class Student def name @name end def name= ( значение ) @name = value end end
Простой публичный доступ только для чтения к подразумеваемым @name
переменная
класс Student attr_reader : конец имени
Простой публичный доступ для чтения и записи к подразумеваемым @name
переменная
класс Student attr_accessor : конец имени
Ржавчина
[ редактировать ]struct Student { name : String , } impl Student { fn name ( & self ) -> & String { & self . name } fn name_mut ( & mut self ) -> & mut String { & mut self . имя } }
Смолток
[ редактировать ] age: aNumber "Установить возраст получателя как aNumber, если он больше 0 и меньше 150 " ( Число между: 0 и: 150 ) ifTrue: [ возраст := число ]
Быстрый
[ редактировать ]class Student { частное var _name : String = "" var name : String { get { return self . _name } set { self . _name = новоезначение } } }
Визуальный Бейсик .NET
[ редактировать ]Этот пример иллюстрирует идею VB.NET о свойствах, которые используются в классах. Как и в C#, здесь явно используется Get
и Set
методы.
Открытый класс Student Частное _name As String открытого свойства Имя () Get Return _name End Get Set ( ByVal значение ) _name = значение End Set End Property End Class
В VB.NET 2010 автоматически реализуемые свойства можно использовать для создания свойства без использования синтаксиса Get и Set. Обратите внимание, что компилятор создает скрытую переменную, называемую _name
, чтобы соответствовать собственности name
. Использование другой переменной внутри класса с именем _name
приведет к ошибке. Привилегированный доступ к базовой переменной доступен изнутри класса.
Публичный класс Student публичного свойства Имя как строки конца класс
См. также
[ редактировать ]Ссылки
[ редактировать ]- ^ Стивен Фукуа (2009). «Автоматические свойства в C# 3.0» . Архивировано из оригинала 13 мая 2011 г. Проверено 19 октября 2009 г.
- ^ Тим Ли (13 июля 1998 г.). «Эффективность выполнения функций доступа» .
- ^ «CLHS: Макрос ДЕФКЛАСС» . Проверено 29 марта 2011 г.
- ^ «CLHS: 7.5.2 Доступ к слотам» . Проверено 29 марта 2011 г.
- ^ «MOP: Определения слотов» . Проверено 29 марта 2011 г.
- ^ «Функции — язык программирования D» . Проверено 13 января 2013 г.
- ^ «Стиль Д» . Проверено 1 февраля 2013 г.
- ^ «Object.prototype.__defineGetter__() — JavaScript | MDN» . http://developer.mozilla.org . Проверено 6 июля 2021 г.
- ^ «PHP: Перегрузка — Руководство» . www.php.net . Проверено 6 июля 2021 г.