Método mutador - Mutator method

Na ciência da computação , um método modificador é um método usado para controlar as mudanças em uma variável. Eles também são amplamente conhecidos como métodos setter . Freqüentemente, um setter é acompanhado por um getter (juntos também conhecidos como acessadores ), que retorna o valor da variável de membro privado.

O método mutator é mais frequentemente usado em programação orientada a objetos , de acordo com o princípio de encapsulamento . De acordo com este princípio, as variáveis de membro de uma classe são tornadas privadas para ocultá-las e protegê-las de outro código, e só podem ser modificadas por uma função de membro pública (o método mutator), que leva o novo valor desejado como parâmetro, opcionalmente valida ele e modifica a variável de membro privado . Os métodos mutator podem ser comparados à sobrecarga do operador de atribuição , mas eles normalmente aparecem em diferentes níveis da hierarquia do objeto.

Os métodos mutator também podem ser usados ​​em ambientes não orientados a objetos. Neste caso, uma referência à variável a ser modificada é passada ao modificador, junto com o novo valor. Nesse cenário, o compilador não pode impedir o código de ignorar o método modificador e alterar a variável diretamente. A responsabilidade recai sobre os desenvolvedores para garantir que a variável seja modificada apenas por meio do método modificador e não diretamente modificada.

Em linguagens de programação que as suportam, as propriedades oferecem uma alternativa conveniente sem abrir mão da utilidade do encapsulamento.

Nos exemplos abaixo, um método mutador totalmente implementado também pode validar os dados de entrada ou realizar outras ações, como disparar um evento .

Implicações

A alternativa para definir métodos modificadores e acessadores, ou blocos de propriedade , é dar à variável de instância alguma visibilidade diferente de privada e acessá-la diretamente de fora dos objetos. Um controle muito mais preciso dos direitos de acesso pode ser definido usando modificadores e acessadores. Por exemplo, um parâmetro pode ser tornado somente leitura simplesmente definindo um acessador, mas não um modificador. A visibilidade dos dois métodos pode ser diferente; geralmente é útil para o acessador ser público enquanto o modificador permanece protegido, privado do pacote ou interno.

O bloco onde o modificador é definido oferece uma oportunidade para validação ou pré-processamento dos dados recebidos. Se for garantido que todo o acesso externo venha pelo modificador, essas etapas não podem ser ignoradas. Por exemplo, se uma data é representada por variáveis privadas e separadas year, as datas de entrada podem ser divididas pelo modificador, enquanto para consistência as mesmas variáveis ​​de instância privadas são acessadas por e . Em todos os casos, os valores do mês fora de 1 - 12 podem ser rejeitados pelo mesmo código. monthdaysetDatesetYearsetMonth

Os acessadores, por outro lado, permitem a síntese de representações de dados úteis de variáveis ​​internas, enquanto mantêm sua estrutura encapsulada e oculta de módulos externos. Um getAmountacessador monetário pode construir uma string a partir de uma variável numérica com o número de casas decimais definido por um currencyparâmetro oculto .

Linguagens de programação modernas geralmente oferecem a capacidade de gerar o clichê para modificadores e acessadores em uma única linha - como por exemplo C # public string Name { get; set; }e Ruby attr_accessor :name. Nestes casos, nenhum bloco de código é criado para validação, pré-processamento ou síntese. Esses acessadores simplificados ainda mantêm a vantagem do encapsulamento sobre as variáveis ​​de instância pública simples, mas é comum que, à medida que os projetos do sistema progridem , o software é mantido e os requisitos mudam, as demandas sobre os dados se tornam mais sofisticadas. Muitos modificadores e acessores automáticos são eventualmente substituídos por blocos de código separados. O benefício de criá-los automaticamente nos primeiros dias da implementação é que a interface pública da classe permanece idêntica, independentemente de ser adicionada maior sofisticação ou não, não exigindo refatoração extensa se for o caso.

A manipulação de parâmetros que possuem modificadores e acessadores de dentro da classe onde são definidos geralmente requer algum pensamento adicional. Nos primeiros dias de uma implementação, quando há pouco ou nenhum código adicional nesses blocos, não faz diferença se a variável de instância privada é acessada diretamente ou não. Conforme a validação, validação cruzada , verificações de integridade de dados , pré-processamento ou outra sofisticação são adicionadas, bugs sutis podem aparecer onde algum acesso interno faz uso do código mais recente enquanto em outros lugares ele é contornado.

As funções de acesso podem ser menos eficientes do que buscar ou armazenar diretamente campos de dados devido às etapas extras envolvidas, no entanto, essas funções são frequentemente embutidas, o que elimina a sobrecarga de uma chamada de função.

Exemplos

conjunto

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

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

No arquivo 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;
}

No arquivo 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;
}

No arquivo 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 ++

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

No arquivo 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 #

Este exemplo ilustra a ideia C # de propriedades , que são um tipo especial de membro de classe . Ao contrário do Java, nenhum método explícito é definido; uma 'propriedade' pública contém a lógica para lidar com as ações. Observe o uso da variável embutida (não declarada) value.

