Mutator-Methode - Mutator method

In der Informatik ist eine Mutatormethode eine Methode , mit der Änderungen an einer Variablen gesteuert werden. Sie sind auch weithin als Setter- Methoden bekannt. Häufig wird ein Setter von einem Getter (zusammen auch als Accessoren bezeichnet ) begleitet, der den Wert der privaten Membervariablen zurückgibt.

Die Mutator-Methode wird am häufigsten in der objektorientierten Programmierung verwendet , um dem Prinzip der Kapselung zu folgen . Nach diesem Prinzip werden Member- Variablen einer Klasse privat gemacht, um sie vor anderem Code zu verbergen und zu schützen, und können nur durch eine öffentliche Member-Funktion (die Mutator-Methode) geändert werden, die den gewünschten neuen Wert als Parameter nimmt, optional validiert it und ändert die private Membervariable . Mutatormethoden können mit dem Überladen von Zuweisungsoperatoren verglichen werden, aber sie erscheinen normalerweise auf verschiedenen Ebenen der Objekthierarchie.

Mutatormethoden können auch in nicht objektorientierten Umgebungen verwendet werden. In diesem Fall wird dem Mutator zusammen mit dem neuen Wert eine Referenz auf die zu ändernde Variable übergeben. In diesem Szenario kann der Compiler den Code nicht daran hindern, die Mutatormethode zu umgehen und die Variable direkt zu ändern. Es liegt in der Verantwortung der Entwickler , sicherzustellen, dass die Variable nur durch die Mutator-Methode und nicht direkt geändert wird.

In Programmiersprachen, die sie unterstützen, bieten Eigenschaften eine bequeme Alternative, ohne auf den Nutzen der Kapselung verzichten zu müssen.

In den folgenden Beispielen kann eine vollständig implementierte Mutator-Methode auch die Eingabedaten validieren oder weitere Aktionen wie das Auslösen eines Ereignisses ausführen .

Auswirkungen

Die Alternative zum Definieren von Mutator- und Accessormethoden oder Eigenschaftsblöcken besteht darin, der Instanzvariablen eine andere Sichtbarkeit als private zu geben und direkt von außerhalb der Objekte darauf zuzugreifen. Eine viel feinere Kontrolle der Zugriffsrechte kann mithilfe von Mutatoren und Accessoren definiert werden. Beispielsweise kann ein Parameter schreibgeschützt gemacht werden, indem einfach ein Accessor, aber kein Mutator definiert wird. Die Sichtbarkeit der beiden Methoden kann unterschiedlich sein; Es ist oft nützlich, wenn der Accessor öffentlich ist, während der Mutator geschützt bleibt, entweder im Paket privat oder intern.

Der Block, in dem der Mutator definiert ist, bietet die Möglichkeit zur Validierung oder Vorverarbeitung eingehender Daten. Wenn sichergestellt ist, dass alle externen Zugriffe über den Mutator erfolgen, können diese Schritte nicht umgangen werden. Wenn ein Datum beispielsweise durch separate private year, monthund day-Variablen dargestellt wird, können eingehende Datumsangaben vom setDateMutator geteilt werden, während aus Konsistenzgründen auf dieselben privaten Instanzvariablen durch setYearund zugegriffen wird setMonth. In allen Fällen können Monatswerte außerhalb von 1 - 12 mit demselben Code abgelehnt werden.

Umgekehrt ermöglichen Zugriffshilfen die Synthese nützlicher Datendarstellungen aus internen Variablen, während ihre Struktur gekapselt und vor äußeren Modulen verborgen bleibt. Ein monetärer getAmountAccessor kann eine Zeichenfolge aus einer numerischen Variablen mit der durch einen versteckten currencyParameter definierten Anzahl von Dezimalstellen erstellen .

Moderne Programmiersprachen bieten oft die Möglichkeit, den Boilerplate für Mutatoren und Accessoren in einer einzigen Zeile zu generieren – wie zum Beispiel C# public string Name { get; set; }und Ruby attr_accessor :name. In diesen Fällen werden keine Codeblöcke zur Validierung, Vorverarbeitung oder Synthese erstellt. Diese vereinfachten Zugriffsmethoden behalten immer noch den Vorteil der Kapselung gegenüber einfachen öffentlichen Instanzvariablen, aber es ist üblich, dass mit fortschreitendem Systemdesign , die Software gewartet wird und sich die Anforderungen ändern, die Anforderungen an die Daten anspruchsvoller werden. Viele automatische Mutatoren und Accessoren werden schließlich durch separate Codeblöcke ersetzt. Der Vorteil der automatischen Erstellung in den frühen Tagen der Implementierung besteht darin, dass die öffentliche Schnittstelle der Klasse unabhängig davon, ob sie erweitert wird oder nicht, identisch bleibt und keine umfangreiche Umgestaltung erforderlich ist, falls dies der Fall ist.

