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

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

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

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

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

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

Последствия [ править ]

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

Блок , в котором определен мутатор, предоставляет возможность проверки или предварительной обработки входящих данных. Если весь внешний доступ гарантированно осуществляется через мутатор, то эти шаги невозможно обойти. Например, если дата представлена ​​отдельным приватным 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;
    }
});

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

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

Visual Basic .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

См. также [ править ]

Ссылки [ править ]

  1. ^ Стивен Фукуа (2009). «Автоматические свойства в C# 3.0» . Архивировано из оригинала 13 мая 2011 г. Проверено 19 октября 2009 г.
  2. ^ Тим Ли (13 июля 1998 г.). «Эффективность выполнения функций доступа» .
  3. ^ «CLHS: Макрос ДЕФКЛАСС» . Проверено 29 марта 2011 г.
  4. ^ «CLHS: 7.5.2 Доступ к слотам» . Проверено 29 марта 2011 г.
  5. ^ «MOP: Определения слотов» . Проверено 29 марта 2011 г.
  6. ^ «Функции — язык программирования D» . Проверено 13 января 2013 г.
  7. ^ «Стиль Д» . Проверено 1 февраля 2013 г.
  8. ^ «Object.prototype.__defineGetter__() — JavaScript | MDN» . http://developer.mozilla.org . Проверено 6 июля 2021 г.
  9. ^ «PHP: Перегрузка — Руководство» . www.php.net . Проверено 6 июля 2021 г.