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.
month
day
setDate
setYear
setMonth
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 getAmount
acessador monetário pode construir uma string a partir de uma variável numérica com o número de casas decimais definido por um currency
parâ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 set
parte da propriedade deve estar presente para atribuição. O acesso pode ser restrito com um set
modificador 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
, :writer
e :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 setf
método). Os slots estão sempre diretamente acessíveis por meio de seus nomes com o uso de with-slots
e 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 :accessor
opçã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 @property
atributo.
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 Student
instâ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" __get
e as __set
propriedades 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_reader
ou attr_accessor
podem 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 à @name
variável
implícita
class Student
attr_reader :name
end
Acesso público simples de leitura e gravação à @name
variá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 Get
e 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 _name
resultaria em um erro. O acesso privilegiado à variável subjacente está disponível na classe.
Public Class Student
Public Property name As String
End Class