Mutatormetod
Inom datavetenskap är en mutatormetod en metod som används för att kontrollera förändringar av en variabel. De är också allmänt kända som settermetoder . Ofta åtföljs en setter av en getter (tillsammans även känd som accessorer ), som returnerar värdet på den privata medlemsvariabeln.
Mutatormetoden används oftast i objektorienterad programmering , i enlighet med principen om inkapsling . Enligt denna princip görs medlemsvariabler i en klass privata för att dölja och skydda dem från annan kod, och kan endast modifieras av en offentlig medlemsfunktion (mutatormetoden), som tar det önskade nya värdet som en parameter , valfritt validerar it, och ändrar den privata medlemsvariabeln . Mutatormetoder kan jämföras med överbelastning av tilldelningsoperatörer, men de förekommer vanligtvis på olika nivåer i objekthierarkin.
Mutatormetoder kan också användas i icke-objektorienterade miljöer. I detta fall skickas en referens till variabeln som ska modifieras till mutatorn tillsammans med det nya värdet. I det här scenariot kan kompilatorn inte begränsa koden från att kringgå mutatormetoden och ändra variabeln direkt. Ansvaret faller på utvecklarna att säkerställa att variabeln endast modifieras genom mutatormetoden och inte modifieras direkt.
I programmeringsspråk som stöder dem erbjuder egenskaper ett bekvämt alternativ utan att ge upp användbarheten av inkapsling.
I exemplen nedan kan en fullt implementerad mutatormetod också validera indata eller vidta ytterligare åtgärder som att utlösa en händelse .
Implikationer
Alternativet till att definiera mutator- och accessormetoder, eller egenskapsblock , är att ge instansvariabeln någon annan synlighet än privat och komma åt den direkt utifrån objekten. Mycket finare kontroll av åtkomsträttigheter kan definieras med hjälp av mutatorer och accessorer. Till exempel kan en parameter göras skrivskyddad helt enkelt genom att definiera en accessor men inte en mutator. Synligheten för de två metoderna kan vara olika; det är ofta användbart för accessorn att vara offentlig medan mutatorn förblir skyddad, paketprivat eller intern.
Blocket där mutatorn definieras ger möjlighet till validering eller förbearbetning av inkommande data . Om all extern åtkomst garanterat kommer genom mutatorn, kan dessa steg inte förbigås. Till exempel, om ett datum representeras av separata privata för år
, månad
och dag
, kan inkommande datum delas av setDate-
mutatorn, medan samma privata instansvariabler nås av setYear
och setMonth för konsekvens
. I alla fall kan månadsvärden utanför 1 - 12 avvisas med samma kod.
Tillbehör omvänt tillåter syntes av användbara datarepresentationer från interna variabler samtidigt som deras struktur hålls inkapslad och dold från externa moduler. En monetär getAmount-
accessor kan bygga en sträng från en numerisk variabel med antalet decimaler definierade av en dold valutaparameter
.
Moderna programmeringsspråk erbjuder ofta möjligheten att generera boilerplate för mutatorer och accessorer på en enda rad – som till exempel C#:s publika sträng Namn { get; uppsättning; }
och Ruby's attr_accessor :name
. I dessa fall skapas inga kodblock för validering, förbearbetning eller syntes. Dessa förenklade accessorer behåller fortfarande fördelen med inkapsling framför enkla offentliga instansvariabler, men det är vanligt att, allt eftersom systemdesignen fortskrider , mjukvaran underhålls och kraven förändras, blir kraven på data mer sofistikerade. Många automatiska mutatorer och tillbehör ersätts så småningom av separata kodblock. Fördelen med att automatiskt skapa dem i början av implementeringen är att det publika gränssnittet för klassen förblir identiskt oavsett om större sofistikering läggs till eller inte, vilket inte kräver någon omfattande refaktorisering om så är fallet.
Manipulering av parametrar som har mutatorer och accessorer inifrån klassen där de är definierade kräver ofta ytterligare eftertanke. I början av en implementering, när det finns lite eller ingen ytterligare kod i dessa block, gör det ingen skillnad om variabeln för privata instanser nås direkt eller inte. När validering, korsvalidering , dataintegritetskontroller , förbearbetning eller annan sofistikering läggs till, kan subtila buggar dyka upp där viss intern åtkomst använder den nyare koden medan den på andra ställen förbigås.
Accessorfunktioner kan vara mindre effektiva än att direkt hämta eller lagra datafält på grund av de extra stegen som är involverade, men sådana funktioner är ofta infogade , vilket eliminerar overheaden för ett funktionsanrop.
Exempel
hopsättning
elev struktur ålder dd ? elev slutar
.code student_get_age proc objekt : 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 ], [ eb retx ] student_set_age endp
C
I filen student.h:
#ifndef _STUDENT_H #define _STUDENT_H struct student ; /* ogenomskinlig struktur */ 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
I filen student.c:
#include <stdlib.h> #include <string.h> #include "student.h" struct student { int age ; char * namn ; }; student * student_new ( int age , char * name ) { student * s = malloc ( sizeof ( student )); s -> namn = strdup ( namn ); s -> ålder = ålder ; returnera s ; } void student_delete ( student * s ) { free ( s -> name ); gratis ( 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 ; }
I filen main.c:
0
#include <stdio.h> #include "student.h" int main ( void ) { student * s = student_new ( 19 , "Maurice" ); char * namn = student_get_name ( s ); int old_age = student_get_age ( s ); printf ( "%s ålderdom = %i \n " , namn , ålderdom ); student_set_age ( s , 21 ); int new_age = student_get_age ( s ); printf ( "%ss nya ålder = %i \n " , namn , ny_ålder ); student_delete ( s ); återvända ; }
I filen Makefile:
alla : ut . txt ; cat $< out.txt : main ; ./$< > $@ main : main . o student . o huvud.o student.o : student . h ren : ; $( RM ) *. o ut . txt main
C++
I filen Student.h:
#ifndef STUDENT_H #define STUDENT_H #include <string> class Student { public : Student ( const std :: string & name ); const std :: sträng & namn () const ; void namn ( const std :: sträng & namn ); privat : std :: strängnamn_ ; _ }; #endif
I filen Student.cpp:
#include "Student.h" Student :: Student ( const std :: sträng & namn ) : name_ ( namn ) { } const std :: sträng & Student :: namn () const { return name_ ; } void Student :: namn ( const std :: sträng & namn ) { name_ = name ; }
C#
Det här exemplet illustrerar C# -idén för egenskaper , som är en speciell typ av klassmedlem . Till skillnad från Java är inga explicita metoder definierade; en offentlig "egendom" innehåller logiken för att hantera åtgärderna. Observera användningen av det inbyggda (odeklarerade) variabelvärdet
.
public class Student { privat strängnamn ; _ /// <summary> /// Hämtar eller ställer in elevens namn /// </summary> public string Namn { get { return name ; } set { namn = värde ; } } }
I senare C#-versioner (.NET Framework 3.5 och högre) kan det här exemplet förkortas enligt följande, utan att deklarera den privata variabelns namn
.
public class Student { public string Namn { get ; set ; } }
Att använda den förkortade syntaxen innebär att den underliggande variabeln inte längre är tillgänglig inifrån klassen. Som ett resultat måste den fastställda
delen av fastigheten vara närvarande för tilldelning. Åtkomsten kan begränsas med en uppsättningsspecifik
åtkomstmodifierare.
public class Student { public string Namn { get ; privat set ; } }
Vanlig Lisp
I Common Lisp Object System kan platsspecifikationer inom klassdefinitioner specificera vilket som helst av alternativen :reader
, :writer
och :accessor
(även flera gånger) för att definiera läsarmetoder, sättermetoder och åtkomstmetoder (en läsarmetod och respektive setf
-metod) . Slots är alltid direkt tillgängliga genom sina namn med hjälp av with-slots
och slot-value
, och slot-accessor-alternativen definierar specialiserade metoder som använder slot-value
.
CLOS självt har ingen uppfattning om egenskaper, även om MetaObject Protocol- tillägget anger sätt att komma åt en slots läsare och skrivarfunktionsnamn, inklusive de som genereras med alternativet : accessor .
Följande exempel visar en definition av en elevklass som använder dessa platsalternativ och direkt tillgång till plats:
0
0
( defclass student () (( namn :initarg :namn :initform "" :accessor studentnamn ) ; studentnamn är inställbart ( födelsedatum :initarg :födelsedatum :initform :läsare studentfödelsedatum ) ( nummer :initarg :nummer :initform :läsare studentnummer :författaruppsättning -studentnummer ))) ;; Exempel på en beräknad egenskapsgetter (detta är helt enkelt en metod) ( defmethod student-age (( self student )) ( - ( get-universal-time ) ( student-birthdate self ))) ;; Exempel på direkt slotåtkomst inom en beräknad egenskapsinställare ( defmethod ( setf student-age ) ( new-age ( self student )) ( with-slots ( birthdate ) self ( setf birthdate ( - ( get-universal-time ) new -age ) )) new-age )) ;; Alternativen för platsåtkomst genererar metoder, vilket tillåter ytterligare metoddefinitioner ( defmethod set-student-number :before ( new-number ( self student )) ;; Du kan också kontrollera om en student med det nya numret redan finns. ( check- skriv nytt-nummer ( heltal 1 * )))
D
D stöder en getter- och setterfunktionssyntax. I version 2 av språket bör getter och setter klass/struct-metoder ha attributet @property .
klass Student { privat char [] namn_ ; // Getter @property char [] namn () { returnera detta . namn_ ; } // Setter @property char [] name ( char [] name_in ) { returnera detta . name_ = name_in ; } }
En Student-
instans kan användas så här:
auto student = ny student ; student . name = "David" ; // samma effekt som student.name("David") auto student_name = student . namn ; // samma effekt som student.name()
Delphi
Detta är en enkel klass i Delphi-språket som illustrerar konceptet med offentlig egendom för att komma åt ett privat område.
gränssnittstyp TStudent = klass strikt privat FName : string ; _ procedure SetName ( const Value : string ) ; public /// <sammanfattning> /// Hämta eller ange namnet på eleven. /// </summary> egenskap Namn : sträng läs FName skriv SetName ; slut ; // ... genomförandeförfarande TStudent . SetName ( const Value : string ) ; börja FName := Värde ; slut ; slut .
Java
I det här exemplet på en enkel klass som representerar en elev med endast namnet lagrat, kan man se att variabelnamnet är privat, dvs endast synligt från Studentklassen, och "setter" och "getter" är offentliga, nämligen " getName ( )
" och " setName(name)
" metoder.
public class Student { private String name ; public String getName () { return name ; } public void setName ( String newName ) { name = newName ; } }
JavaScript
används konstruktor-funktion Student för att skapa objekt som representerar en student med endast namnet lagrat.
function Student ( namn ) { var _name = namn ; detta . getName = function () { return _name ; }; detta . setName = funktion ( värde ) { _name = värde ; }; }
Eller (med ett föråldrat sätt att definiera accessorer i webbläsare):
function Student ( namn ){ var _namn = namn ; detta . __defineGetter__ ( 'namn' , funktion () { retur _namn ; }); detta . __defineSetter__ ( 'namn' , funktion ( värde ) { _name = värde ; }); }
Eller (med prototyper för arv och ES6- accessorsyntax):
funktion Student ( namn ){ detta . _name = namn ; } Student . prototyp = { get name () { returnera detta . _namn ; }, ange namn ( värde ) { detta . _name = värde ; } };
Eller (utan att använda prototyper):
var Student = { get name () { returnera detta . _namn ; }, ange namn ( värde ) { detta . _name = värde ; } };
Eller (med defineProperty):
funktion Student ( namn ){ detta . _name = namn ; } Objekt . 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 ( värde : String ) : void { _name = värde ; } } }
Mål-C
Använder traditionell Objective-C 1.0-syntax, med manuell referens som räknas som den som arbetar på GNUstep på Ubuntu 12.04 :
@interface Student : NSObject { NSString * _name ; } - ( NSString * ) namn ; - ( void ) setName: ( NSString * ) name ; @end @implementation Student - ( NSString * ) name { return _name ; } - ( void ) setName: ( NSString * ) name { [ _name release ]; _name = [ namn behåller ]; } @slut
Använder nyare Objective-C 2.0-syntax som används i Mac OS X 10.6 , iOS 4 och Xcode 3.2, genererar samma kod som beskrivs ovan:
@interface Student : NSObject @property ( icke-atomic , retain ) NSString * name ; @end @implementation Student @synthesize name = _name ; @slutet
Och från och med OS X 10.8 och iOS 6 , medan du använder Xcode 4.4 och uppåt, kan syntaxen till och med förenklas:
@interface Student : NSObject @property ( icke-atomisk , stark ) NSString * name ; @end @implementation Student //Ingenting går här och det är OK. @slutet
Perl
0
paket Student ; sub new { välsigna {}, skift ; } sub set_name { my $self = shift ; $self -> { namn } = $_ [ ]; } sub get_name { my $self = shift ; returnera $self -> { namn }; } 1 ;
Eller genom att använda Class::Accessor
paket Student ; använd basen qw(Class::Accessor) ; __PACKAGE__ -> follow_best_practice ; Student -> mk_accessors ( qw(namn) ); 1 ;
Eller, med hjälp av Moose Object System :
paket Student ; använd Älg ; # Moose använder attributnamnet som sätter och getter, läsar- och skrivegenskaper # tillåter oss att åsidosätta det och tillhandahålla våra egna namn, i det här fallet har get_name och set_name 'name' => ( är => 'rw' , isa => 'Str' , läsare => 'get_name' , writer => 'set_name' ); 1 ;
PHP
PHP definierar de "magiska metoderna" __get
och __set
för egenskaper hos objekt.
I detta exempel på en enkel klass som representerar en elev med endast namnet lagrat, kan man se att variabelnamnet är privat, dvs endast synligt från Studentklassen, och "setter" och "getter" är offentliga, nämligen getName ( )
och setName('name')
metoder.
klass Student { privat sträng $namn ; /** * @return sträng Namnet. */ public function getName () : string { return $this -> name ; } /** * @param sträng $newName Namnet som ska ställas in. */ public function setName ( sträng $newName ) : void { $this -> name = $newName ; } }
Pytonorm
Det här exemplet använder en Python-klass med en variabel, en getter och en setter.
klass Elev : # Initializer def __init__ ( self , name : str ) -> None : # En instansvariabel som innehåller elevens namn self . _name = namn # Getter-metod @property def namn ( själv ): returnera själv . _name # Setter method @name . setter def namn ( själv , nytt_namn ): själv . _namn = nytt_namn
>>> bob = Student ( "Bob" ) >>> bob . namn Bob >>> bob . namn = "Alice" >>> bob . namn Alice >>> bob . _name = "Charlie" # förbi sättern >>> bob . _name # kringgå getter Charlie
Racket
I Racket är objektsystemet ett sätt att organisera kod som kommer utöver moduler och enheter. Liksom i resten av språket har objektsystemet förstklassiga värden och lexikal räckvidd används för att kontrollera tillgången till objekt och metoder.
#lang racket ( definiera elev% ( klassobjekt % ( init-field name ) ( definiera/public ( get-name ) name ) ( definiera/public ( set-name! new-name ) ( set! name new-name )) ( super-new ))) ( definiera s ( ny student% [ namn "Alice" ])) ( skicka s get-name ) ; => "Alice" ( skicka s set-name! "Bob" ) ( skicka s get-name ) ; => "Bob"
Strukturdefinitioner är ett alternativt sätt att definiera nya typer av värden, där mutatorer är närvarande när det uttryckligen krävs:
#lang racket ( struct student ( namn ) #: mutable ) ( definiera s ( elev "Alice" )) ( set-student-name! s "Bob" ) ( student-name s ) ; => "Bob"
Rubin
I Ruby kan individuella accessor- och mutatormetoder definieras, eller så kan metaprogrammeringskonstruktionerna attr_reader
eller attr_accessor
användas både för att deklarera en privat variabel i en klass och för att ge antingen lässkyddad eller läs-skriv offentlig åtkomst till den.
Att definiera individuella accessor- och mutatormetoder skapar utrymme för förbearbetning eller validering av data
klass Student def namn @namn slut def namn= ( värde ) @namn = värde slut slut
Skrivskyddad enkel offentlig åtkomst till implicit @name-
variabel
klass Student attr_reader :namn slut
Läs-skriv enkel offentlig åtkomst till implicit @name-
variabel
klass Student attr_accessor :namn slut
Rost
struct Student { name : String , } impl Student { fn name ( & self ) -> & String { & self . name } fn set_name ( & mut self , name : String ) { self . namn = namn } }
Småprat
0 age: aNumber " Ställ in mottagaråldern att vara aNumber om är större än 0 och mindre än 150 " ( aNumber mellan: och: 150 ) ifTrue: [ age := aNumber ]
Snabb
class Student { private var _name : String = "" var name : String { get { return self . _name } set { self . _name = newValue } } }
Visual Basic .NET
Det här exemplet illustrerar VB.NET-idén med egenskaper som används i klasser. I likhet med C# finns det en explicit användning av Get-
och Set
-metoderna.
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
I VB.NET 2010 kan automatiskt implementerade egenskaper användas för att skapa en egenskap utan att behöva använda syntaxen Get and Set. Observera att en dold variabel skapas av kompilatorn, kallad _name
, för att motsvara egenskapsnamnet
. Att använda en annan variabel inom klassen med namnet _name
skulle resultera i ett fel. Privilegerad åtkomst till den underliggande variabeln är tillgänglig inifrån klassen.
Public Class Student Public Property name Som String End Class