public class Student {
    private string name;

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

Em versões posteriores do C # (.NET Framework 3.5 e superior), este exemplo pode ser abreviado da seguinte maneira, sem declarar a variável privada name.

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

Usar a sintaxe abreviada significa que a variável subjacente não está mais disponível dentro da classe. Como resultado, a setparte da propriedade deve estar presente para atribuição. O acesso pode ser restrito com um setmodificador de acesso específico.

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

Lisp Comum

No Common Lisp Object System , as especificações de slot dentro das definições de classe podem especificar qualquer uma das opções :reader, :writere :accessor(mesmo várias vezes) para definir métodos de leitor, métodos de setter e métodos de acessador (um método de leitor e o respectivo setfmétodo). Os slots estão sempre diretamente acessíveis por meio de seus nomes com o uso de with-slotse slot-value, e as opções do acessador de slots definem métodos especializados que usam slot-value.

O próprio CLOS não tem noção de propriedades, embora a extensão do protocolo MetaObject especifique meios para acessar os nomes das funções de leitor e gravador de um slot, incluindo aqueles gerados com a :accessoropção.

O exemplo a seguir mostra a definição de uma classe de aluno usando essas opções de slot e acesso direto ao slot:

(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 suporta uma sintaxe de função getter e setter. Na versão 2 da linguagem os métodos getter e setter class / struct devem ter o @propertyatributo.

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

Uma Studentinstância pode ser usada assim:

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

Delphi

Esta é uma classe simples na linguagem Delphi que ilustra o conceito de propriedade pública para acessar um campo privado.

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

Neste exemplo de uma classe simples que representa um aluno com apenas o nome armazenado, pode-se ver que o nome da variável é privado, ou seja, apenas visível na classe Aluno, e o "setter" e "getter" são públicos, ou seja, o " " e métodos " ". getName()setName(name)

public class Student {
    private String name;

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

JavaScript

Neste exemplo, a função construtora Studenté usada para criar objetos que representam um aluno com apenas o nome armazenado.

function Student(name) {
  var _name = name;

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

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

Ou (não padronizado e obsoleto):

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

Ou (se usar protótipos para herança; ECMA-6 !):

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

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

Ou (sem usar protótipos; ECMA-6):

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

Ou (se estiver usando 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;
        }
    }
}

Objective-C

Usando a sintaxe Objective-C 1.0 tradicional, com referência manual contando como a que funciona no GNUstep no 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

Usando a sintaxe Objective-C 2.0 mais recente, conforme usada no Mac OS X 10.6 , iOS 4 e Xcode 3.2, gerando o mesmo código descrito acima:

@interface Student : NSObject

@property (nonatomic, retain) NSString *name;

@end

@implementation Student

@synthesize name = _name;

@end

E a partir do OS X 10.8 e iOS 6 , ao usar o Xcode 4.4 e superior, a sintaxe pode ser ainda simplificada:

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

Ou, usando Class :: Accessor

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

Student->mk_accessors(qw(name));

1;

Ou, usando o 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 define os "métodos mágicos" __gete as __setpropriedades dos objetos.

Neste exemplo de uma classe simples que representa um aluno com apenas o nome armazenado, pode-se ver que o nome da variável é privado, ou seja, apenas visível na classe Aluno, e o "setter" e o "getter" são públicos, ou seja, os métodos e . 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;
    }
}

Pitão

Este exemplo usa uma classe Python com uma variável, um getter e um 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

Raquete

No Racket , o sistema de objetos é uma forma de organizar o código que vem junto com os módulos e unidades. Como no resto da linguagem, o sistema de objetos tem valores de primeira classe e o escopo léxico é usado para controlar o acesso a objetos e métodos.

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

As definições de estrutura são uma forma alternativa de definir novos tipos de valores, com modificadores presentes quando explicitamente necessários:

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

Rubi

Em Ruby , métodos acessadores e mutadores individuais podem ser definidos, ou as construções de metaprogramação, attr_readerou attr_accessorpodem ser usados ​​para declarar uma variável privada em uma classe e fornecer acesso público somente leitura ou leitura-escrita a ela, respectivamente.

A definição de acessadores individuais e métodos modificadores cria espaço para pré-processamento ou validação dos dados

class Student
  def name
    @name
  end

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

Acesso público simples somente leitura à @namevariável implícita

class Student
  attr_reader :name
end

Acesso público simples de leitura e gravação à @namevariável implícita

class Student
  attr_accessor :name
end

Conversa fiada

  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 ]

Rápido

class Student {
    private var _name: String = ""

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

Visual Basic .NET

Este exemplo ilustra a ideia VB.NET de propriedades, que são usadas em classes. Semelhante ao C #, há um uso explícito dos métodos Gete 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

No VB.NET 2010, as propriedades implementadas automaticamente podem ser utilizadas para criar uma propriedade sem a necessidade de usar a sintaxe Get e Set. Observe que uma variável oculta é criada pelo compilador, chamada _name, para corresponder à propriedade name. Usar outra variável dentro da classe nomeada _nameresultaria em um erro. O acesso privilegiado à variável subjacente está disponível na classe.

Public Class Student
    Public Property name As String
End Class

Veja também

Referências