Mutator methode - Mutator method

In de informatica is een mutatormethode een methode die wordt gebruikt om wijzigingen in een variabele te controleren. Ze zijn ook algemeen bekend als setter- methoden. Vaak gaat een setter vergezeld van een getter (samen ook wel accessors genoemd ), die de waarde van de private lidvariabele retourneert.

De mutatormethode wordt het meest gebruikt in objectgeoriënteerd programmeren , in overeenstemming met het principe van inkapseling . Volgens dit principe worden lidvariabelen van een klasse privé gemaakt om ze te verbergen en te beschermen tegen andere code, en kunnen ze alleen worden gewijzigd door een openbare lidfunctie (de mutatormethode), die de gewenste nieuwe waarde als parameter neemt, optioneel valideert it, en wijzigt de private member variabele . Mutatormethoden kunnen worden vergeleken met overbelasting van de toewijzingsoperator , maar ze verschijnen meestal op verschillende niveaus van de objecthiërarchie.

Mutatormethoden kunnen ook worden gebruikt in niet-objectgeoriënteerde omgevingen. In dit geval wordt een verwijzing naar de te wijzigen variabele samen met de nieuwe waarde doorgegeven aan de mutator. In dit scenario kan de compiler niet voorkomen dat code de mutatormethode overslaat en de variabele rechtstreeks wijzigt. De verantwoordelijkheid ligt bij de ontwikkelaars om ervoor te zorgen dat de variabele alleen wordt gewijzigd via de mutatormethode en niet rechtstreeks wordt gewijzigd.

In programmeertalen die ze ondersteunen, bieden eigenschappen een handig alternatief zonder het nut van inkapseling op te geven.

In de onderstaande voorbeelden kan een volledig geïmplementeerde mutatormethode ook de invoergegevens valideren of verdere actie ondernemen, zoals het activeren van een gebeurtenis .

Implicaties

Het alternatief voor het definiëren van mutator- en accessor-methoden, of eigenschapsblokken , is om de instantievariabele een andere zichtbaarheid te geven dan privé en deze direct van buiten de objecten te benaderen. Een veel fijnere controle van toegangsrechten kan worden gedefinieerd met behulp van mutators en accessors. Een parameter kan bijvoorbeeld alleen-lezen worden gemaakt door simpelweg een accessor te definiëren, maar geen mutator. De zichtbaarheid van de twee methoden kan verschillen; het is vaak handig dat de accessor openbaar is, terwijl de mutator beschermd blijft, pakket-privé of intern.

Het blok waarin de mutator is gedefinieerd, biedt de mogelijkheid voor validatie of voorverwerking van binnenkomende gegevens. Als alle externe toegang gegarandeerd via de mutator verloopt, kunnen deze stappen niet worden overgeslagen. Als een datum bijvoorbeeld wordt weergegeven door afzonderlijke private year, monthen dayvariabelen, dan kunnen inkomende datums worden gesplitst door de setDatemutator, terwijl voor consistentie dezelfde variabelen van de privéinstantie worden benaderd door setYearen setMonth. In alle gevallen kunnen maandwaarden buiten 1 - 12 door dezelfde code worden afgewezen.

Accessors daarentegen zorgen voor synthese van bruikbare gegevensrepresentaties van interne variabelen, terwijl hun structuur ingekapseld en verborgen blijft voor externe modules. Een monetaire getAmountaccessor kan een string samenstellen uit een numerieke variabele met het aantal decimalen gedefinieerd door een verborgen currencyparameter.

