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

Se även