В информатике , А метод мутатором является метод используется для контроля изменения в переменной. Они также широко известны как сеттерные методы. Часто сеттер сопровождается геттером (также известным как метод доступа ), который возвращает значение частной переменной-члена.
Метод мутатора чаще всего используется в объектно-ориентированном программировании в соответствии с принципом инкапсуляции . Согласно этому принципу, члены переменные из в классе сделаны частное , чтобы скрыть и защитить их от другого кода, и может быть изменен только с помощью функции общественного члена (метод мутатора), который принимает желаемое новое значение в качестве параметра, необязательно проверяет он и изменяет частную переменную-член . Методы мутатора можно сравнить с перегрузкой оператора присваивания, но обычно они появляются на разных уровнях иерархии объектов.
Методы мутатора также могут использоваться в не объектно-ориентированных средах. В этом случае ссылка на изменяемую переменную передается мутатору вместе с новым значением. В этом сценарии компилятор не может запретить коду обойти метод мутатора и напрямую изменить переменную. Ответственность ложится на разработчиков , чтобы обеспечить переменная изменяется только через метод мутаторный и не изменять непосредственно.
В языках программирования, которые их поддерживают, свойства предлагают удобную альтернативу без отказа от инкапсуляции.
В приведенных ниже примерах полностью реализованный метод мутатора также может проверять входные данные или предпринимать дальнейшие действия, такие как запуск события .
Подразумеваемое
Альтернативой определению методов мутатора и доступа или блоков свойств является предоставление переменной экземпляра некоторой видимости, отличной от частной, и доступ к ней напрямую извне объектов. Более точный контроль прав доступа можно определить с помощью мутаторов и средств доступа. Например, параметр можно сделать доступным только для чтения, просто определив метод доступа, но не мутатор. Видимость двух методов может быть разной; Часто бывает полезно, чтобы метод доступа был общедоступным, в то время как мутатор оставался защищенным, закрытым для пакета или внутренним.
Блок , где определен мутатор предоставляет возможность для проверки или предварительной обработки входящих данных. Если гарантировано, что весь внешний доступ будет осуществляться через мутатор, то эти шаги нельзя обойти. Например, если дата представлена отдельными частными year
, month
и day
переменные, то входящие даты могут быть разделены с помощью setDate
мутатора в то время как для последовательности тот же частный переменного экземпляр доступа setYear
и setMonth
. Во всех случаях значения месяцев за пределами 1–12 могут быть отклонены одним и тем же кодом.
Аксессоры, наоборот, позволяют синтезировать полезные представления данных из внутренних переменных, сохраняя при этом их структуру инкапсулированной и скрытой от внешних модулей. Денежный getAmount
метод доступа может построить строку из числовой переменной с количеством десятичных знаков, определенным скрытым currency
параметром.
Современные языки программирования часто предлагают возможность генерировать шаблон для мутаторов и средств доступа в одной строке - как, например, C # public string Name { get; set; }
и Ruby attr_accessor :name
. В этих случаях блоки кода не создаются для проверки, предварительной обработки или синтеза. Эти упрощенные средства доступа по-прежнему сохраняют преимущество инкапсуляции по сравнению с простыми общедоступными переменными экземпляра, но обычно по мере развития системного проектирования программное обеспечение поддерживается и требования меняются, требования к данным становятся более сложными. Многие автоматические мутаторы и аксессоры со временем заменяются отдельными блоками кода. Преимущество их автоматического создания на ранних этапах реализации заключается в том, что открытый интерфейс класса остается идентичным вне зависимости от того, добавляется ли большая изощренность, и не требует обширного рефакторинга, если это так. [1]
Манипулирование параметрами, имеющими мутаторы и методы доступа, внутри класса, в котором они определены, часто требует дополнительных размышлений. В первые дни реализации, когда в этих блоках мало или совсем нет дополнительного кода, не имеет значения, осуществляется прямой доступ к частной переменной экземпляра или нет. По мере добавления проверки, перекрестной проверки , проверки целостности данных, предварительной обработки или других усовершенствований могут появиться тонкие ошибки, когда при некотором внутреннем доступе используется более новый код, а в других местах он обходится.
Функции доступа могут быть менее эффективными, чем прямая выборка или сохранение полей данных из-за задействованных дополнительных шагов [2], однако такие функции часто встроены, что устраняет накладные расходы на вызов функции.
Примеры
сборка
студент структура возраст дд ? студент заканчивается
.code student_get_age объект proc : DWORD mov ebx , объект mov eax , student.age [ ebx ] ret student_get_age endp student_set_age объект proc : DWORD , возраст : DWORD mov ebx , объект mov eax , возраст mov student.age [ ebx ], eax ret student_set_age endp
C
В файле student.h:
#ifndef _STUDENT_H #define _STUDENT_Hструктурировать студента ; / * непрозрачная структура * / typedef struct student student ;ученик * ученик_новый ( int age , char * name ); void student_delete ( студент * s );void student_set_age ( ученик * s , int возраст ); int student_get_age ( студент * s ); char * student_get_name ( студент * s );#endif
В файле student.c:
#include #include #include "student.h"struct student { int age ; char * имя ; };ученик * ученик_новый ( int возраст , символ * имя ) { ученик * s = malloc ( sizeof ( ученик )); s -> name = strdup ( имя ); s -> age = возраст ; return s ; }void student_delete ( student * s ) { бесплатно ( s -> имя ); бесплатно ( ы ); }void student_set_age ( student * s , int age ) { s -> age = возраст ; }int student_get_age ( студент * s ) { return s -> возраст ; }char * student_get_name ( student * s ) { return s -> имя ; }
В файле main.c:
#include #include "student.h"int main ( void ) { студент * s = student_new ( 19 , "Морис" ); char * name = student_get_name ( а ); INT old_age = student_get_age ( ы ); printf ( "старость% s =% i \ n " , имя , old_age ); student_set_age ( s , 21 ); INT new_age = student_get_age ( ы ); printf ( "новый возраст% s =% i \ n " , имя , новый_ возраст ); student_delete ( s ); возврат 0 ; }
В файле Makefile:
все : из . txt ; cat $ < out.txt : main ; ./$ <> $ @ main : main . о студент . o main.o student.o : студент . ч чистый : ; $ ( RM ) *. о вне . txt main
C ++
В файле Student.h:
#ifndef STUDENT_H #define STUDENT_H#include <строка>класс Студент { общедоступный : Студент ( const std :: string & name ); const std :: string & name () const ; пустое имя ( const std :: string & name );частный : std :: string name_ ; };#endif
В файле Student.cpp:
#include "Student.h"Студент :: Студент ( const std :: string & name ) : name_ ( имя ) { }const std :: string & Student :: name () const { return name_ ; }void Student :: name ( const std :: string & name ) { имя_ = имя ; }
C #
Этот пример иллюстрирует представление C # о свойствах , которые представляют собой особый тип члена класса . В отличие от Java, явные методы не определены; публичное «свойство» содержит логику для обработки действий. Обратите внимание на использование встроенной (необъявленной) переменной value
.
публичный класс Student { частное строковое имя ; /// /// Получает или задает имя студента /// общедоступная строка Name { get { return name ; } установить { имя = значение ; } } }
В более поздних версиях C # (.NET Framework 3.5 и выше) этот пример может быть сокращен следующим образом, без объявления частной переменной name
.
открытый класс Студент { общедоступная строка Имя { получить ; набор ; } }
Использование сокращенного синтаксиса означает, что базовая переменная больше не доступна изнутри класса. В результате set
часть имущества должна быть предоставлена для передачи. Доступ можно ограничить с помощью специального set
модификатора доступа.
открытый класс Студент { общедоступная строка Имя { получить ; частный набор ; } }
Common Lisp
В объектной системе Common Lisp спецификации слотов в определениях классов могут указывать любые параметры :reader
, :writer
и :accessor
(даже несколько раз) для определения методов чтения, методов установки и методов доступа (метод чтения и соответствующий setf
метод). [3] Слоты всегда напрямую доступны через их имена с использованием with-slots
и slot-value
, а параметры доступа к слотам определяют специализированные методы, которые используют slot-value
. [4]
Сам CLOS не имеет понятия свойств, хотя расширение протокола MetaObject определяет средства доступа к именам функций чтения и записи слота, включая те, которые сгенерированы с помощью этой :accessor
опции. [5]
В следующем примере показано определение класса ученика с использованием этих параметров слота и прямого доступа к слоту:
( DEFCLASS студент () (( имя : initarg : Название : initform "" : сбруя студенты-имя ) ; имя студента является setf'able ( дата рождения : initarg : дата рождения : initform 0 : читатель студент-дата рождения ) ( номер : initarg : номер : initform 0 : читатель номер ученика : писатель набор номер ученика )));; Пример вычисленного получателя свойства (это просто метод) ( 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) )) нью-эйдж ));; Параметры доступа к слоту генерируют методы, что позволяет определять дополнительные методы ( defmethod set-student-number : before ( new-number ( self student )) ;; Вы также можете проверить, существует ли уже ученик с новым-номером. ( Check- введите новое число ( целое число 1 * )))
D
D поддерживает синтаксис функций получения и установки. В версии 2 методов класса / структуры языка getter и setter должен быть @property
атрибут. [6] [7]
class Student { private char [] name_ ; // Получение @property char [] name () { возвращает это . name_ ; } // Установить @property char [] name ( char [] name_in ) { вернуть это . name_ = name_in ; } }
Student
Экземпляр может быть использован , как это:
авто студент = новый студент ; студент . name = "Дэвид" ; // тот же эффект, что и student.name ("David") auto student_name = student . имя ; // тот же эффект, что и student.name ()
Delphi
Это простой класс на языке Delphi, который иллюстрирует концепцию общедоступного свойства для доступа к частному полю.
интерфейстип TStudent = class strict private FName : string ; Процедура SetName ( Const Value : строка ) ; public /// /// Получить или установить имя студента. /// Имя свойства : строка read FName write SetName ; конец ; // ...выполнениепроцедура TStudent . SetName ( Const Value : строка ) ; begin FName : = Значение ; конец ;конец .
Ява
В этом примере простого класса, представляющего студента с сохраненным только именем, можно увидеть, что имя переменной является частным, то есть видимым только из класса Student, а «установщик» и «получатель» являются общедоступными, а именно « » и " " методы.getName()
setName(name)
открытый класс Student { частное имя строки ; общедоступная строка getName () { возвращаемое имя ; } public void setName ( String newName ) { name = newName ; } }
JavaScript
В этом примере функция-конструктор Student
используется для создания объектов, представляющих учащегося, только с сохраненным именем.
функция Студент ( имя ) { var _name = name ; это . getName = function () { return _name ; }; это . setName = функция ( значение ) { _name = значение ; }; }
Или (нестандартный):
функция Студент ( имя ) { var _name = name ; это . __defineGetter__ ( 'имя' , функция () { return _name ; }); это . __defineSetter__ ( 'имя' , функция ( значение ) { _name = value ; }); }
Или (если использовать прототипы для наследования; ECMA-6 !):
функция Студент ( имя ) { это . _name = имя ; }Студент . prototype = { получить имя () { вернуть это . _name ; }, установите имя ( значение ) { this . _name = значение ; } };
Или (без использования прототипов; ECMA-6):
var Student = { получить имя () { вернуть это . _name ; }, установите имя ( значение ) { this . _name = значение ; } };
Или (при использовании defineProperty):
функция Студент ( имя ) { это . _name = имя ; } Объект . defineProperty ( Student . prototype , 'name' , { get : function () { return this . _name ; }, set : function ( value ) { this . _name = value ; } });
ActionScript 3.0
пакет { открытый класс Студент { частный var _name : String ; публичная функция получить имя () : String { return _name ; } имя набора общедоступных функций ( значение : String ) : void { _name = value ; } } }
Цель-C
Используя традиционный синтаксис Objective-C 1.0, с ручным подсчетом ссылок, как тот, который работает над GNUstep в Ubuntu 12.04 :
@interface Студент : NSObject { NSString * _name ; }- ( NSString * ) имя ; - ( void ) setName: ( NSString * ) имя ;@конец@implementation Студент- ( NSString * ) имя { return _name ; }- ( void ) setName: ( NSString * ) имя { [ _name release ]; _name = [ имя сохранить ]; }@конец
Используя новый синтаксис Objective-C 2.0, который используется в Mac OS X 10.6 , iOS 4 и Xcode 3.2, генерируя тот же код, что и описанный выше:
@interface Студент : NSObject@property ( неатомный , сохранить ) NSString * name ;@конец@implementation Студент@synthesize name = _name ;@конец
А начиная с OS X 10.8 и iOS 6 при использовании Xcode 4.4 и выше синтаксис можно даже упростить:
@interface Студент : NSObject@property ( неатомный , сильный ) NSString * name ;@конец@implementation Студент// Здесь ничего не происходит, и все в порядке.@конец
Perl
пакет Студент ;sub новый { благословение {}, сдвиг ; }sub set_name { мой $ self = shift ; $ self -> { имя } = $ _ [ 0 ]; }sub get_name { мой $ self = shift ; return $ self -> { имя }; }1 ;
Или, используя Class :: Accessor
пакет Студент ; использовать базовый qw (Class :: Accessor) ; __PACKAGE__ -> follow_best_practice ;Студент -> mk_accessors ( qw (имя) );1 ;
Или, используя объектную систему Moose Object System :
пакет Студент ; использовать Moose ;# Moose использует имя атрибута в качестве установщика и получателя, свойства чтения и записи # позволяют нам переопределить это и предоставить наши собственные имена, в этом случае get_name и set_name имеют 'name' => ( is => 'rw' , isa => 'Str' , reader => 'get_name' , writer => 'set_name' );1 ;
PHP
В этом примере простого класса , представляющий студент только имя хранится, можно увидеть переменное имя является частным, то есть видна только из класса Student, и «сеттер» и «добытчик» является публичным, а именно getName()
и setName('name')
методой.
class Student { частная строка $ name ; / ** * @return string Имя. * / публичная функция getName () : string { return $ this -> name ; } / ** * @param string $ newName Имя для установки. * / публичная функция setName ( строка $ newName ) : void { $ this -> name = $ newName ; } }
Python
В этом примере используется класс Python с одной переменной, геттером и сеттером.
class Student : # Инициализатор def __init__ ( self , name : str ) -> None : # Переменная экземпляра для хранения имени студента self . _name = имя # Метод получения @property def name ( self ): return self . _название # Метод установки @name . setter def name ( self , new_name ): self . _name = новое_имя
>>> bob = Студент ( "Боб" ) >>> bob . имя Боб >>> боб . name = "Алиса" >>> боб . имя Алиса >>> боб . _name = "Charlie" # обойти сеттер >>> 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 )) ( Определить ученик% ( объект класса % ( имя поля инициализации ) ( определить / публично ( получить имя ) имя ) ( определить / публично ( установить имя! новое имя ) ( установить! имя новое имя )) ( супер-новый )))( определить s ( новый студент% [ имя "Алиса" ])) ( отправить s имя-получателя ) ; => «Алиса» ( отправьте s set-name! «Bob» ) ( отправьте s get-name ) ; => «Боб»
Определения структур - это альтернативный способ определения новых типов значений с мутаторами, которые присутствуют, когда это явно требуется:
#lang racket ( struct student ( name ) #: mutable ) ( define s ( student "Алиса" )) ( set-student-name! s "Боб" ) ( имя-студента s ) ; => «Боб»
Рубин
В Ruby могут быть определены индивидуальные методы доступа и мутаторы, либо конструкции метапрограммирования, attr_reader
либо attr_accessor
их можно использовать как для объявления частной переменной в классе, так и для предоставления общего доступа только для чтения или чтения-записи к ней соответственно.
Определение индивидуальных методов доступа и мутатора создает пространство для предварительной обработки или проверки данных.
класс Студент def имя @ имя конец def name = ( значение ) @name = значение end end
Простой публичный доступ только для чтения к подразумеваемой @name
переменной
class Student attr_reader : имя конец
Простой публичный доступ для чтения и записи к подразумеваемой @name
переменной
class Student attr_accessor : имя конец
Болтовня
age: aNumber "Установите возраст получателя как aNumber, если он больше 0 и меньше 150" ( aNumber от: 0 до: 150 ) ifTrue: [ age : = aNumber ]
Быстрый
class Student { private var _name : String = "" var name : String { get { return self . _name } установил { self . _name = newValue } } }
Visual Basic .NET
Этот пример иллюстрирует идею VB.NET о свойствах, которые используются в классах. Подобно C #, есть явное использование Get
и Set
методов.
Студент публичного класса Личное _name как строка Имя общедоступного свойства () Get Return _name End Get Set ( ByVal value ) _name = value End Set End Property Конец класса
В VB.NET 2010 автоматически реализованные свойства можно использовать для создания свойства без использования синтаксиса Get и Set. Обратите внимание, что скрытая переменная создается вызываемым компилятором _name
, чтобы соответствовать свойству name
. Использование другой переменной в названном классе _name
приведет к ошибке. Привилегированный доступ к базовой переменной доступен из класса.
Открытый класс Student Public Property Название Как Струнный End класса
Смотрите также
- Свойство (программирование)
- Индексатор (программирование)
- Неизменяемый объект
Рекомендации
- ^ Стивен Фукуа (2009). «Автоматические свойства в C # 3.0» . Архивировано из оригинала на 2011-05-13 . Проверено 19 октября 2009 .
- ^ Тим Ли (13.07.1998). «Эффективность выполнения функций доступа» .
- ^ «CLHS: Macro DEFCLASS» . Проверено 29 марта 2011 .
- ^ «CLHS: 7.5.2 Доступ к слотам» . Проверено 29 марта 2011 .
- ^ «MOP: определения слотов» . Проверено 29 марта 2011 .
- ^ «Функции - язык программирования D» . Проверено 13 января 2013 .
- ^ "Стиль D" . Проверено 1 февраля 2013 .