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
, month
und day
-Variablen dargestellt wird, können eingehende Datumsangaben vom setDate
Mutator geteilt werden, während aus Konsistenzgründen auf dieselben privaten Instanzvariablen durch setYear
und 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 getAmount
Accessor kann eine Zeichenfolge aus einer numerischen Variablen mit der durch einen versteckten currency
Parameter 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 set
Teil 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
, :writer
und :accessor
-Optionen (sogar mehrfach) angeben , um Reader-Methoden, Setter-Methoden und Accessor-Methoden (eine Reader-Methode und die entsprechende setf
Methode) zu definieren. Auf Slots kann immer direkt über ihre Namen mit with-slots
und 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 :accessor
Option 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 @property
Attribut 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 Student
Instanz 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 Student
verwendet, 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" __get
und __set
fü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_reader
oder attr_accessor
kö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 @name
Variable
class Student
attr_reader :name
end
Einfacher öffentlicher Lese-/Schreibzugriff auf implizite @name
Variable
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 Get
und 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 _name
wü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