Moderne programmeertalen bieden vaak de mogelijkheid om de standaardtekst voor mutators en accessors in een enkele regel te genereren, zoals bijvoorbeeld C#'s public string Name { get; set; }en Ruby's attr_accessor :name. In deze gevallen worden er geen codeblokken gemaakt voor validatie, voorbewerking of synthese. Deze vereenvoudigde accessors behouden nog steeds het voordeel van inkapseling ten opzichte van eenvoudige openbare instantievariabelen, maar het is gebruikelijk dat, naarmate het systeemontwerp vordert , de software wordt onderhouden en de vereisten veranderen, de eisen aan de gegevens geavanceerder worden. Veel automatische mutators en accessors worden uiteindelijk vervangen door afzonderlijke codeblokken. Het voordeel van het automatisch maken ervan in de begindagen van de implementatie is dat de openbare interface van de klasse identiek blijft, ongeacht of er meer verfijning wordt toegevoegd of niet, en indien dit het geval is, is er geen uitgebreide refactoring vereist.

Het manipuleren van parameters die mutators en accessors hebben van binnen de klasse waar ze zijn gedefinieerd, vereist vaak wat extra aandacht. In de begindagen van een implementatie, wanneer er weinig of geen extra code in deze blokken zit, maakt het geen verschil of de private instantievariabele direct toegankelijk is of niet. Naarmate validatie, kruisvalidatie , gegevensintegriteitscontroles , voorverwerking of andere verfijning wordt toegevoegd, kunnen er subtiele bugs optreden waarbij interne toegang gebruik maakt van de nieuwere code terwijl deze op andere plaatsen wordt omzeild.

Accessorfuncties kunnen vanwege de extra stappen minder efficiënt zijn dan het direct ophalen of opslaan van gegevensvelden, maar dergelijke functies zijn vaak inline , waardoor de overhead van een functieaanroep wordt geëlimineerd.

Voorbeelden

samenkomst

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

Dit voorbeeld illustreert het C# -idee van eigenschappen , die een speciaal type klasselid zijn . In tegenstelling tot Java zijn er geen expliciete methoden gedefinieerd; een publieke 'property' bevat de logica om de acties af te handelen. Let op het gebruik van de ingebouwde (niet-aangegeven) variabele 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 latere C#-versies (.NET Framework 3.5 en hoger) kan dit voorbeeld als volgt worden afgekort, zonder de private variabele te declareren name.

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

Het gebruik van de verkorte syntaxis betekent dat de onderliggende variabele niet langer beschikbaar is vanuit de klasse. Als gevolg hiervan moet het setdeel van de woning aanwezig zijn voor toewijzing. Toegang kan worden beperkt met een set-specifieke toegangsmodifier.

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

Gemeenschappelijke Lisp

In Common Lisp Object System , kunnen slotspecificaties binnen klassedefinities elk van de :reader, :writeren :accessoropties specificeren (zelfs meerdere keren) om lezermethoden, settermethoden en accessormethoden (een lezermethode en de respectieve setfmethode) te definiëren . Slots zijn altijd direct toegankelijk via hun naam met het gebruik van with-slotsen slot-value, en de slotaccessor-opties definiëren gespecialiseerde methoden die gebruikmaken van slot-value.

CLOS zelf heeft geen notie van eigenschappen, hoewel de MetaObject Protocol- extensie middelen specificeert om toegang te krijgen tot de lezer- en schrijverfunctienamen van een slot, inclusief degenen die met de :accessoroptie worden gegenereerd .

Het volgende voorbeeld toont een definitie van een studentenklas met behulp van deze slotopties en directe toegang tot slots:

(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 *)))

NS

D ondersteunt een getter- en setter-functiesyntaxis. In versie 2 van de taal getter en setter class/struct methoden zouden het @propertyattribuut moeten hebben .

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

Een Studentinstantie kan als volgt worden gebruikt:

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

Delphi

Dit is een eenvoudige les in Delphi-taal die het concept van openbaar eigendom illustreert voor toegang tot een privéveld.

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 dit voorbeeld van een eenvoudige klas die een leerling vertegenwoordigt met alleen de naam opgeslagen, kan men zien dat de naam van de variabele privé is, dwz alleen zichtbaar voor de klas leerling, en dat de "setter" en "getter" openbaar zijn, namelijk de " " en " " methoden. getName()setName(name)