Die Manipulation von Parametern, die Mutatoren und Accessoren innerhalb der Klasse haben, in der sie definiert sind, erfordert oft einige zusätzliche Überlegungen. In den frühen Tagen einer Implementierung, wenn in diesen Blöcken wenig oder kein zusätzlicher Code vorhanden ist, macht es keinen Unterschied, ob direkt auf die private Instanzvariable zugegriffen wird oder nicht. Wenn Validierung, Kreuzvalidierung , Datenintegritätsprüfungen , Vorverarbeitung oder andere Raffinessen hinzugefügt werden, können subtile Fehler auftreten, bei denen ein interner Zugriff den neueren Code verwendet, während er an anderen Stellen umgangen wird.

Zugriffsfunktionen können aufgrund der zusätzlichen Schritte weniger effizient sein als das direkte Abrufen oder Speichern von Datenfeldern, jedoch werden solche Funktionen oft inline integriert , wodurch der Overhead eines Funktionsaufrufs eliminiert wird.

Beispiele

Montage

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

C

In der Datei 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

In der Datei 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;
}

In der Datei 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;
}

In Datei 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

C++

In der Datei 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

In der Datei 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#

Dieses Beispiel veranschaulicht die C# -Idee von properties , die ein besonderer Typ von Klassenmembern sind . Im Gegensatz zu Java sind keine expliziten Methoden definiert; eine öffentliche 'Eigenschaft' enthält die Logik zur Handhabung der Aktionen. Beachten Sie die Verwendung der integrierten (nicht deklarierten) Variablen value.

public class Student {
    private string name;

    /// <summary>
    /// Gets or sets student's name
    /// </summary>
    public string Name {
        get { return name; }
        set { name = value; }
    }
}

In späteren C#-Versionen (.NET Framework 3.5 und höher) kann dieses Beispiel wie folgt abgekürzt werden, ohne die private Variable zu deklarieren name.

public class Student {
    public string Name { get; set; }
}

Die Verwendung der abgekürzten Syntax bedeutet, dass die zugrunde liegende Variable innerhalb der Klasse nicht mehr verfügbar ist. Daher muss der setTeil der Immobilie für die Abtretung vorhanden sein. Der Zugriff kann mit einem set-spezifischen Zugriffsmodifizierer eingeschränkt werden.

public class Student {
    public string Name { get; private set; }
}

Gemeinsame Lisp

In Common Lisp Object System können Slot-Spezifikationen innerhalb von Klassendefinitionen beliebige der :reader, :writerund :accessor-Optionen (sogar mehrfach) angeben , um Reader-Methoden, Setter-Methoden und Accessor-Methoden (eine Reader-Methode und die entsprechende setfMethode) zu definieren. Auf Slots kann immer direkt über ihre Namen mit with-slotsund zugegriffen werden slot-value, und die Slot-Accessor-Optionen definieren spezialisierte Methoden, die verwenden slot-value.

CLOS selbst hat keine Vorstellung von Eigenschaften, obwohl die MetaObject Protocol- Erweiterung Mittel für den Zugriff auf die Namen der Lese- und Schreibfunktionen eines Slots spezifiziert, einschließlich der mit der :accessorOption generierten .

Das folgende Beispiel zeigt eine Definition einer Schülerklasse mit diesen Slot-Optionen und dem direkten Slot-Zugriff:

(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

D unterstützt eine Getter- und Setter-Funktionssyntax. In Version 2 der Sprache getter und setter sollten class/struct-Methoden das @propertyAttribut haben.

class Student {
    private char[] name_;
    // Getter
    @property char[] name() {
        return this.name_;
    }
    // Setter
    @property char[] name(char[] name_in) {
        return this.name_ = name_in;
    }
}

Eine StudentInstanz kann wie folgt verwendet werden:

auto student = new Student;
student.name = "David";           // same effect as student.name("David")
auto student_name = student.name; // same effect as student.name()

Delphi

Dies ist eine einfache Klasse in Delphi, die das Konzept der öffentlichen Eigenschaft für den Zugriff auf ein privates Feld veranschaulicht.

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.

Java

In diesem Beispiel einer einfachen Klasse einen Schüler mit nur dem Namen repräsentiert gespeichert ist , kann man das sehen Variable Name ist privat, dh von der Student - Klasse nur dann sichtbar, und die „Setter“ und „Getter“ sind öffentlich, nämlich die „ getName()“ und " setName(name)"-Methoden.

public class Student {
    private String name;

    public String getName() {
        return name;
    }
    
    public void setName(String newName) {
        name = newName;
    }
}

JavaScript

In diesem Beispiel wird die Konstruktor-Funktion Studentverwendet, um Objekte zu erstellen, die einen Schüler darstellen, wobei nur der Name gespeichert ist.

function Student(name) {
  var _name = name;

  this.getName = function() {
    return _name;
  };

  this.setName = function(value) {
    _name = value;
  };
}

Oder (nicht standardmäßig und veraltet):

function Student(name){
    var _name = name;
   
    this.__defineGetter__('name', function() {
        return _name;
    });
   
    this.__defineSetter__('name', function(value) {
        _name = value;
    });
}

Oder (bei Verwendung von Prototypen zur Vererbung; ECMA-6 !):

function Student(name){
    this._name = name;
}

Student.prototype = {
    get name() {
        return this._name;
    },
    set name(value) {
        this._name = value;
    }
};

Oder (ohne Verwendung von Prototypen; ECMA-6):

var Student = {
    get name() {
        return this._name;
    },
    set name(value) {
        this._name = value;
    }
};

Oder (bei Verwendung von 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;
        }
    }
}

