stringtranslate.com

Метод мутатора

В информатике метод мутатора — это метод, используемый для управления изменениями переменной. Они также широко известны как методы сеттера . Часто сеттер сопровождается геттером , который возвращает значение закрытой переменной-члена. Они также известны вместе как аксессоры .

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

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

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

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

Подразумеваемое

Альтернативой определению методов мутатора и аксессора или блоков свойств является предоставление переменной экземпляра некоторой видимости, отличной от закрытой, и доступ к ней напрямую извне объектов. Гораздо более тонкий контроль прав доступа может быть определен с помощью мутаторов и аксессоров. Например, параметр может быть сделан доступным только для чтения, просто определив аксессор, но не мутатор. Видимость двух методов может быть разной; часто бывает полезно, чтобы аксессор был публичным, в то время как мутатор оставался защищенным, закрытым для пакета или внутренним.

Блок , в котором определен мутатор, предоставляет возможность для проверки или предварительной обработки входящих данных. Если весь внешний доступ гарантированно осуществляется через мутатор, то эти шаги нельзя обойти. Например, если дата представлена ​​отдельными частными переменными year, monthи , то входящие даты могут быть разделены мутатором, в то время как для согласованности к тем же частным переменным экземпляра обращаются и . Во всех случаях значения месяцев за пределами 1 - 12 могут быть отклонены тем же кодом.daysetDatesetYearsetMonth

Accessors, наоборот, позволяют синтезировать полезные представления данных из внутренних переменных, сохраняя их структуру инкапсулированной и скрытой от внешних модулей. Money getAmountaccessor может построить строку из числовой переменной с количеством десятичных знаков, определяемых скрытым 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 ; } }                

JavaScript

В этом примере функция-конструктор 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 ; } } ) ;                   

ActionScript 3.0

пакет { public class Student { private var _name : String ; public function get name () : String { return _name ; }                     имя набора публичных функций ( значение : строка ) : void { _name = значение ; } } }             

Objective-C

Используя традиционный синтаксис 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

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 } }       

Smalltalk

 age:  aNumber  " Установить возраст получателя равным aNumber, если он больше 0 и меньше 150 " ( aNumber  между:  0  и:  150 ) ifTrue: [ age  :=  aNumber ]

Быстрый

класс  Студент  {  частная  переменная  _имя :  Строка  =  ""  имя переменной :  строка  {  получить  {  вернуть  self._name } установить { self._name = newValue } } }        

Visual Basic .NET

Этот пример иллюстрирует идею свойств 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        

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

Ссылки

  1. ^ Стивен Фукуа (2009). "Автоматические свойства в C# 3.0". Архивировано из оригинала 2011-05-13 . Получено 2009-10-19 .
  2. ^ Тим Ли (1998-07-13). «Эффективность времени выполнения функций доступа».
  3. ^ "CLHS: Macro DEFCLASS" . Получено 29.03.2011 .
  4. ^ "CLHS: 7.5.2 Доступ к слотам" . Получено 29.03.2011 .
  5. ^ "MOP: Определения слотов" . Получено 29.03.2011 .
  6. ^ "Функции - Язык программирования D" . Получено 2013-01-13 .
  7. ^ "The D Style" . Получено 2013-02-01 .
  8. ^ "Object.prototype.__defineGetter__() - JavaScript | MDN". developer.mozilla.org . Получено 2021-07-06 .
  9. ^ "PHP: Перегрузка - Руководство". www.php.net . Получено 2021-07-06 .