En informática , un método mutador es un método que se utiliza para controlar los cambios en una variable. También son ampliamente conocidos como métodos setter . A menudo, un establecedor va acompañado de un captador (también conocido como descriptor de acceso ), que devuelve el valor de la variable miembro privada.
El método del mutador se utiliza con mayor frecuencia en la programación orientada a objetos , de acuerdo con el principio de encapsulación . De acuerdo con 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, opcionalmente valida it, y modifica la variable miembro privada . Los métodos de mutadores se pueden comparar con la sobrecarga del operador de asignación , pero generalmente aparecen en diferentes niveles de la jerarquía de objetos.
Los métodos mutantes también se pueden utilizar en entornos no orientados a objetos. En este caso, se pasa al mutador una referencia a la variable a modificar, junto con el nuevo valor. En este escenario, el compilador no puede restringir que el código omita el método mutador y cambie la variable directamente. La responsabilidad recae en los desarrolladores de asegurarse de que la variable solo se modifique a través del método mutador y no se modifique directamente.
En los lenguajes de programación que los admiten, las propiedades ofrecen una alternativa conveniente sin renunciar a la utilidad del encapsulado.
En los ejemplos siguientes, un método mutador completamente implementado también puede validar los datos de entrada o tomar acciones adicionales, como desencadenar un evento .
Trascendencia
La alternativa a la definición de métodos mutadores y accesores, o bloques de propiedades , es darle 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 hacerse 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 accesor sea público mientras que el mutador permanece protegido, paquete privado o interno.
El bloque donde se define el mutador brinda la oportunidad de validar o preprocesar los datos entrantes. Si se garantiza que todo el acceso externo pasará por el mutador, estos pasos no se pueden omitir. Por ejemplo, si una fecha está representada por variables privadas y variables separadas year
, el mutador puede dividir las fechas entrantes, mientras que, por coherencia, se accede a las mismas variables de instancia privada mediante y . En todos los casos, los valores de mes fuera de 1 - 12 pueden rechazarse con el mismo código.month
day
setDate
setYear
setMonth
Por el contrario, los accesores permiten la síntesis de representaciones de datos útiles a partir de variables internas mientras mantienen su estructura encapsulada y oculta a los módulos externos. Un descriptor de getAmount
acceso monetario puede construir una cadena a partir de una variable numérica con el número de lugares decimales definido por un currency
parámetro oculto .
Los lenguajes de programación modernos a menudo ofrecen la capacidad de generar el texto estándar para mutadores y 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 validación, preprocesamiento o síntesis. Estos accesores simplificados aún conservan la ventaja de la encapsulación sobre las variables de instancia pública simple, pero es común que, a medida que avanzan los diseños del sistema , el software se mantiene y los requisitos cambian, las demandas de los datos se vuelven más sofisticadas. Muchos mutadores y accesadores 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 se agregue o no mayor sofisticación, y no requiere una refactorización extensa si es así. [1]
La manipulación de parámetros que tienen mutadores y descriptores de acceso desde el interior de la clase donde se definen a menudo requiere un pensamiento adicional. En los primeros días 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 agrega validación , validación cruzada , verificaciones de integridad de datos , preprocesamiento u otra sofisticación, pueden aparecer errores sutiles donde algunos accesos internos hacen uso del código más nuevo mientras que en otros lugares se omite.
Las funciones de accesor pueden ser menos eficientes que buscar o almacenar directamente campos de datos debido a los pasos adicionales involucrados, [2] sin embargo, tales funciones a menudo están en línea, lo que elimina la sobrecarga de una llamada de función.
Ejemplos de
Montaje
estudiante estructura edad dd ? estudiante termina
.code student_get_age proc objeto : DWORD mov ebx , object mov eax , student.age [ ebx ] ret student_get_age endpstudent_set_age proc object : DWORD , age : DWORD mov ebx , object mov eax , age mov student.age [ ebx ], eax ret student_set_age endp
C
En el archivo student.h:
#ifndef _STUDENT_H #define _STUDENT_H estudiante de estructura ; / * estructura opaca * / typedef struct student student ;estudiante * student_new ( int edad , Char * nombre ); void student_delete ( estudiante * s );void student_set_age ( estudiante * s , int edad ); int student_get_age ( estudiante * s ); char * student_get_name ( estudiante * s );#terminara si
En el archivo student.c:
#include #include #include "estudiante.h"estructura estudiante { int edad ; char * nombre ; };estudiante * student_new ( 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 ); libre ( s ); }void student_set_age ( estudiante * s , int edad ) { s -> edad = edad ; }int student_get_age ( estudiante * s ) { return s -> edad ; }char * student_get_name ( estudiante * s ) { return s -> nombre ; }
En el archivo main.c:
#include #include "estudiante.h"int main ( void ) { estudiante * s = estudiante_nuevo ( 19 , "Maurice" ); char * name = student_get_name ( s ); int old_age = student_get_age ( s ); printf ( "vejez de% s =% i \ n " , nombre , edad_antigua ); estudiante_conjunto_edad ( s , 21 ); int new_age = estudiante_get_age ( s ); printf ( "nueva era de% s =% i \ n " , nombre , nueva_edad ); estudiante_delete ( s ); return 0 ; }
En archivo Makefile:
todos : fuera . txt ; cat $ < out.txt : main ; ./$ <> $ @ main : main . o estudiante . o principal. o estudiante . o : estudiante . h limpia : ; $ ( RM ) *. o fuera . txt principal
C ++
En el archivo Student.h:
#ifndef STUDENT_H #define STUDENT_H#include class Student { public : Student ( const std :: string & name ); const std :: string & name () const ; void name ( const std :: string & name );privado : std :: string name_ ; };#terminara si
En el archivo Student.cpp:
#include "Student.h"Estudiante :: Estudiante ( const std :: cadena y nombre ) : nombre_ ( nombre ) { }const std :: string & Student :: name () const { return name_ ; }Void Student :: name ( const std :: string & name ) { name_ = name ; }
C#
Este ejemplo ilustra la idea de C # de 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. Tenga en cuenta el uso de la variable incorporada (no declarada) value
.
estudiante de la clase pública { nombre de la cadena privada ; /// /// Obtiene o establece el nombre del estudiante /// cadena pública Name { get { return name ; } establecer { nombre = valor ; } } }
En versiones posteriores de C # (.NET Framework 3.5 y superior), este ejemplo puede abreviarse de la siguiente manera, sin declarar la variable privada name
.
estudiante de clase pública { nombre de cadena pública { get ; 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 su asignación. El acceso se puede restringir con un set
modificador de acceso específico.
estudiante de clase pública { nombre de cadena pública { get ; conjunto privado ; } }
Lisp común
En Common Lisp Object System , las especificaciones de ranura dentro de las definiciones de clase pueden especificar cualquiera de las opciones :reader
, :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 son siempre accesibles directamente a través de sus nombres con el uso de with-slots
y slot-value
, y las opciones de acceso a las ranuras definen los métodos especializados que se utilizan slot-value
. [4]
CLOS en sí no tiene noción de propiedades, aunque la extensión del Protocolo MetaObject especifica los medios para acceder a los nombres de las 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 estudiantes que utiliza estas opciones de espacio y acceso directo al espacio:
( Defclass estudiante () (( nombre : initarg : nombre : initForm "" : de acceso estudiante-nombre ) ; estudiante-nombre es setf'able ( fecha de nacimiento : initarg : fecha de nacimiento : initForm 0 : Lector de estudiante-fecha de nacimiento ) ( número : initarg : número : initform 0 : lector número-alumno : escritor conjunto-número-alumno )));; Ejemplo de un captador de propiedades calculado (esto es simplemente un método) ( defmethod student-age (( self student )) ( - ( get-universal-time ) ( student-birthdate self )));; Ejemplo de acceso directo ranura dentro de una incubadora propiedad calculada ( defmethod ( setf estudiante de edad ) ( la nueva era ( auto estudiante )) ( con ranuras ( fecha de nacimiento ) auto ( setf fecha de nacimiento ( - ( get universal en tiempo ) de la nueva era )) nueva era ));; Las opciones de acceso a las ranuras 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- escriba nuevo-número ( entero 1 * )))
D
D admite una sintaxis de función getter y setter. En la versión 2 del lenguaje, los métodos getter y setter class / struct deben tener el @property
atributo. [6] [7]
clase Estudiante { carácter privado [] nombre_ ; // Getter @property char [] name () { devuelve esto . nombre_ ; } // Setter @property char [] name ( char [] name_in ) { devuelve esto . nombre_ = nombre_en ; } }
Una Student
instancia se puede usar así:
auto alumno = nuevo alumno ; estudiante . nombre = "David" ; // mismo efecto que student.name ("David") auto student_name = student . nombrar ; // el mismo efecto que student.name ()
Delphi
Esta es una clase simple en lenguaje Delphi que ilustra el concepto de propiedad pública para acceder a un campo privado.
interfazescriba TStudent = clase estricta privada FName : cadena ; procedimiento SetName ( valor constante : cadena ) ; public /// /// Obtiene o establece el nombre del estudiante. /// Nombre de la propiedad : string read FName write SetName ; terminar ; // ...implementaciónprocedimiento TStudent . SetName ( valor constante : cadena ) ; comenzar FName : = Valor ; terminar ; final .
Java
En este ejemplo de una clase simple que representa a un estudiante con solo el nombre almacenado, se puede ver que el nombre de la variable es privado, es decir, solo es visible desde la clase Student, y el "setter" y "getter" son públicos, es decir, " " y " " métodos.getName()
setName(name)
estudiante de clase pública { nombre de cadena privada ; public String getName () { nombre de retorno ; } public void setName ( String newName ) { name = newName ; } }
JavaScript
En este ejemplo, la función constructora Student
se usa para crear objetos que representan a un estudiante con solo el nombre almacenado.
función Estudiante ( nombre ) { var _name = nombre ; esto . getName = function () { return _name ; }; esto . setName = función ( valor ) { _name = valor ; }; }
O (no estándar y obsoleto): [8]
función Estudiante ( nombre ) { var _name = nombre ; esto . __defineGetter__ ( 'nombre' , función () { return _name ; }); esto . __defineSetter__ ( 'nombre' , función ( valor ) { _nombre = valor ; }); }
O (si usa prototipos para herencia; ¡ ECMA-6 !):
función Estudiante ( nombre ) { esto . _name = nombre ; }Estudiante . prototype = { get name () { devuelve esto . _nombre ; }, establezca el nombre ( valor ) { this . _nombre = valor ; } };
O (sin usar prototipos; ECMA-6):
var Student = { get name () { devuelve esto . _nombre ; }, establezca el nombre ( valor ) { this . _nombre = valor ; } };
O (si usa defineProperty):
función Estudiante ( nombre ) { esto . _name = nombre ; } Objeto . defineProperty ( Student . prototype , 'name' , { get : function () { return this . _name ; }, set : function ( value ) { this . _name = value ; } });
Actionscript 3.0
paquete { público de clase Estudiante { privado var _name : Cadena ; función pública get name () : String { return _name ; } nombre del conjunto de funciones públicas ( valor : String ) : void { _name = value ; } } }
C objetivo
Usando la sintaxis tradicional de Objective-C 1.0, con la referencia manual contando como la que trabaja en GNUstep en Ubuntu 12.04 :
@interface Estudiante : NSObject { NSString * _name ; }- ( NSString * ) nombre ; - ( void ) setName: ( NSString * ) nombre ;@final@implementation Student- ( NSString * ) nombre { return _name ; }- ( void ) setName: ( NSString * ) name { [ _name release ]; _name = [ nombre retenido ]; }@final
Usando la sintaxis de Objective-C 2.0 más nueva como se usa 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 , conservar ) NSString * nombre ;@final@implementation Student@synthesize name = _name ;@final
Y a partir de OS X 10.8 e iOS 6 , mientras se usa Xcode 4.4 y versiones posteriores, la sintaxis se puede incluso simplificar:
@interface Estudiante : NSObject@property (no atómico , fuerte ) NSString * nombre ;@final@implementation Student// Nada va aquí y está bien.@final
Perl
paquete Estudiante ;sub nuevo { bendecir {}, cambiar ; }sub set_name { my $ self = shift ; $ self -> { name } = $ _ [ 0 ]; }sub get_name { my $ self = shift ; return $ self -> { name }; }1 ;
O, usando Class :: Accessor
paquete Estudiante ; use base qw (Clase :: Accesor) ; __PACKAGE__ -> follow_best_practice ;Estudiante -> mk_accessors ( qw (nombre) );1 ;
O, usando el sistema de objetos Moose :
paquete Estudiante ; use Moose ;# Moose usa el nombre del atributo como setter y getter, 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
PHP define los "métodos mágicos" __get
y las __set
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 el nombre de la variable es privado, es decir, solo visible desde la clase Student, y el "setter" y el "getter" son públicos, es decir, los métodos y .getName()
setName('name')
clase Estudiante { cadena privada $ nombre ; / ** * @return string El nombre. * / public function getName () : string { return $ this -> name ; } / ** * @param string $ newName El nombre a establecer. * / public function setName ( string $ newName ) : void { $ this -> name = $ newName ; } }
Pitón
Este ejemplo usa una clase Python con una variable, un captador y un definidor.
class Student : # Initializer def __init__ ( self , name : str ) -> None : # Una variable de instancia para contener el nombre del estudiante self . _name = nombre # Método getter @property def name ( self ): return self . _nombre # Método de establecimiento @nombre . setter def name ( self , new_name ): self . _name = new_name
>>> bob = Estudiante ( "Bob" ) >>> bob . nombre Bob >>> bob . nombre = "Alice" >>> bob . nombre Alice >>> bob . _name = "Charlie" # omite el setter >>> bob . _name # omite el captador Charlie
Raqueta
En Racket , el sistema de objetos es una forma de organizar el código que se suma a los módulos y unidades. Como en el resto del lenguaje, el sistema de objetos tiene valores de primera clase y el alcance léxico se usa para controlar el acceso a 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-nuevo )))( define s ( nuevo estudiante% [ nombre "Alice" ])) ( envía s get-name ) ; => "Alice" ( envía s set-name! "Bob" ) ( envía s get-name ) ; => "Bob"
Las definiciones de estructuras son una forma alternativa de definir nuevos tipos de valores, con mutadores presentes cuando se requieren explícitamente:
#lang racket ( struct student ( name ) #: mutable ) ( define s ( student "Alice" )) ( set-student-name! s "Bob" ) ( student-name s ) ; => "Bob"
Rubí
En Ruby , se pueden definir métodos de acceso y mutación individuales, o las construcciones de metaprogramación, attr_reader
o attr_accessor
se pueden usar tanto para declarar una variable privada en una clase como para proporcionar acceso público de solo lectura o de lectura y escritura, 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.
class Student def name @name end def name = ( valor ) @name = value end end
Acceso público simple de solo lectura a la @name
variable implícita
class Student attr_reader : name end
Acceso público simple de lectura y escritura a la @name
variable implícita
class Student attr_accessor : nombre final
Charla
age: aNumber "Establezca la edad del receptor en unNumber si es mayor que 0 y menor que 150" ( unNumber entre: 0 y: 150 ) ifTrue: [ age : = aNumber ]
Rápido
class Student { private var _name : String = "" var nombre : String { get { return self . _name } set { self . _name = newValue } } }
Visual Basic .NET
Este ejemplo ilustra la idea de propiedades de VB.NET, que se utilizan en las clases. Al igual que en C #, hay un uso explícito de los métodos Get
y Set
.
Estudiante de clase pública Private _name como cadena Nombre de propiedad pública () Obtener Devolver _nombre Fin Obtener conjunto ( valor ByVal ) _nombre = valor Finalizar Establecer propiedad final Clase final
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
. Usar otra variable dentro de la clase nombrada _name
resultaría en un error. El acceso privilegiado a la variable subyacente está disponible desde dentro de la clase.
Público Clase Estudiante pública propiedad nombre Como Cadena End Class
Ver también
- Propiedad (programación)
- Indexador (programación)
- Objeto inmutable
Referencias
- ^ Stephen Fuqua (2009). "Propiedades automáticas en C # 3.0" . Archivado desde el original el 13 de mayo de 2011 . Consultado el 19 de octubre de 2009 .
- ^ Tim Lee (13 de julio de 1998). "Run Time Efficiency of Accessor Functions" .
- ^ "CLHS: Macro DEFCLASS" . Consultado el 29 de marzo de 2011 .
- ^ "CLHS: 7.5.2 Accediendo a Slots" . Consultado el 29 de marzo de 2011 .
- ^ "MOP: Definiciones de ranuras" . Consultado el 29 de marzo de 2011 .
- ^ "Funciones - Lenguaje de programación D" . Consultado el 13 de enero de 2013 .
- ^ "El estilo D" . Consultado el 1 de febrero de 2013 .
- ^ "Object.prototype .__ defineGetter __ () - JavaScript | MDN" . developer.mozilla.org . Consultado el 6 de julio de 2021 .
- ^ "PHP: Sobrecarga - Manual" . www.php.net . Consultado el 6 de julio de 2021 .