En informática , un método mutador es un método utilizado para controlar los cambios en una variable. También se conocen ampliamente como métodos setter . A menudo, un setter va acompañado de un getter , que devuelve el valor de la variable miembro privada. También se conocen colectivamente como métodos de acceso .
El método mutador se utiliza con mayor frecuencia en la programación orientada a objetos , de acuerdo con el principio de encapsulación . Según este principio, las variables miembro de una clase se hacen privadas para ocultarlas y protegerlas de otro código, y solo pueden ser modificadas por una función miembro pública (el método mutador), que toma el nuevo valor deseado como parámetro, lo valida opcionalmente y modifica la variable miembro privada . Los métodos mutadores se pueden comparar con la sobrecarga del operador de asignación , pero normalmente aparecen en diferentes niveles de la jerarquía de objetos.
Los métodos mutadores también se pueden utilizar en entornos no orientados a objetos. En este caso, se pasa al mutador una referencia a la variable que se va a modificar, junto con el nuevo valor. En este escenario, el compilador no puede impedir que el código omita el método mutador y cambie la variable directamente. Los desarrolladores son responsables de garantizar que la variable solo se modifique a través del método mutador y no directamente.
En los lenguajes de programación que los admiten, las propiedades ofrecen una alternativa conveniente sin renunciar a la utilidad de la encapsulación.
En los ejemplos siguientes, un método mutador completamente implementado también puede validar los datos de entrada o realizar otras acciones, como activar un evento .
La alternativa a definir métodos de acceso y mutadores, o bloques de propiedades , es dar a la variable de instancia alguna visibilidad que no sea privada y acceder a ella directamente desde fuera de los objetos. Se puede definir un control mucho más preciso de los derechos de acceso utilizando mutadores y descriptores de acceso. Por ejemplo, un parámetro puede convertirse en de solo lectura simplemente definiendo un descriptor de acceso pero no un mutador. La visibilidad de los dos métodos puede ser diferente; a menudo es útil que el descriptor de acceso sea público mientras que el mutador permanece protegido, privado del paquete o interno.
El bloque donde se define el mutador brinda una oportunidad para la validación o el preprocesamiento de los datos entrantes. Si se garantiza que todo el acceso externo se realizará a través del mutador, estos pasos no se pueden omitir. Por ejemplo, si una fecha está representada por variables privadas y separadas year
, month
entonces day
el mutador puede dividir las fechas entrantes mientras que, para mantener la coherencia, y setDate
acceden a las mismas variables de instancia privadas . En todos los casos, los valores de mes que no estén entre 1 y 12 pueden ser rechazados por el mismo código.setYear
setMonth
Los descriptores de acceso, por el contrario, permiten la síntesis de representaciones de datos útiles a partir de variables internas, manteniendo su estructura encapsulada y oculta a los módulos externos. Un getAmount
descriptor de acceso monetario puede construir una cadena a partir de una variable numérica con la cantidad de decimales definida por un currency
parámetro oculto.
Los lenguajes de programación modernos suelen ofrecer la posibilidad de generar el código fuente de los mutadores y los descriptores de acceso en una sola línea, como por ejemplo C# public string Name { get; set; }
y Ruby attr_accessor :name
. En estos casos, no se crean bloques de código para la validación, el preprocesamiento o la síntesis. Estos descriptores de acceso simplificados aún conservan la ventaja de la encapsulación sobre las variables de instancia públicas simples, pero es común que, a medida que avanzan los diseños de sistemas , se mantiene el software y cambian los requisitos, las demandas sobre los datos se vuelvan más sofisticadas. Muchos mutadores y descriptores de acceso automáticos eventualmente son reemplazados por bloques de código separados. El beneficio de crearlos automáticamente en los primeros días de la implementación es que la interfaz pública de la clase permanece idéntica independientemente de si se agrega o no mayor sofisticación, sin requerir una refactorización extensa si se agrega. [1]
La manipulación de parámetros que tienen mutadores y descriptores de acceso desde dentro de la clase donde están definidos a menudo requiere un poco de reflexión adicional. En las primeras etapas de una implementación, cuando hay poco o ningún código adicional en estos bloques, no importa si se accede directamente a la variable de instancia privada o no. A medida que se agregan validaciones, validaciones cruzadas , verificaciones de integridad de datos , preprocesamiento u otra sofisticación, pueden aparecer errores sutiles en los que algún acceso interno hace uso del código más nuevo mientras que en otros lugares se lo omite.
Las funciones de acceso pueden ser menos eficientes que obtener o almacenar directamente los campos de datos debido a los pasos adicionales involucrados, [2] sin embargo, dichas funciones a menudo están en línea, lo que elimina la sobrecarga de una llamada de función.
¿ La estructura del estudiante tiene edad dd ? ¿El estudiante termina ?
.código estudiante_obtener_edad proc objeto : DWORD mov ebx , objeto mov eax , estudiante.edad [ ebx ] ret estudiante_obtener_edad endp estudiante_establecer_edad proc objeto : DWORD , edad : DWORD mov ebx , objeto mov eax , edad mov estudiante.edad [ ebx ], eax ret estudiante_establecer_edad endp
En el archivo student.h:
#ifndef _ESTUDIANTE_H #define _ESTUDIANTE_Hstruct student ; /* estructura opaca */ typedef struct student student ; estudiante * estudiante_nuevo ( int edad , char * nombre ); void estudiante_eliminar ( estudiante * s ); void estudiante_establecer_edad ( estudiante * s , int edad ); int estudiante_obtener_edad ( estudiante * s ); char * estudiante_obtener_nombre ( estudiante * s ); #finsi
En el archivo student.c:
#include <stdlib.h> #include <string.h> #include "estudiante.h" struct estudiante { int edad ; char * nombre ; }; estudiante * estudiante_nuevo ( int edad , char * nombre ) { estudiante * s = malloc ( sizeof ( estudiante )); s -> nombre = strdup ( nombre ); s -> edad = edad ; return s ; } void student_delete ( estudiante * s ) { free ( s -> nombre ); free ( s ); } void estudiante_conjunto_edad ( estudiante * s , int edad ) { s -> edad = edad ; } int student_get_age ( student * s ) { return s -> edad ; } char * estudiante_obtener_nombre ( estudiante * s ) { return s -> nombre ; }
En el archivo main.c:
#include <stdio.h> #include "estudiante.h" int main ( void ) { estudiante * s = estudiante_nuevo ( 19 , "Mauricio" ); char * nombre = estudiante_obtener_nombre ( s ); int edad_vieja = estudiante_obtener_edad ( s ); printf ( "edad vieja de %s = %i \n " , nombre , edad_vieja ); estudiante_establecer_edad ( s , 21 ); int edad_nueva = estudiante_obtener_edad ( s ); printf ( "edad nueva de %s = %i \n " , nombre , edad_nueva ); estudiante_eliminar ( s ); return 0 ; }
En el archivo Makefile:
all : out . txt ; cat $< out.txt : principal ; ./$< > $@ principal : principal . o estudiante . o principal.o estudiante.o : estudiante . h clean : ; $( RM ) *. o out . txt principal
En el archivo Student.h:
#ifndef ESTUDIANTE_H #define ESTUDIANTE_H#include <cadena> clase Estudiante { público : Estudiante ( const std :: string & nombre ); const std :: string & nombre () const ; void nombre ( const std :: string & nombre ); privado : std :: cadena nombre_ ; }; #finsi
En el archivo Student.cpp:
#include "Estudiante.h" Estudiante :: Estudiante ( const std :: string & nombre ) : nombre_ ( nombre ) { } const std :: string & Estudiante :: nombre () const { return nombre_ ; } void Estudiante :: nombre ( const std :: string & nombre ) { nombre_ = nombre ; }
Este ejemplo ilustra la idea de C# de las propiedades , que son un tipo especial de miembro de clase . A diferencia de Java, no se definen métodos explícitos; una "propiedad" pública contiene la lógica para manejar las acciones. Observe el uso de la variable incorporada (no declarada) value
.
clase pública Estudiante { cadena privada nombre ; /// <summary> /// Obtiene o establece el nombre del estudiante /// </summary> public string Name { get { return name ; } set { name = value ; } } }
En versiones posteriores de C# (.NET Framework 3.5 y superiores), este ejemplo se puede abreviar de la siguiente manera, sin declarar la variable privada name
.
clase pública Estudiante { cadena pública Nombre { obtener ; establecer ; } }
El uso de la sintaxis abreviada significa que la variable subyacente ya no está disponible desde dentro de la clase. Como resultado, la set
parte de la propiedad debe estar presente para la asignación. El acceso se puede restringir con un set
modificador de acceso específico.
clase pública Estudiante { cadena pública Nombre { obtener ; conjunto privado ; } }
En Common Lisp Object System , las especificaciones de ranuras dentro de las definiciones de clase pueden especificar cualquiera de las :reader
opciones :writer
y :accessor
(incluso varias veces) para definir métodos de lectura, métodos de establecimiento y métodos de acceso (un método de lectura y el setf
método respectivo). [3] Las ranuras siempre son directamente accesibles a través de sus nombres con el uso de with-slots
y slot-value
, y las opciones de acceso de ranura definen métodos especializados que usan slot-value
. [4]
CLOS en sí no tiene noción de propiedades, aunque la extensión del Protocolo MetaObject especifica medios para acceder a los nombres de funciones de lectura y escritura de una ranura, incluidos los generados con la :accessor
opción. [5]
El siguiente ejemplo muestra una definición de una clase de estudiante utilizando estas opciones de espacio y acceso directo al espacio:
( defclass student () (( nombre :initarg :name :initform "" :accessor nombre-estudiante ) ; nombre-estudiante se puede configurar ( fecha-nacimiento :initarg :birthdate :initform 0 :reader fecha-nacimiento-estudiante ) ( número :initarg :number :initform 0 :reader número-estudiante :writer número-estudiante establecido ))) ;; Ejemplo de un captador de propiedad calculado (este es simplemente un método) ( defmethod student-age (( self student )) ( - ( get-universal-time ) ( student-birthdate self ))) ;; Ejemplo de acceso directo a una ranura dentro de un establecedor de propiedades calculado ( defmethod ( setf student-age ) ( new-age ( self student )) ( with-slots ( birthdate ) self ( setf birthdate ( - ( get-universal-time ) new-age )) new-age )) ;; Las opciones de acceso a la ranura generan métodos, lo que permite más definiciones de métodos ( defmethod set-student-number :before ( new-number ( self student )) ;; También puede verificar si ya existe un estudiante con el nuevo número. ( check-type new-number ( entire 1 * )))
D admite una sintaxis de función de obtención y definición. En la versión 2 del lenguaje, los métodos de clase/estructura de obtención y definición deben tener el @property
atributo . [6] [7]
clase Estudiante { char privado [] nombre_ ; // Getter @property char [] nombre () { devuelve este . nombre_ ; } // Setter @property char [] nombre ( char [] nombre_in ) { devuelve este . nombre_ = nombre_in ; } }
Una Student
instancia se puede utilizar de la siguiente manera:
auto estudiante = new Estudiante ; estudiante . nombre = "David" ; // mismo efecto que estudiante. nombre("David") auto estudiante_nombre = estudiante . nombre ; // mismo efecto que estudiante. nombre()
Esta es una clase simple en lenguaje Delphi que ilustra el concepto de propiedad pública para acceder a un campo privado.
interfaztipo TStudent = clase estricta privada FName : cadena ; procedimiento SetName ( const Valor : cadena ) ; público /// <summary> /// Obtener o establecer el nombre del estudiante. /// </summary> propiedad Nombre : cadena leer FName escribir SetName ; fin ; // ...implementaciónprocedimiento TStudent .SetName ( const Value : string ) ; inicio FName : = Value ; fin ; fin .
En este ejemplo de una clase simple que representa a un estudiante con solo el nombre almacenado, se puede ver que la variable nombre es privada, es decir, solo visible desde la clase Estudiante, y el "setter" y el "getter" son públicos, es decir, los métodos " getName()
" y " setName(name)
".
clase pública Estudiante { cadena privada nombre ; public String getName () { return nombre ; } public void setName ( String nuevoNombre ) { nombre = nuevoNombre ; } }
En este ejemplo, se utiliza la función constructora Student
para crear objetos que representan a un estudiante con solo el nombre almacenado.
función Estudiante ( nombre ) { var _nombre = nombre ; esto .getName = función ( ) { return _name ; }; este .setName = función ( valor ) { _name = valor ; } ; }
O (utilizando una forma obsoleta de definir accesores en navegadores web): [8]
función Estudiante ( nombre ){ var _nombre = nombre ; this .__ defineGetter__ ( 'nombre' , función () { return _nombre ; }); this .__ defineSetter__ ( 'nombre' , función ( valor ) { _nombre = valor ; }); }
O (usando prototipos para herencia y sintaxis de acceso ES6 ):
función Estudiante ( nombre ){ this . _nombre = nombre ; } Estudiante . prototipo = { obtener nombre () { devolver este . _nombre ; }, establecer nombre ( valor ) { este . _nombre = valor ; } };
O (sin utilizar prototipos):
var Estudiante = { obtener nombre () { devolver este . _nombre ; }, establecer nombre ( valor ) { este . _nombre = valor ; } };
O (usando defineProperty):
función Estudiante ( nombre ){ this . _nombre = nombre ; } Objeto . defineProperty ( Estudiante . prototipo , 'nombre' , { obtener : función () { devolver this . _nombre ; }, establecer : función ( valor ) { this . _nombre = valor ; } });
paquete { clase pública Estudiante { var privada _nombre : String ; función pública obtener nombre () : String { return _nombre ; } función pública conjunto nombre ( valor : String ) : void { _name = valor ; } } }
Utilizando la sintaxis tradicional de Objective-C 1.0, con referencia manual contando como la que funciona en GNUstep en Ubuntu 12.04 :
@interface Estudiante : NSObject { NSString * _name ; } - ( NSString * ) nombre ; - ( void ) setName: ( NSString * ) nombre ; @fin @Estudiante de implementación- ( NSString * ) nombre { return _nombre ; } - ( void ) setName: ( NSString * ) nombre { [ _nombre liberación ]; _nombre = [ nombre retención ]; } @fin
Utilizando la sintaxis más reciente de Objective-C 2.0 utilizada en Mac OS X 10.6 , iOS 4 y Xcode 3.2, generando el mismo código que se describe arriba:
@interface Estudiante : NSObject@property ( no atómico , retener ) NSString * nombre ; @fin @Estudiante de implementación@synthesize nombre = _nombre ; @fin
Y a partir de OS X 10.8 y iOS 6 , al utilizar Xcode 4.4 y versiones posteriores, la sintaxis se puede simplificar aún más:
@interface Estudiante : NSObject@property ( no atómico , fuerte ) NSString * nombre ; @fin @Estudiante de implementación//Aquí no pasa nada y está bien.@fin
paquete Estudiante ; sub nuevo { bendecir {}, shift ; } sub set_name { mi $self = shift ; $self -> { nombre } = $_ [ 0 ]; } sub get_name { my $self = shift ; return $self -> { nombre }; } 1 ;
O bien, utilizando Class::Accessor
paquete Estudiante ; usar base qw(Class::Accessor) ; __PAQUETE__ -> seguir_las_mejores_prácticas ; Estudiante -> mk_accessors ( qw(nombre) );1 ;
O bien, utilizando el sistema de objetos Moose :
paquete Estudiante ; utilizar Moose ; # Moose usa el nombre del atributo como el definidor y el captador, las propiedades del lector y del escritor # nos permiten anular eso y proporcionar nuestros propios nombres, en este caso get_name y set_name tienen 'name' => ( is => 'rw' , isa => 'Str' , reader => 'get_name' , writer => 'set_name' ); 1 ;
PHP define los "métodos mágicos" __get
y __set
las propiedades de los objetos. [9]
En este ejemplo de una clase simple que representa a un estudiante con solo el nombre almacenado, se puede ver que la variable nombre es privada, es decir, solo visible desde la clase Estudiante, y el "setter" y "getter" son públicos, es decir, los métodos getName()
y setName('name')
.
clase Estudiante { cadena privada $nombre ; /** * @return string El nombre. */ public function getName () : string { return $this -> nombre ; } /** * @param string $newName El nombre a establecer. */ public function setName ( string $newName ) : void { $this -> name = $newName ; } }
Este ejemplo utiliza una clase Python con una variable, un captador y un definidor.
clase Estudiante : # Inicializador def __init__ ( self , name : str ) -> None : # Una variable de instancia para guardar el nombre del estudiante self._name = name # Método getter @property def name ( self ): return self . _name # Método Setter @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 " # omite el setter >>> bob._name # omite el getter Charlie
En Racket , el sistema de objetos es una forma de organizar el código que viene además de los módulos y las unidades. Como en el resto del lenguaje, el sistema de objetos tiene valores de primera clase y el alcance léxico se utiliza para controlar el acceso a los objetos y 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 ( nuevo estudiante% [ nombre "Alice" ])) ( envía s obtener-nombre ) ; => "Alice" ( envía s establecer-nombre! "Bob" ) ( envía s obtener-nombre ) ; => "Bob"
Las definiciones de estructuras son una forma alternativa de definir nuevos tipos de valores, con mutadores presentes cuando se requiere explícitamente:
#lang racket ( struct student ( nombre ) #:mutable ) ( define s ( student "Alice" )) ( set-student-name! s "Bob" ) ( student-name s ) ; => "Bob"
En Ruby , se pueden definir métodos de acceso y mutación individuales, o las construcciones de metaprogramación attr_reader
se attr_accessor
pueden usar para declarar una variable privada en una clase y para proporcionar acceso público de solo lectura o de lectura y escritura a la misma respectivamente.
La definición de métodos de acceso y mutación individuales crea espacio para el preprocesamiento o la validación de los datos.
clase Estudiante def nombre @nombre fin def nombre= ( valor ) @nombre = valor fin fin
Acceso público simple de solo lectura a @name
variable implícita
clase Estudiante attr_reader :nombre fin
Acceso público simple de lectura y escritura a @name
variable implícita
clase Estudiante attr_accessor :nombre fin
struct Estudiante { nombre : String , } impl Estudiante { fn nombre ( & self ) -> & String { & self . nombre } función nombre_mut ( & mut self ) -> & mut String { & mut self . nombre } }
edad: aNumber " Establece la edad del receptor como aNumber si es mayor que 0 y menor que 150 " ( aNumber entre: 0 y: 150 ) ifTrue: [ edad := aNumber ]
clase Estudiante { var privado _name : String = "" var nombre : String { obtener { devolver self._name } establecer { self._name = newValue } } }
Este ejemplo ilustra la idea de VB.NET sobre las propiedades, que se utilizan en las clases. De manera similar a C#, existe un uso explícito de los métodos Get
y Set
.
Estudiante de clase pública Nombre privado como cadena Nombre de propiedad pública () Obtener Retorno _name Fin Obtener Establecer ( ByVal valor ) _name = valor Fin Establecer Fin Propiedad Fin de clase
En VB.NET 2010, las propiedades implementadas automáticamente se pueden utilizar para crear una propiedad sin tener que usar la sintaxis Get y Set. Tenga en cuenta que el compilador crea una variable oculta, llamada _name
, para que se corresponda con la propiedad name
. El uso de otra variable dentro de la clase nombrada _name
generaría un error. El acceso privilegiado a la variable subyacente está disponible desde dentro de la clase.
Clase pública Estudiante Nombre de la propiedad pública Como cadena Fin de clase