El principio de acceso uniforme en la programación informática fue propuesto por Bertrand Meyer (originalmente en su libro Object-Oriented Software Construction ). Este principio establece que «todos los servicios ofrecidos por un módulo deben estar disponibles mediante una notación uniforme, que no revele si se implementan mediante almacenamiento o mediante computación». [ 1 ] [ 2 ] Este principio se aplica generalmente a la sintaxis de los lenguajes de programación orientados a objetos . En términos más sencillos, establece que no debe existir ninguna diferencia sintáctica entre trabajar con un atributo , una propiedad precalculada o un método / consulta de un objeto.
Si bien la mayoría de los ejemplos se centran en el aspecto de "lectura" del principio (es decir, recuperar un valor), Meyer muestra que las implicaciones de "escritura" (es decir, modificar un valor) del principio son más difíciles de manejar en su columna mensual en el sitio web oficial del lenguaje de programación Eiffel . [ 3 ]
Explicación
El problema que aborda Meyer se refiere al mantenimiento de grandes proyectos o bibliotecas de software. A veces, al desarrollar o mantener software, una vez que gran parte del código está implementado, es necesario modificar una clase u objeto para transformar lo que antes era simplemente un acceso a un atributo en una llamada a un método. Los lenguajes de programación suelen usar sintaxis diferentes para el acceso a atributos y la invocación de métodos (por ejemplo, object.somethingversus object.something()). En los lenguajes de programación más populares de la época, este cambio de sintaxis requeriría modificar el código fuente en todos los lugares donde se utiliza el atributo. Esto podría implicar modificar el código fuente en numerosas ubicaciones dentro de un gran volumen de código. O peor aún, si el cambio se realiza en una biblioteca de objetos utilizada por cientos de clientes, cada uno de ellos tendría que encontrar y modificar todos los lugares donde se utiliza el atributo en su propio código y recompilar sus programas.
Hacer lo contrario (de método a atributo simple) realmente no fue un problema, ya que siempre se puede mantener la función y hacer que simplemente devuelva el valor del atributo.
Meyer reconoció la necesidad de que los desarrolladores de software escribieran código de manera que se minimizaran o eliminaran los cambios en cascada que resultan de las modificaciones que convierten un atributo de objeto en una llamada a un método, o viceversa. Para ello, desarrolló el Principio de Acceso Uniforme.
Muchos lenguajes de programación no admiten estrictamente el UAP, pero sí admiten algunas de sus variantes. Las propiedades, presentes en varios lenguajes de programación, abordan el problema que Meyer trataba con su UAP de una manera diferente. En lugar de proporcionar una notación uniforme, las propiedades ofrecen una forma de invocar un método de un objeto utilizando la misma notación que se usa para acceder a los atributos. La sintaxis independiente para la invocación de métodos sigue estando disponible.
Ejemplo de UAP
Si el lenguaje utiliza la sintaxis de invocación de métodos, podría verse algo así.
// Se asume que print muestra la variable que se le pasa, con o sin paréntesis. // Establece el atributo 'bar' de Foo al valor 5. Foo.bar(5) imprimir Foo.bar()
Al ejecutarse, debería mostrar :
5
El hecho de que Foo.bar(5)se invoque una función o simplemente se establezca un atributo permanece oculto para quien realiza la llamada. Del mismo modo, si Foo.bar()simplemente se recupera el valor del atributo o si se invoca una función para calcular el valor devuelto, es un detalle de implementación que también permanece oculto para quien realiza la llamada.
Si el lenguaje utiliza la sintaxis de atributos, la sintaxis podría verse así.
Foo.bar = 5 imprimir Foo.bar
Una vez más, el hecho de que se invoque o no un método, o que simplemente se asigne un valor a un atributo, permanece oculto para el método que realiza la llamada.
Problemas
Sin embargo, el UAP en sí mismo puede generar problemas si se utiliza en lugares donde las diferencias entre los métodos de acceso no son insignificantes, como cuando el valor devuelto es costoso de calcular o activará operaciones de caché. [ 2 ]
Ejemplos de lenguaje
Pitón
Las propiedades de Python permiten invocar un método con la misma sintaxis que se usa para acceder a un atributo. Mientras que el UAP de Meyer utiliza una única notación tanto para el acceso a atributos como para la invocación de métodos (sintaxis de invocación de métodos), un lenguaje compatible con propiedades aún admite notaciones separadas para ambos tipos de acceso. Las propiedades permiten usar la notación de atributo, pero ocultan que se está invocando un método en lugar de simplemente recuperar o establecer un valor.
Por lo tanto, Python deja la opción de adherirse a UAP a criterio del programador. La función integrada @propertyproporciona una forma sencilla de decorar cualquier método dado con la sintaxis de acceso a atributos, abstraiendo así la diferencia sintáctica entre las invocaciones de métodos y los accesos a atributos. [ 4 ]
En Python, podemos tener código que acceda a un Eggobjeto que podría definirse de tal manera que el peso y el color sean atributos simples como en el siguiente ejemplo:
""" >>> huevo = Huevo(4.0, "blanco") >>> huevo.color = "verde" >>> print(huevo) Huevo(4.0, verde) """clase Huevo : def __init __ ( self , peso , color ) - > None : self.peso = peso self.color = colordef __str__ ( self ) -> str : return f " { __class__ . __name__ } ( { self . weight } , { self . color } )"O bien, el objeto Egg podría usar propiedades e invocar métodos getter y setter en su lugar.
# ... (fragmento) ... clase Huevo : def __init __ ( self , peso_oz : float , color_nombre : float ) - > None : self.peso = peso_oz self.color = color_nombre@property def color ( self ) -> str : """Color del huevo.""" return to_color_str ( self . _color_rgb )@color . setter def color ( self , color_name : str ) -> None : self . _color_rgb = to_rgb ( color_name )@property def peso ( self ) -> float : """Peso en onzas.""" return self . _peso_gramo / 29.3@weight . setter def weight ( self , weight_oz : float ) -> None : self . _weight_gram = 29.3 * weight_oz# ...(recorte)...Independientemente de cómo Eggse defina, el código de llamada puede permanecer igual. La implementación Eggpuede cambiar de una forma a otra sin afectar al código que utiliza la clase Egg. Los lenguajes que implementan UAP también poseen esta propiedad.
Rubí
Considere lo siguiente
y = Egg.new ( " Green " ) y.color = " White " puts y.colorAhora la clase Egg podría definirse de la siguiente manera:
clase Egg attr_accessor :color def initialize ( color ) @color = color end endEl segmento de código inicial anterior funcionaría correctamente si Egg se definiera de esta manera. La clase Egg también podría definirse como se muestra a continuación, donde color es un método. El código que realiza la llamada seguiría funcionando sin cambios si Egg se definiera como sigue.
clase Egg def inicializar ( color ) @rgb_color = to_rgb ( color ) findef color to_color_name ( @rgb_color ) end def color= ( color ) @rgb_color = to_rgb ( color ) endprivado def to_rgb ( color_name ) ..... findef to_color_name ( color ) .... fin finNótese cómo, aunque colorparezca un atributo en un caso y un par de métodos en el siguiente, la interfaz de la clase sigue siendo la misma. La persona que mantiene la clase Egg puede cambiar de una forma a otra sin temor a romper el código de ningún llamador. Ruby sigue el UAP revisado, que attr_accessor :colorsolo actúa como azúcar sintáctico para generar métodos de acceso/setter para color. No hay forma en Ruby de recuperar una variable de instancia de un objeto sin llamar a un método sobre él.
En rigor, Ruby no sigue el Principio de Acceso Uniforme (PAU) original de Meyer, ya que la sintaxis para acceder a un atributo difiere de la sintaxis para invocar un método. Sin embargo, en este caso, el acceso a un atributo siempre se realiza mediante una función, que a menudo se genera automáticamente. Por lo tanto, en esencia, ambos tipos de acceso invocan una función y el lenguaje sí sigue el Principio de Acceso Uniforme revisado de Meyer.
DO#
El lenguaje C# admite propiedades de clase , que permiten definir getoperaciones set( obtener y establecer valores ) para una variable miembro. La sintaxis para acceder o modificar la propiedad es la misma que para acceder a cualquier otra variable miembro de la clase, pero la implementación real puede definirse como un simple acceso de lectura/escritura o como código funcional.
clase pública Foo { cadena privada _nombre ;// Propiedad pública int Size { get ; // Método getter set ; // Método setter }// Propiedad pública string Nombre { get { return _nombre ; } // Getter set { _nombre = valor ; } // Setter } }En el ejemplo anterior, la clase Foocontiene dos propiedades, Sizey Name. La Sizepropiedad es un entero que se puede leer (obtener) y escribir (establecer). De manera similar, la Namepropiedad es una cadena que también se puede leer y modificar, pero su valor se almacena en una variable de clase separada (privada) _name.
Omitir la setoperación en la definición de una propiedad hace que la propiedad sea de solo lectura, mientras que omitir la getoperación la hace de solo escritura.
El uso de las propiedades emplea el UAP, como se muestra en el código a continuación.
public Foo CreateFoo ( int size , string name ) { var foo = new Foo (); foo . Size = size ; // Setter de propiedad foo . Name = name ; // Setter de propiedad return foo ; }C++
C++ no tiene ni UAP ni propiedades, cuando un objeto se modifica de tal manera que un atributo (color) se convierte en un par de funciones ( getA, setA ). Cualquier lugar en que use una instancia del objeto y establezca u obtenga el valor del atributo ( x = obj.coloro obj.color = x) debe modificarse para invocar una de las funciones ( x = obj.getColor()o obj.setColor(x)). Usando plantillas y sobrecarga de operadores , es posible simular propiedades, pero esto es más complejo que en lenguajes que admiten propiedades directamente. Esto complica el mantenimiento de los programas C++. Las bibliotecas distribuidas de objetos C++ deben tener cuidado con la forma en que proporcionan acceso a los datos de los miembros.
JavaScript
JavaScript ha tenido soporte para propiedades calculadas a través de getters y setters desde ECMAScript 5 en 2009. [ 5 ] Sin embargo, hay casos especiales, como la lengthpropiedad on Array.prototypey String.prototypeque son anteriores a ECMAScript 5. [ 6 ]
En ECMAScript 5 JavaScript, Object.definePropertyse puede utilizar para definir métodos getter y setter. [ 7 ] Por ejemplo:
var obj = {};Objeto.defineProperty ( obj , " dos " , { "obtener" : function ( ) { return 1 + 1 ; } });Luego obj.twose puede usar para ejecutar la función getter. Dado que la propiedad twono define un setter, cualquier escritura en la propiedad no tendrá ningún efecto . Para un setter, setse usa la propiedad en su lugar:
var obj = {};Objeto.defineProperty ( obj , "dos" , { " obtener " : function ( ) { return this.x ; } , "establecer" : function () { this.x = 1 + 1 ; } } ) ;Referencias
- ↑ Meyer, Bertrand (1997). Construcción de software orientado a objetos (segunda ed.). Prentice Hall. pág. 57. ISBN 978-0-13-629155-8.
- 1 2 "El principio de acceso uniforme" . c2 wiki . Consultado el 6 de agosto de 2013 .
- ↑ Meyer, Bertrand. "Columna de EiffelWorld: Negocios más placer" . Consultado el 6 de agosto de 2013 .
- ↑ Documentación oficial de Python, funciones integradas
- ↑ "ECMA-262, 5.ª edición, diciembre de 2009" (PDF) . Ecma International . Diciembre de 2009. Consultado el 15 de julio de 2025 .
- ↑ "Especificación del lenguaje ECMAScript" (PDF) . Ecma International . 24 de marzo de 2000. Consultado el 15 de julio de 2025 .
- ↑ "Object.defineProperty() - JavaScript" . Mozilla . Consultado el 15 de julio de 2025 .
- Diseño de software
- paradigmas de programación
- Principios de programación