Metoda mutátoru - Mutator method

Ve vědě o počítačích , je metoda mutator je metoda používá k řízení změn proměnné. Jsou také široce známé jako metody setteru . Setter je často doprovázen getrem (také známým jako accessor ), který vrací hodnotu soukromé členské proměnné.

Metoda mutátoru se nejčastěji používá v objektově orientovaném programování , v souladu s principem zapouzdření . Podle tohoto principu, člen proměnné příslušníky třídy jsou soukromé, na kůži, a chránit je před jiným kódem, a mohou být upraveny pouze veřejné členské funkce (metoda mutator), který bere v novou požadovanou hodnotu jako parametr, případně ověřuje a upraví proměnnou soukromého člena . Metody mutátoru lze přirovnat k přetížení operátoru přiřazení, ale obvykle se objevují na různých úrovních hierarchie objektů.

Metody mutátorů lze také použít v neobjektově orientovaných prostředích. V tomto případě je mutátoru předán odkaz na proměnnou, která má být změněna, spolu s novou hodnotou. V tomto scénáři kompilátor nemůže omezit kód z obcházení metody mutátoru a přímé změny proměnné. Odpovědnost připadá vývojářům, aby zajistili, že proměnná bude upravena pouze metodou mutátoru a nebude přímo upravena.

V programovacích jazycích, které je podporují, nabízejí vlastnosti pohodlnou alternativu, aniž by se vzdaly užitečnosti zapouzdření.

V níže uvedených příkladech může plně implementovaná metoda mutátoru také ověřit vstupní data nebo provést další akci, jako je spuštění události .

Dopady

Alternativou k definování metod mutátoru a přístupového objektu nebo bloků vlastností je poskytnout proměnné instance určitou viditelnost jinou než soukromou a přistupovat k ní přímo zvenčí objektů. Mnohem jemnější kontrolu nad přístupovými právy lze definovat pomocí mutátorů a přístupových práv. Například může být parametr nastaven jen pro čtení definováním přístupového objektu, ale nikoli mutátoru. Viditelnost těchto dvou metod se může lišit; často je užitečné, aby přístupový server byl veřejný, zatímco mutátor zůstává chráněný, soukromý nebo interní.

Blok , kde je definována mutator poskytuje příležitost pro validaci nebo předběžného zpracování příchozích dat. Pokud je zaručeno, že veškerý externí přístup prochází mutátorem, nelze tyto kroky obejít. Například pokud je datum reprezentovány samostatnými soukromými year, montha dayproměnných, pak příchozí data mohou být rozděleny podle setDatemutatorem zatímco konzistence stejné soukromé instance proměnné jsou přístupné setYeara setMonth. Ve všech případech mohou být hodnoty měsíce mimo 1 - 12 odmítnuty stejným kódem.

Accessors naopak umožňují syntézu užitečných reprezentací dat z interních proměnných při zachování jejich struktury zapouzdřené a skryté před vnějšími moduly. Peněžní getAmountpřístupový modul může vytvořit řetězec z numerické proměnné s počtem desetinných míst definovaným skrytým currencyparametrem.

Moderní programovací jazyky často nabízejí schopnost generovat standardizovaný program pro mutátory a přístupové objekty v jednom řádku - například C # public string Name { get; set; }a Ruby's attr_accessor :name. V těchto případech se nevytvoří žádné bloky kódu pro ověření, předzpracování nebo syntézu. Tito zjednodušení přístupoví uživatelé si stále zachovávají výhodu zapouzdření oproti jednoduchým proměnným veřejné instance, ale je běžné, že jak postupují návrhy systému , udržuje se software a mění se požadavky, požadavky na data se stávají sofistikovanějšími. Mnoho automatických mutátorů a přistupujících zařízení bude nakonec nahrazeno samostatnými bloky kódu. Výhodou jejich automatického vytváření v počátcích implementace je, že veřejné rozhraní třídy zůstává identické bez ohledu na to, zda je nebo není přidána větší sofistikovanost, což nevyžaduje žádné rozsáhlé refaktorování, pokud je.

Manipulace s parametry, které mají mutátory a accessory zevnitř třídy, kde jsou definovány, často vyžaduje další zamyšlení. V počátcích implementace, kdy je v těchto blocích malý nebo žádný další kód, nezáleží na tom, zda je k proměnné soukromé instance přistupováno přímo nebo ne. Když se přidá validace, křížová validace , kontroly integrity dat , předzpracování nebo jiná sofistikovanost, mohou se objevit jemné chyby tam, kde některý interní přístup využívá novější kód, zatímco na jiných místech je obcházen.

Funkce přístupového modulu mohou být méně efektivní než přímé načítání nebo ukládání datových polí kvůli dalším příslušným krokům, nicméně takové funkce jsou často inline, což eliminuje režii volání funkce.

Příklady

Shromáždění

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

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

V souboru 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;
}

V souboru 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;
}

V souboru 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 ++

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

V souboru 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#

Tento příklad ilustruje C # myšlenku vlastností , které jsou zvláštním typem člena třídy . Na rozdíl od Javy nejsou definovány žádné explicitní metody; veřejná „vlastnost“ obsahuje logiku zpracování akcí. Všimněte si použití vestavěné (nedeklarované) proměnné value.