Ziel c

Unter Verwendung der traditionellen Objective-C 1.0-Syntax mit manueller Referenzzählung als diejenige, die an GNUstep unter Ubuntu 12.04 arbeitet :

@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

Unter Verwendung der neueren Objective-C 2.0-Syntax, wie sie in Mac OS X 10.6 , iOS 4 und Xcode 3.2 verwendet wird, wird derselbe Code wie oben beschrieben generiert:

@interface Student : NSObject

@property (nonatomic, retain) NSString *name;

@end

@implementation Student

@synthesize name = _name;

@end

Und ab OS X 10.8 und iOS 6 kann die Syntax bei Verwendung von Xcode 4.4 und höher sogar vereinfacht werden:

@interface Student : NSObject

@property (nonatomic, strong) NSString *name;

@end

@implementation Student

//Nothing goes here and it's OK.

@end

Perl

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;

Oder mit Class::Accessor

package Student;
use base qw(Class::Accessor);
__PACKAGE__->follow_best_practice;

Student->mk_accessors(qw(name));

1;

Oder mit dem Moose Object System :

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 definiert die "magischen Methoden" __getund __setfür Eigenschaften von Objekten.

In diesem Beispiel einer einfachen Klasse einen Schüler mit nur dem Namen repräsentiert gespeichert ist , kann man das sehen Variable Name ist privat, dh nur von der Student - Klasse sichtbar, und die „Setter“ und „Getter“ sind öffentlich, nämlich die getName()und setName('name')Methoden.

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

In diesem Beispiel wird eine Python-Klasse mit einer Variablen, einem Getter und einem Setter verwendet.

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

Schläger

In Racket ist das Objektsystem eine Möglichkeit, Code zu organisieren, der zusätzlich zu Modulen und Einheiten kommt. Wie im Rest der Sprache hat das Objektsystem erstklassige Werte und der lexikalische Geltungsbereich wird verwendet, um den Zugriff auf Objekte und Methoden zu steuern.

#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"

Strukturdefinitionen sind eine alternative Möglichkeit, neue Typen von Werten zu definieren, wobei Mutatoren vorhanden sind, wenn dies ausdrücklich erforderlich ist:

#lang racket
(struct student (name) #:mutable)
(define s (student "Alice"))
(set-student-name! s "Bob")
(student-name s)                        ; => "Bob"

Rubin

In Ruby können einzelne Accessor- und Mutator-Methoden definiert werden, oder die Metaprogrammierungskonstrukte attr_readeroder attr_accessorkönnen sowohl verwendet werden, um eine private Variable in einer Klasse zu deklarieren, als auch um entweder schreibgeschützten bzw. öffentlichen Lese-/Schreibzugriff darauf bereitzustellen.

Die Definition individueller Accessor- und Mutator-Methoden schafft Raum für die Vorverarbeitung oder Validierung der Daten

class Student
  def name
    @name
  end

  def name=(value)
    @name=value
  end
end

Schreibgeschützter einfacher öffentlicher Zugriff auf implizierte @nameVariable

class Student
  attr_reader :name
end

Einfacher öffentlicher Lese-/Schreibzugriff auf implizite @nameVariable

class Student
  attr_accessor :name
end

Smalltalk

  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 ]

Schnell

class Student {
    private var _name: String = ""

    var name: String {
        get {
            return self._name
        }
        set {
            self._name = newValue
        }
    }
}

Visual Basic .NET

Dieses Beispiel veranschaulicht die VB.NET-Idee von Eigenschaften, die in Klassen verwendet werden. Ähnlich wie in C# gibt es eine explizite Verwendung der Getund Set-Methoden.

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

In VB.NET 2010 können automatisch implementierte Eigenschaften verwendet werden, um eine Eigenschaft zu erstellen, ohne die Get- und Set-Syntax verwenden zu müssen. Beachten Sie, dass vom Compiler eine versteckte Variable namens _name, die der Property entspricht, erstellt wird name. Die Verwendung einer anderen Variablen innerhalb der benannten Klasse _namewürde zu einem Fehler führen. Der privilegierte Zugriff auf die zugrunde liegende Variable ist innerhalb der Klasse verfügbar.

Public Class Student
    Public Property name As String
End Class

Siehe auch

Verweise