Метод мутатора
В информатике — метод мутатора это метод, используемый для управления изменениями переменной. Они также широко известны как установки методы . Часто установщик сопровождается геттером , который возвращает значение частной переменной-члена. Они также известны под общим названием аксессоры .
Метод мутатора чаще всего используется в объектно-ориентированном программировании с соблюдением принципа инкапсуляции . Согласно этому принципу, переменные- члены класса проверяет делаются закрытыми, чтобы скрыть и защитить их от другого кода, и могут быть изменены только с помощью общедоступной функции-члена (метода мутатора), которая принимает желаемое новое значение в качестве параметра и при необходимости его и изменяет закрытую переменную-член . Методы-мутаторы можно сравнить с присваивания перегрузкой оператора , но они обычно появляются на разных уровнях иерархии объектов.
Методы-мутаторы также могут использоваться в необъектно-ориентированных средах. В этом случае ссылка на изменяемую переменную передается мутатору вместе с новым значением. В этом сценарии компилятор не может запретить коду обходить метод мутатора и напрямую изменять переменную. Ответственность ложится на разработчиков за то, чтобы переменная изменялась только с помощью метода мутатора, а не напрямую, .
В языках программирования, которые их поддерживают, свойства предлагают удобную альтернативу, не отказываясь от полезности инкапсуляции.
В приведенных ниже примерах полностью реализованный метод мутатора также может проверять входные данные или предпринимать дальнейшие действия, такие как запуск события .
Подразумеваемое
[ редактировать ]Альтернативой определению методов-мутаторов и средств доступа или блоков свойств является предоставление переменной экземпляра некоторой видимости, отличной от частной, и доступ к ней непосредственно извне объектов. Гораздо более тонкий контроль прав доступа можно определить с помощью мутаторов и аксессоров. Например, параметр можно сделать доступным только для чтения, просто определив метод доступа, а не мутатор. Видимость этих двух методов может быть разной; часто бывает полезно, чтобы метод доступа был общедоступным, в то время как мутатор оставался защищенным, частным для пакета или внутренним.
Блок , в котором определен мутатор, предоставляет возможность проверки или предварительной обработки входящих данных. Если весь внешний доступ гарантированно осуществляется через мутатор, то эти шаги невозможно обойти. Например, если дата представлена отдельным приватным year
, month
и day
переменные, то входящие даты можно разделить по setDate
мутатор, в то время как для обеспечения согласованности доступ к одним и тем же частным переменным экземпляра осуществляется setYear
и setMonth
. Во всех случаях значения месяцев за пределами 1–12 могут быть отклонены одним и тем же кодом.
Аксессоры, наоборот, позволяют синтезировать полезные представления данных из внутренних переменных, сохраняя при этом их структуру инкапсулированной и скрытой от внешних модулей. Денежный getAmount
аксессор может построить строку из числовой переменной с количеством десятичных знаков, определенным скрытым currency
параметр.
Современные языки программирования часто предлагают возможность генерировать шаблон для мутаторов и средств доступа в одной строке, как, например, в C#. public string Name { get; set; }
и Руби attr_accessor :name
. В этих случаях блоки кода для проверки, предварительной обработки или синтеза не создаются. Эти упрощенные методы доступа по-прежнему сохраняют преимущество инкапсуляции перед простыми общедоступными переменными экземпляра, но обычно по мере развития системы программное обеспечение поддерживается , а требования изменяются, а требования к данным становятся более сложными. Многие автоматические мутаторы и средства доступа со временем заменяются отдельными блоками кода. Преимущество автоматического создания их на первых этапах реализации заключается в том, что общедоступный интерфейс класса остается идентичным независимо от того, добавляется ли большая сложность или нет, и в этом случае не требуется обширного рефакторинга. [1]
Манипулирование параметрами, имеющими мутаторы и средства доступа, изнутри класса, где они определены, часто требует некоторого дополнительного размышления. На ранних этапах реализации, когда в этих блоках мало или вообще нет дополнительного кода, не имеет значения, осуществляется ли прямой доступ к переменной частного экземпляра или нет. валидации, перекрестной проверки , проверки целостности данных По мере добавления , предварительной обработки или других усовершенствований могут возникать незначительные ошибки , когда при некотором внутреннем доступе используется более новый код, а в других местах он обходит.
Функции доступа могут быть менее эффективными, чем прямая выборка или сохранение полей данных, из-за дополнительных шагов, [2] однако такие функции часто являются встроенными , что устраняет накладные расходы на вызов функции.
Примеры
[ редактировать ]Сборка
[ редактировать ]student struct
age dd ?
student ends
.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 _STUDENT_H
#define _STUDENT_H
struct student; /* opaque structure */
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, "Maurice");
char *name = student_get_name(s);
int old_age = student_get_age(s);
printf("%s's old age = %i\n", name, old_age);
student_set_age(s, 21);
int new_age = student_get_age(s);
printf("%s's new age = %i\n", name, new_age);
student_delete(s);
return 0;
}
В файле Makefile:
all: out.txt; cat $<
out.txt: main; ./$< > $@
main: main.o student.o
main.o student.o: student.h
clean: ;$(RM) *.o out.txt main
С++
[ редактировать ]В файле Student.h:
#ifndef STUDENT_H
#define STUDENT_H
#include <string>
class Student {
public:
Student(const std::string& name);
const std::string& name() const;
void name(const std::string& name);
private:
std::string 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
.
public class Student
{
private string name;
/// <summary>
/// Gets or sets student's name
/// </summary>
public string Name
{
get { return name; }
set { name = value; }
}
}
В более поздних версиях C# (.NET Framework 3.5 и выше) этот пример можно сократить следующим образом, без объявления частной переменной name
.
public class Student
{
public string Name { get; set; }
}
Использование сокращенного синтаксиса означает, что базовая переменная больше не доступна внутри класса. В результате set
часть имущества должна быть передана в распоряжение. Доступ можно ограничить с помощью set
-специфичный модификатор доступа.
public class Student
{
public string Name { get; private set; }
}
Общий Лисп
[ редактировать ]В 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 student-name) ; student-name is setf'able
(birthdate :initarg :birthdate :initform 0 :reader student-birthdate)
(number :initarg :number :initform 0 :reader student-number :writer set-student-number)))
;; Example of a calculated property getter (this is simply a method)
(defmethod student-age ((self student))
(- (get-universal-time) (student-birthdate self)))
;; Example of direct slot access within a calculated property setter
(defmethod (setf student-age) (new-age (self student))
(with-slots (birthdate) self
(setf birthdate (- (get-universal-time) new-age))
new-age))
;; The slot accessing options generate methods, thus allowing further method definitions
(defmethod set-student-number :before (new-number (self student))
;; You could also check if a student with the new-number already exists.
(check-type new-number (integer 1 *)))
Д
[ редактировать ]D поддерживает синтаксис функций получения и установки. В версии 2 языка методы получения и установки класса/структуры должны иметь @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"; // same effect as student.name("David")
auto student_name = student.name; // same effect as student.name()
Дельфи
[ редактировать ]Это простой класс на языке Delphi, который иллюстрирует концепцию открытого свойства для доступа к частному полю.
interface
type
TStudent = class
strict private
FName: string;
procedure SetName(const Value: string);
public
/// <summary>
/// Get or set the name of the student.
/// </summary>
property Name: string read FName write SetName;
end;
// ...
implementation
procedure TStudent.SetName(const Value: string);
begin
FName := Value;
end;
end.
Ява
[ редактировать ]В этом примере простого класса, представляющего студента, в котором сохранено только имя, можно видеть, что переменной имя является частным, то есть видимым только из класса Student, а «установщик» и «получатель» являются общедоступными, а именно « getName()
" и " setName(name)
"методы.
public class Student {
private String name;
public String getName() {
return name;
}
public void setName(String newName) {
name = newName;
}
}
JavaScript
[ редактировать ]В этом примере функция-конструктор Student
используется для создания объектов, представляющих учащегося, сохраняя только имя.
function Student(name) {
var _name = name;
this.getName = function() {
return _name;
};
this.setName = function(value) {
_name = value;
};
}
Или (используя устаревший способ определения средств доступа в веб-браузерах): [8]
function Student(name){
var _name = name;
this.__defineGetter__('name', function() {
return _name;
});
this.__defineSetter__('name', function(value) {
_name = value;
});
}
Или (используя прототипы для наследования и синтаксис средств доступа ES6 ):
function Student(name){
this._name = name;
}
Student.prototype = {
get name() {
return this._name;
},
set name(value) {
this._name = value;
}
};
Или (без использования прототипов):
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;
}
});
ЭкшнСкрипт 3.0
[ редактировать ]package
{
public class Student
{
private var _name : String;
public function get name() : String
{
return _name;
}
public function set name(value : String) : void
{
_name = value;
}
}
}
Цель-C
[ редактировать ]Использование традиционного синтаксиса Objective-C 1.0 с ручным подсчетом ссылок, как в GNUstep в Ubuntu 12.04 :
@interface Student : NSObject
{
NSString *_name;
}
- (NSString *)name;
- (void)setName:(NSString *)name;
@end
@implementation Student
- (NSString *)name
{
return _name;
}
- (void)setName:(NSString *)name
{
[_name release];
_name = [name retain];
}
@end
Используя новый синтаксис Objective-C 2.0, используемый в Mac OS X 10.6 , iOS 4 и Xcode 3.2, генерируем тот же код, что описано выше:
@interface Student : NSObject
@property (nonatomic, retain) NSString *name;
@end
@implementation Student
@synthesize name = _name;
@end
А начиная с OS X 10.8 и iOS 6 , при использовании Xcode 4.4 и выше синтаксис можно даже упростить:
@interface Student : NSObject
@property (nonatomic, strong) NSString *name;
@end
@implementation Student
//Nothing goes here and it's OK.
@end
Перл
[ редактировать ]package Student;
sub new {
bless {}, shift;
}
sub set_name {
my $self = shift;
$self->{name} = $_[0];
}
sub get_name {
my $self = shift;
return $self->{name};
}
1;
Или, используя Class::Accessor
package Student;
use base qw(Class::Accessor);
__PACKAGE__->follow_best_practice;
Student->mk_accessors(qw(name));
1;
Или, используя объектную систему Moose :
package Student;
use Moose;
# Moose uses the attribute name as the setter and getter, the reader and writer properties
# allow us to override that and provide our own names, in this case get_name and set_name
has 'name' => (is => 'rw', isa => 'Str', reader => 'get_name', writer => 'set_name');
1;
PHP
[ редактировать ]PHP определяет «волшебные методы» __get
и __set
для свойств объектов. [9]
В этом примере простого класса, представляющего студента, в котором сохранено только имя, можно видеть, что переменной имя является частным, то есть видимым только из класса Student, а «установщик» и «получатель» являются общедоступными, а именно getName()
и setName('name')
методы.
class Student
{
private string $name;
/**
* @return string The name.
*/
public function getName(): string
{
return $this->name;
}
/**
* @param string $newName The name to set.
*/
public function setName(string $newName): void
{
$this->name = $newName;
}
}
Питон
[ редактировать ]В этом примере используется класс Python с одной переменной, геттером и сеттером.
class Student:
# Initializer
def __init__(self, name: str) -> None:
# An instance variable to hold the student's name
self._name = name
# Getter method
@property
def name(self):
return self._name
# Setter method
@name.setter
def name(self, new_name):
self._name = new_name
>>> bob = Student("Bob")
>>> bob.name
Bob
>>> bob.name = "Alice"
>>> bob.name
Alice
>>> bob._name = "Charlie" # bypass the setter
>>> bob._name # bypass the getter
Charlie
Ракетка
[ редактировать ]В 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 "Alice"))
(set-student-name! s "Bob")
(student-name s) ; => "Bob"
Руби
[ редактировать ]В Ruby могут быть определены отдельные методы доступа и мутаторы или конструкции метапрограммирования. attr_reader
или attr_accessor
может использоваться как для объявления частной переменной в классе, так и для предоставления к ней публичного доступа только для чтения или для чтения и записи соответственно.
Определение отдельных методов доступа и мутаторов создает пространство для предварительной обработки или проверки данных.
class Student
def name
@name
end
def name=(value)
@name=value
end
end
Простой публичный доступ только для чтения к подразумеваемым @name
переменная
class Student
attr_reader :name
end
Простой публичный доступ для чтения и записи к подразумеваемым @name
переменная
class Student
attr_accessor :name
end
Ржавчина
[ редактировать ]struct Student {
name: String,
}
impl Student {
fn name(&self) -> &String {
&self.name
}
fn name_mut(&mut self) -> &mut String {
&mut self.name
}
}
Смолток
[ редактировать ] age: aNumber
" Set the receiver age to be aNumber if is greater than 0 and less than 150 "
(aNumber between: 0 and: 150)
ifTrue: [ age := aNumber ]
Быстрый
[ редактировать ]class Student {
private var _name: String = ""
var name: String {
get {
return self._name
}
set {
self._name = newValue
}
}
}
Визуальный Бейсик .NET
[ редактировать ]Этот пример иллюстрирует идею VB.NET о свойствах, которые используются в классах. Как и в C#, здесь явно используется Get
и Set
методы.
Public Class Student
Private _name As String
Public Property Name()
Get
Return _name
End Get
Set(ByVal value)
_name = value
End Set
End Property
End Class
В VB.NET 2010 автоматически реализуемые свойства можно использовать для создания свойства без использования синтаксиса Get и Set. Обратите внимание, что компилятор создает скрытую переменную, называемую _name
, чтобы соответствовать собственности name
. Использование другой переменной внутри класса с именем _name
приведет к ошибке. Привилегированный доступ к базовой переменной доступен изнутри класса.
Public Class Student
Public Property name As String
End Class
См. также
[ редактировать ]Ссылки
[ редактировать ]- ^ Стивен Фукуа (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 г.