public class Student {
    private string name;

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

V pozdějších C # verzích (.NET Framework 3.5 a vyšší) může být tento příklad zkrácen takto, bez deklarace soukromé proměnné name.

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

Použití zkrácené syntaxe znamená, že podkladová proměnná již není k dispozici zevnitř třídy. Ve výsledku setmusí být pro přiřazení přítomna část vlastnosti. Přístup lze omezit pomocí setspecifického modifikátoru přístupu.

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

Společný Lisp

V Common Lisp Object System , specifikace slot v rámci definice třídy mohou specifikovat některý z :reader, :writera :accessormožnosti (i vícekrát), definovat metody čtenáři, nastavovací metody a přístupové metody (metoda čtenář a příslušná setfmetoda). Sloty jsou vždy přímo přístupné prostřednictvím jejich názvů s použitím with-slotsa slot-valuea možnosti přistupujícího slotu definují specializované metody, které používají slot-value.

Samotný CLOS nemá žádnou představu o vlastnostech, ačkoli rozšíření protokolu MetaObject určuje prostředky pro přístup k názvům funkcí čtečky a zapisovače slotů, včetně těch, které byly generovány s :accessormožností.

Následující příklad ukazuje definici třídy studentů pomocí těchto možností slotů a přímého přístupu k slotům:

(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 podporuje syntaxi funkce getter a setter. Ve verzi 2 metody getter a setter třídy / struktury by měl mít @propertyatribut.

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

StudentPříklad může být použit například takto:

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

Delphi

Toto je jednoduchá třída v jazyce Delphi, která ilustruje koncept veřejného majetku pro přístup k soukromému poli.

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.

Jáva

V tomto příkladu jednoduché třídy představující studenta pouze s uloženým jménem lze vidět, že název proměnné je soukromý, tj. Viditelný pouze ze třídy Student, a „setter“ a „getter“ jsou veřejné, konkrétně „ “ a " " metody. getName()setName(name)

public class Student {
    private String name;

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

JavaScript

V tomto příkladu se funkce konstruktoru Studentpoužívá k vytváření objektů představujících studenta s uloženým pouze jménem.

function Student(name) {
  var _name = name;

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

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

Nebo (nestandardní a zastaralé):

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

Nebo (pokud použijete prototypy pro dědičnost; ECMA-6 !):

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

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

Nebo (bez použití prototypů; ECMA-6):

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

Nebo (pokud používáte 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;
        }
    }
}

Cíl-C

Používání tradiční syntaxe Objective-C 1.0 s ručním počítáním referencí jako s tím, který pracuje na GNUstep na 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

Použití novější syntaxe Objective-C 2.0, jaká se používá v Mac OS X 10.6 , iOS 4 a Xcode 3.2, generování stejného kódu, jak je popsáno výše:

@interface Student : NSObject

@property (nonatomic, retain) NSString *name;

@end

@implementation Student

@synthesize name = _name;

@end

A počínaje OS X 10.8 a iOS 6 , při používání Xcode 4.4 a novějších , lze syntaxi ještě zjednodušit:

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

Nebo pomocí Class :: Accessor

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

Student->mk_accessors(qw(name));

1;

Nebo pomocí systému 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 definuje „magické metody“ __geta __setvlastnosti objektů.

V tomto příkladu jednoduché třídy představující studenta s uloženým pouze jménem lze vidět, že název proměnné je soukromý, tj. Viditelný pouze ze třídy Student, a „setter“ a „getter“ jsou veřejné, jmenovitě metody and . 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;
    }
}

Krajta

Tento příklad používá třídu Python s jednou proměnnou, getrem a setrem.

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

Raketa

V Racket je objektový systém způsob, jak organizovat kód, který přichází kromě modulů a jednotek. Stejně jako ve zbytku jazyka má objektový systém prvotřídní hodnoty a k řízení přístupu k objektům a metodám se používá lexikální rozsah.

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

Definice struktur jsou alternativním způsobem, jak definovat nové typy hodnot, přičemž jsou přítomny mutátory, pokud jsou výslovně požadovány:

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

Rubín

V Ruby lze definovat jednotlivé přístupové a mutátorové metody nebo metaprogramovací konstrukty attr_readernebo attr_accessorje lze použít jak k deklaraci soukromé proměnné ve třídě, tak k poskytnutí veřejného přístupu pouze pro čtení nebo pro čtení a zápis.

Definování jednotlivých přístupových a mutačních metod vytváří prostor pro předběžné zpracování nebo ověření dat

class Student
  def name
    @name
  end

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

Jednoduchý veřejný přístup jen pro čtení k implikované @nameproměnné

class Student
  attr_reader :name
end

Jednoduchý veřejný přístup pro čtení a zápis k implikované @nameproměnné

class Student
  attr_accessor :name
end

Pokec

  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 ]

Rychlý

class Student {
    private var _name: String = ""

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

Visual Basic .NET

Tento příklad ilustruje myšlenku VB.NET na vlastnosti, které se používají ve třídách. Podobně jako v C # existuje explicitní použití metod Geta Set.

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

Ve VB.NET 2010 lze automaticky implementované vlastnosti využít k vytvoření vlastnosti, aniž byste museli používat syntaxi Získat a Nastavit. Všimněte si, že kompilátor vytvoří skrytou proměnnou, která se nazývá _name, aby odpovídala vlastnosti name. Použití jiné proměnné v rámci pojmenované třídy _nameby mělo za následek chybu. Privilegovaný přístup k podkladové proměnné je k dispozici v rámci třídy.

Public Class Student
    Public Property name As String
End Class

Viz také

Reference