public class Student {
    private String name;

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

JavaScript

In dit voorbeeld wordt de constructor-functie Studentgebruikt om objecten te maken die een student vertegenwoordigen met alleen de naam opgeslagen.

function Student(name) {
  var _name = name;

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

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

Of (niet-standaard en verouderd):

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

Of (bij gebruik van prototypes voor overerving; ECMA-6 !):

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

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

Of (zonder gebruik te maken van prototypes; ECMA-6):

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

Of (bij gebruik van 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;
        }
    }
}

Doelstelling C

Traditionele Objective-C 1.0-syntaxis gebruiken, met handmatige referentietelling als degene die werkt aan GNUstep op 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

Met de nieuwere Objective-C 2.0-syntaxis zoals gebruikt in Mac OS X 10.6 , iOS 4 en Xcode 3.2, wordt dezelfde code gegenereerd als hierboven beschreven:

@interface Student : NSObject

@property (nonatomic, retain) NSString *name;

@end

@implementation Student

@synthesize name = _name;

@end

En beginnend met OS X 10.8 en iOS 6 , terwijl Xcode 4.4 en hoger wordt gebruikt, kan de syntaxis zelfs worden vereenvoudigd:

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

Of, met behulp van Class::Accessor

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

Student->mk_accessors(qw(name));

1;

Of, met behulp van het 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 definieert de "magische methoden" __geten __setvoor eigenschappen van objecten.

In dit voorbeeld van een eenvoudige klasse die een student vertegenwoordigt met alleen de naam opgeslagen, kan men zien dat de naam van de variabele privé is, dwz alleen zichtbaar vanuit de klasse Student, en de "setter" en "getter" zijn openbaar, namelijk de en methoden. 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

In dit voorbeeld wordt een Python-klasse gebruikt met één variabele, een getter en een setter.

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

In Racket is het objectsysteem een ​​manier om code te organiseren die naast modules en eenheden wordt geleverd. Net als in de rest van de taal heeft het objectsysteem eersteklas waarden en wordt lexicale reikwijdte gebruikt om de toegang tot objecten en methoden te regelen.

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

Structuurdefinities zijn een alternatieve manier om nieuwe soorten waarden te definiëren, waarbij mutators aanwezig zijn wanneer dit expliciet vereist is:

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

Robijn

In Ruby kunnen individuele accessor- en mutatormethoden worden gedefinieerd, of de metaprogrammeringsconstructies attr_readerof attr_accessorkunnen zowel worden gebruikt om een ​​privévariabele in een klasse te declareren als om respectievelijk alleen-lezen of lezen-schrijven openbare toegang te verschaffen.

Het definiëren van individuele accessor- en mutatormethoden schept ruimte voor voorbewerking of validatie van de gegevens

class Student
  def name
    @name
  end

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

Alleen-lezen eenvoudige openbare toegang tot impliciete @namevariabele

class Student
  attr_reader :name
end

Lees-schrijf eenvoudige openbare toegang tot impliciete @namevariabele

class Student
  attr_accessor :name
end

Koetjes en kalfjes

  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 ]

Snel

class Student {
    private var _name: String = ""

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

Visual Basic .NET

Dit voorbeeld illustreert het VB.NET-idee van eigenschappen die in klassen worden gebruikt. Net als bij C# is er een expliciet gebruik van de Geten Setmethoden.

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 kunnen automatisch geïmplementeerde eigenschappen worden gebruikt om een ​​eigenschap te maken zonder de syntaxis Get en Set te hoeven gebruiken. Merk op dat een verborgen variabele wordt gemaakt door de compiler, genaamd _name, om overeen te komen met de eigenschap name. Het gebruik van een andere variabele binnen de genoemde klasse _namezou resulteren in een fout. Bevoorrechte toegang tot de onderliggende variabele is beschikbaar vanuit de klas.

Public Class Student
    Public Property name As String
End Class

Zie ook

Referenties