viernes, noviembre 03, 2006

Los métodos "getter / setter" o "accesores / modificadores" (revisado 10/2008)

He visto mucha documentación que habla sobre el tema de los métodos "getter / setter", o traducido al castellano los métodos "accesores / modificadores", y la mayoría se va a los extremos, perdiendo de explicar de forma simple la razón de su existencia.

Trataremos de sacarle la "mística" al asunto.

Antes de usar la estrategia de los "set y get" para los atributos

En PHP4 todos los atributos de un objeto son siempre atributos públicos, es decir, cuando creamos una instancia del objeto a partir de la definición de la clase, tenemos acceso completo a cada uno de los atributos, tanto para leerlos como para modificarlos.

Definición de la clase "Usuario"

1
<?php
2
class Usuario{
3 var
$nombre;
4 var
$nombreReal;
5 var
$edad;
6 var
$clave;
7 }
8
?>



Creo el objeto "unUsuario":

$unUsuario = new Usuario();

En este momento el usuario está vacío, pues sus atributos no tienen ningún valor asignado. Por lo tanto, le daremos sentido a este objeto:

1
<?php
2 $unUsuario
->nombre = "eplace";
3
$unUsuario->nombreReal = "Enrique Place";
4
$unUsuario->edad = "33";
5
$unUsuario->clave = "pepe";
6
?>



Inventemos un escenario completamente de fantasía (surrealista diría)

Supongamos ahora que nuestro sistema es mantenido por varios desarrolladores y que una parte del sistema es mantenida por un "estudiante de programación" que decidió unilateralmente que cuando le llegue el objeto "unUsuario" le pondrá siempre el nombre en mayúsculas y le colocará una clave por defecto si esta está vacía.

1
<?php
2 $unUsuario
->nombre = strtoupper($unUsuario->nombre);
3
4 if (
is_null($unUsuario->clave)){
5
$unUsuario->clave="clavePorDefecto";
6 }
7
?>



Nosotros, como "desarrolladores experimentados", nos sentimos molestos por la tontería que acaba de hacer el "estudiante"... ahora la pregunta es, como evitamos que esto suceda?

Uno de los problemas aquí es que PHP4 no soporta la definición del "alcance" (o también llamada "visibilidad") de los atributos y métodos, algo habitual en cualquier lenguaje serio de tipo "Orientado a Objetos". Esto hace que -aunque no nos guste- el desarrollador de turno pueda hacer lo que quiera con los atributos de cualquier objeto a su alcance.

Entonces migremos a PHP5

PHP5, consciente de este problema, implementa la definición de "visibilidad" de los atributos y métodos, como lo haría Java o .Net. Si hiciéramos una migración "mecánica" de nuestro código, este cambiaría la sintaxis a esta forma (ya que la sintaxis "var" pasa a desuso y esta definía a los atributos siempre "públicos"):

1
<?php
2
3
class Usuario{
4 public
$nombre;
5 public
$nombreReal;
6 public
$edad;
7 public
$clave;
8 }
9
10
?>



Ahora disponemos de las siguientes opciones gracias a la nueva sintaxis: public, private y protected.

Ahora bien, si queremos que el "estudiante" no pueda modificar nuestros datos, podemos pasar a colocar todos los atributos como "private":

1
<?php
2
class Usuario{
3 private
$nombre;
4 private
$nombreReal;
5 private
$edad;
6 private
$clave;
7 }
8
?>



Pronto, ahora cuando el "estudiante" quiera ver o modificar un atributo, el sistema le enviará un error. El problema ahora es que nosotros queremos que:
  1. La edad se pueda saber y cambiar en todo momento.
  2. Se pueda saber el nombre del usuario, pero no modificarlo
  3. No nos interesa que se sepa el nombre real del mismo
  4. Pero queremos que pueda colocar una nueva clave si el usuario se la olvida, pero no saber la que existe actualmente

Esto no lo podemos hacer ni teniendo todos los atributos públicos como sucedía con PHP4 ni restringiendo toda la visibilidad como nos permite ahora PHP5.

¿Cómo se hace entonces?

Por un tema de principios de la POO los atributos de los objetos deben ser siempre "privados" (concepto "encapsulación": no son accesibles desde fuera del objeto, solo el objeto tiene la potestad de usarlos directamente) y se deberán crear métodos públicos que sustituya una u otra operación, o ambas, cada vez que la situación lo amerite: un método "setter" para "cargar un valor" (asignar un valor a una variable) y/o un método "getter" para "retornar el valor" (solo devolver la información del atributo para quién la solicite).

Requerimiento 1) la edad se puede acceder y modificar en todo momento, por consiguiente se deben agregar los dos métodos, un "get" y un "set" para ese atributo:

1
<?php
2
class Usuario{
3 private
$nombre;
4 private
$nombreReal;
5 private
$edad;
6 private
$clave;
7
8 public function
getEdad() {
9 return
$this->edad;
10 }
11 public function
setEdad($edad){
12
$this->edad = $edad;
13 }
14 }
15
?>



Pronto, ahora el atributo se puede consultar o modificar no directamente, solo a través de los métodos "get / set". En este caso no se nota la utilidad, pero pasemos al siguiente requerimiento.

Requerimiento 2) poder saber el nombre del usuario pero no modificarlo, para eso hay que agregar solo un método get para ese atributo:

1
<?php
2
3
class Usuario{
4 private
$nombre;
5 private
$nombreReal;
6 private
$edad;
7 private
$clave;
8
9 public function
getEdad() {
10 return
$this->edad;
11 }
12 public function
setEdad($edad){
13
$this->edad = $edad;
14 }
15 public function
getNombre(){
16 return
$this->nombre;
17 }
18 }
19
?>



Ahora se puede consultar el nombre pero no modificar, pues el atributo no es visible desde fuera del objeto, solo a través de los métodos públicos que vamos definiendo.

Requerimiento 3) no nos interesa que se sepa el nombre real del usuario

Lo dejamos como está y queda inaccesible desde fuera del objeto.

Requerimiento 4) queremos que pueda colocar una nueva clave si el usuario se la olvida, pero no saber la que existe actualmente

Para eso, hacemos lo contrario que con el atributo nombre, agregamos un método "set" pero no el "get":

1
<?php
2
class Usuario{
3 private
$nombre;
4 private
$nombreReal;
5 private
$edad;
6 private
$clave;
7
8 public function
getEdad() {
9 return
$this->edad;
10 }
11 public function
setEdad($edad){
12
$this->edad = $edad;
13 }
14 public function
getNombre(){
15 return
$this->nombre;
16 }
17 public function
setClave($clave){
18
$this->clave = $clave;
19 }
20 }
21
?>



Pronto, usando simples métodos podemos reforzar el diseño de nuestro objeto, restringiendo según nuestra necesidad el acceso a sus atributos.

Formalicemos: "Getter/Setter es solo un tema de conceptos"

Cuando empezamos a aprender a usar Objetos el primer error que cometemos es dejar todos los atributos públicos, lo que permite que cualquier usuario de nuestra clase pueda hacer y deshacer sin control nuestro objeto (modificando y consultando sus valores).

Generalmente nuestros objetos deberían contener determinada información que no necesariamente el usuario de nuestro objeto debería saber, porque no conviene, o porque directamente no corresponde y permitirle acceder a la misma es aumentar innecesariamente la complejidad del uso del objeto.

Regla: "Evitar que el objeto muestre detalles de su implementación"

Otro ejemplo: si tu sabes que tu objeto "Persona" tiene una fecha de nacimiento y calculas su edad, no deberías poder permitir que alguien "de afuera del objeto" cambie la edad, pues está relacionada con otra información (fecha de nacimiento) y a partir de ella es que se genera (calcularEdad()). Lo correcto sería modificar la fecha de nacimiento para que el propio objeto la vuelva a calcular.

Los detalles del funcionamiento del objeto son internos, y el usuario del objeto (otro desarrollador u otro sistema) no debe ni necesita conocerlos. Por consiguiente ya tenemos otro caso claro, el atributo "edad" no debería poder ser modificado externamente, pero sí consultado cada vez que se necesite. Si este fuera un atributo público sería imposible restringir una operación y habilitar la otra. De ahí que nace el concepto de "getter / setter", o de "métodos accesores / modificadores", o como muchos autores se refieren a estos métodos especiales como "propiedades" (pero creo que puede generar confusiones con el concepto "atributo", pues muchos otros autores usan las mismas palabras indistintamente).

Si tú colocas atributos privados, estos serán solo "vistos / accedidos / usados / modificados" dentro de la propia clase. Si tu quieres que puedan ser accedidos desde fuera de la clase, debes crear métodos públicos que internamente "accedan" a los atributos, pero que los dejarán "resguardados" dentro del objeto (no hay que olvidar que en un caso pueden hacer una operación u otra, no necesariamente ambas).

Recalco, digo "nomenclatura", puesto que los nombres de los métodos pueden llamarse como quieras, pero si cumplen con ambas definiciones (get/set), complirá con la esencia de la funcionalidad. El tema es, por convención, se tiende a reconocer así a los métodos que solo sirven para "hacer operaciones básicas como si trabajáramos con atributos públicos", y que además, no deben tener más complejidad que esa. De lo contrario, ya sería mejor que crearas un método comunes y corriente, asignándole un nombre claro a su acción.

Errores más comunes

Definir todos los "get" y "set" para todos los atributos existentes. Es casi lo mismo que si fueran todos públicos, careciendo de utilidad. Lo habitual es que esto no suceda, cuanto más escondamos de nuestro objeto mejor será, pues disminuimos la complejidad de su uso y evitamos que cambie a un estado que no queremos, y cuando menos se sepa de como trabaja internamente, más fácil será poder reutilizarlo en contextos distintos (deberá ser raro que debas dejar disponible todo y no existan algunos que sean solo de uso interno).

Otro error común es agregarle más lógica que asignar un valor o retornar el valor del atributo. Si necesitas agregarle lógica, ya dejan de ser "get / set", lo cual es mejor que cambiemos de nombre y lo dejemos con los demás métodos comunes de nuestra clase.

Por ejemplo: si para cambiar el nombre del usuario, antes de asignar el valor voy a la base de datos y verifico que no exista otro igual, y luego en caso de nombre duplicado genero otro alternativo, etc, yo preferiría o desglosar el método setNombre en varias llamadas a métodos privados, o directamente crear un método nuevo que se llame cambiarNombre, reflejando un proceso fuera del simple get/set.

Nota: esto es muy a nivel personal, hay opiniones encontradas sobre tener una lógica elemental dentro de un set/get o hacerlo tan extenso como necesitemos. Claramente yo estoy en el primer grupo.

Como detalle, para que quede más evidente y visualmente claro

Como todo es convención a la hora de definir la forma de trabajo en un entorno de desarrollo, es habitual que los atributos siempre vayan juntos al principio de la clase e inmediatamente -antes de empezar a definir los métodos- deberían estar los métodos "accesores / modificadores". Lo que se suele hacer, pues son métodos muy simples y generalmente carentes de mucha lógica (como se explica en el punto anterior), tal vez sería mejor hacerlos en una sola línea, sin indentación:

1
<?php
2
3
class Usuario{
4 private
$nombre;
5 private
$nombreReal;
6 private
$edad;
7 private
$clave;
8
9
/** getter / setter */
10
public function getEdad() {return $this->edad;}
11 public function
setEdad($edad){$this->edad = $edad;}
12
13 public function
getNombre(){return $this->nombre;}
14
15 public function
setClave($clave){$this->clave = $clave;}
16
17 }
18
?>


Nota: De todas formas no tomar esto más de un ejemplo, ya que el estándar oficial de condificación para PHP (el que define la empresa Zend) no sugiere en ningún momento esta práctica.

En resumen

El tema no es tan complejo, de forma genérica esta es la explicación de qué es "getter/setter" y para qué sirve y cómo se usan.

También quiero destacar que a pesar de existir esta técnica, no quiere decir que deba ser usada o que su uso esté completamente permitido. Hay que entender que la POO no debería hacer uso de de los getter y setters ya que tendríamos acceso de forma directa al "estado del objeto" (la información que tienen los atributos de un objeto) y permitir el acceso o modificación genera el mismo efecto de manipulación que si fuera una operación de "corazón abierto". Nuestro objeto debería estar protegido del exterior y no permitir tener acceso directo a sus atributos, y trabajar siempre sobre sus métodos ("los métodos definen el comportamiento del objeto"). En caso de no poder hacerlo, se podría priorizar disponer de "get" (obtener valor de un atributo de forma directa) y no "set" (modificar un valor directo de un atributo), y en el peor de los casos, tener un uso muy controlado de los "set".

Para más información sobre diseño OO, discusiones teóricas, buenas prácticas, etc, consultar escritos de gurúes como Martín Fowler.

Artículo basado en una respuesta dada en Foros del Web

28 comentarios:

Anónimo dijo...

Pero... ¿cuál es la mística posible en unos getters and setters??

enrique_place dijo...

Anónimo:

Me ha sucedido que los "no entendidos", "novatos", "novicios", no logran entender el sentido de:

- que los atributos no deben ser públicos
- porqué y para qué existen los getter y setter
- y de que forma usarlos en sus diseños.

Los que estamos un poco más de tiempo en estos temas lo vemos de forma natural y ya ni lo cuestionamos ni razonamos, lo damos como "un hecho" que debe ser así.

Para muchos, hablar de este tipo de cosas, hasta de OO en PHP, es algo de "brujas" y "hechiceros".

No te olvides que el gran porcentaje de los programadores PHP desconoce la POO, otros directamente la rechaza, y los que la usan, muchos, es sin entender el concepto que hay detrás.

davida dijo...

Hola, me gustaría saber donde puedo encontrar ejemplos de aplicaciones web programadas con clases y objetos.... es que me cuesta mucho entender como es posible pues he de suponer que 'al cambiar de página' los objetos desaparecen. No consigo abstraerme.

Gracias a este blog estoy intentando subirme a 'los railes' (Ruby on the Rails) Pero tambien me resulta algo complicado de entender ¿Es normal que me suceda esto? Yo termine hace poco mis estudios de Programador, y me gustaría llegar a ser diseñador... pero supongo que me queda mucho camino.

Gracias por atenderme

enrique_place dijo...

Estimado David:

> Hola, me gustaría saber donde puedo
> encontrar ejemplos de aplicaciones web
> programadas con clases y objetos....
> es que me cuesta mucho entender como
> es posible pues he de suponer que 'al
> cambiar de página' los objetos
> desaparecen. No consigo abstraerme.

Este tema lo traté en el siguiente "post":
"Consideraciones sobre el uso del Patrón Singleton desde un entorno web con PHP"


Lee también los comentarios que complementan el contenido del mismo.

Si quieres entender lo que es el patrón Singleton:
Patrón de Diseño "Singleton"


> Gracias a este blog estoy intentando
> subirme a 'los railes' (Ruby on the
> Rails) Pero tambien me resulta algo
> complicado de entender ¿Es normal que
> me suceda esto? Yo termine hace poco
> mis estudios de Programador, y me
> gustaría llegar a ser diseñador...
> pero supongo que me queda mucho
> camino.

¿Diseñador o Desarrollador? Todo lleva su tiempo de maduración y experiencia... no te quedes solo en los lenguajes, investiga y fundamentalmente lee sobre los conceptos.

> Gracias por atenderme

De nada, a las órdenes ;-)

PD: en poco tiempo estoy liberando varios proyectos, entre ellos un libro en formato digital sobre POO y Patrones desde PHP, un framework que uso para dictar las clases que doy (desarrollado por los alumnos) y alguna otra cosa más que tengo en la incubadora. ;-)

davida dijo...

Gracias, muchas gracias por contestar. Por cierto, disculpa que publicara aquí el comentario, pensaba que lo estba haciendo en el artículo sobre la profesionalización de los programadores PHP.

Sí, quería decir desarrollador.

Intento madurar, pero ando perdido. Intenté ponerme en conatcto con usted vía mail, pero no lo conseguí.

Estaré atento a sus blogs para poder ver esos proyectos que liberará, además de que me ha gustado mucho porque sus contenidos son más que buenos, desde mi óptica, claro.

Gracias una vez más.

Anónimo dijo...

Los metodos get y set deben llevar más lógica implicita, si bien es cierto que los atributos de los metodos no deben ser públicos no tiene sentido que un método set simplemente acceda a la varieble y la fije sin más.

El método set debe ser usado para validar la correctitud de los datos que recibe y de esa forma garantizar que la variable es fijada con los datos que necesita.

Por ejemplo si necesitas que el valor de la variable $variable sea numerica y de tipo integer y el método set simplemente es un $this->variable = $variable se le puede pasar lo que sea, strings, arrays, etc. Esto tiene mucho mayor sentido en un lenguaje como PHP que pobremente tipado.

En el caso del método get lo normal es usarlo simplemente para devolver una variable a la cual no se tiene acceso, pero también es posible usar más lógica en esto y por ejemplo asrgurarse de que no se va a devolver una variable vacia.

En fin, los metodos get y set no tienen sentido simplemente para esconder los atributos si el acceso a los mismos termina siendo igual que si fuesen públicos.

Saludos

enrique_place dijo...

Estimado "Anónimo":

> Los metodos get y set deben
> llevar más lógica implicita, si
> bien es cierto que los atributos
> de los metodos no deben ser
> públicos no tiene sentido que un
> método set simplemente acceda a
> la varieble y la fije sin más.

Todo es relativo, cada desarrollador tiene su versión de cómo aplicar los conceptos.

Con respecto a lo que afirmas al final, es incorrecto. En ningún momento se habla de esconder los atributos para luego mostrarlos con métodos get y set.


> El método set debe ser usado
> para validar la correctitud de
> los datos que recibe y de esa
> forma garantizar que la variable
> es fijada con los datos que
> necesita.

Es relativo, no se puede aplicar ninguna de las versiones con exclusividad.

El concepto que quiero dejar es que los set/get son para ocultar los atributos y tener un control sobre ellos. Si tú le agregas mucha lógica a cada método set/get ya creo que deberían ser directamente métodos comunes y no un "accesor/modificador" (la simplicidad está implícita en los términos).

> Por ejemplo si necesitas que
> el valor de la variable $variable
> sea numerica y de tipo integer y
> el método set simplemente es un
> $this->variable = $variable se le
> puede pasar lo que sea, strings,
> arrays, etc. Esto tiene mucho
> mayor sentido en un lenguaje como
> PHP que pobremente tipado.

"Tipado dinámico". Lo que tú dices va en oposición a la esencia del lenguaje. La asignación dinámica no es una debilidad de la POO en PHP, es propiamente parte del lenguaje.

Que tu quieras hacer una POO en PHP mucho más robusta, es otro tema.

> En el caso del método get lo
> normal es usarlo simplemente para
> devolver una variable a la cual
> no se tiene acceso, pero también
> es posible usar más lógica en
> esto y por ejemplo asrgurarse de
> que no se va a devolver una
> variable vacia.

Esta última parte es ridícula, no puedes generalizar, esto es pura y exclusivamente responsabilidad del desarrollador definir un diseño que se comporte de esta forma.

> En fin, los metodos get y set
> no tienen sentido simplemente
> para esconder los atributos si el
> acceso a los mismos termina
> siendo igual que si fuesen
> públicos.

No leíste bien el artículo.

Eugenio dijo...

Hola, que es lo que sucedes cuando accedemos a todos los metodos de la clase, y desde muchos metodos de otras clases.

Esta conceptualmente mal hacer un;
funcion __get($var) { return $this->$vat; }

Esto seria escribir una linea en vez de hacer 200 de getters!

Saludos

enrique_place dijo...

Estimado Eugenio:

> Hola, que es lo que sucedes
> cuando accedemos a todos los
> metodos de la clase, y desde
> muchos metodos de otras clases.

Asumo que quieres decir a "todos los atributos de una clase".

> Esta conceptualmente mal hacer
> un;
> funcion __get($var) { return
> $this->$vat; }

Los __get y __set son herramientas que provee específicamente PHP5, por lo que se pueden usar o no como una forma de simplificar la creación de métodos get y set, pero conceptualmente generalizarlos hacen el mismo efecto que variables "públicas", por lo que pierde el efecto (está bien explicado su uso en el artículo original, te recomiendo re-leerlo).

> Esto seria escribir una linea en
> vez de hacer 200 de getters!

Si tienes 200 atributos y tienes que hacer 200 get y 200 set, es que tu diseño está erróneo.

Una clase que muestre todos sus atributos rompe el principio de ocultación al no esconder los detalles de implementación.

Relee el artículo, por favor.

Jose Eduardo Rios dijo...

Que tal

Encuentro muy buena la explicación dada en este articulo, yo estaba acostumbrado( y aun no cambio 100%) a trabajar con Programación de Eventos pero tube que estudiar Java en el Instituto y me metierón a la fuerza :S el concepto de POO.

Una de las discuciones (estaba mal yo) que tube con el profe era esto de los Get's y Set's, yo erraba en compararlo con el Set de Visual basic.

Creo que este tema es muy interesante e importante al momento de programar orientado a objetos ya que te permite controlar las entrañas de nuestros objetos como el control de acceso este definido por el mundo real (lo que el Poo representa).

Saludos

José Rios
noteroschile.cl

enrique_place dijo...

Estimado José:

> Encuentro muy buena la
> explicación dada en este
> articulo, yo estaba
> acostumbrado( y aun no cambio
> 100%) a trabajar con
> Programación de Eventos pero
> tube que estudiar Java en el
> Instituto y me metierón a la
> fuerza :S el concepto de POO.

Bien, impecable, yo también me vi influenciado por Java... y luego ves el mundo actual de los desarrolladores PHP y te das cuenta que nos falta mucho. He aquí el aporte que intento dar desde el blog.

> Creo que este tema es muy
> interesante e importante al
> momento de programar orientado a
> objetos ya que te permite
> controlar las entrañas de
> nuestros objetos como el control
> de acceso este definido por el
> mundo real (lo que el Poo
> representa).

Es que conviene revisar una y otra vez los conceptos base (releer de vez en cuando la entrada sobre POO en Wikipedia) y verás que nos olvidamos cómo deberíamos encarar nuestros diseños, ya que muchos terminan usando simples clases en ambientes estructurados y no aplican todas sus ventajas.

José, gracias por los comentarios ;-)

Nebur dijo...

Podrías indicar que autores hablan sobre la manipulación de propiedades (getter y setter) y si sus publicaciones son aceptados por la comunidad internacional?. llevo tiempo tratando de entender esta manera de programar y sé que varía bastante entre lenguajes (para los que trabajamos con mas de 1 lenguaje) y todo lo que he encontrado sobre el tema son blogs y mas blogs. Yo tengo entendido que no existe una regla para el tema que tu tratas en tu blog, y el concepto de abstracción (público) e implementación (privado) donde quedá. porque según entendí, cuando tú hablas de requerimientos lo tratas como algo PRIVADO, es decir implementación, entonces, cuando haces abstracción en tu software?? con métodos públicos usando getter y setter? creo que eso es una mala práctica.

http://www.amazon.com/gp/reader/0321267974/ref=sib_dp_pt#reader-link

Booch, Jacobson, Rumbaugh, estos tipos saben de lo que hablan.

saludos cordiales

Nebur Álvarez B.

Claudio dijo...

Hola, queria hacerte una consulta. Que pasa si un atributo privado es un array? como funcionan los setter y los getter, ya que estos métodos no reciben el indice con el cual trabajarían?

enrique_place dijo...

Estimado Claudio:

> Que pasa si un atributo privado es
> un array? como funcionan los
> setter y los getter, ya que estos
> métodos no reciben el indice con
> el cual trabajarían?

Se podrían aplicar variantes o implementaciones para que los get sí funcionen, pero por lo pronto, si tu get o set se vuelve complejo, lo mejor será que hagas métodos propiamente dichos y le agregues todo lo que necesitas.

El mundo no inicia y termina en los get / set.

enrique_place dijo...

Que tal Nebur:

Disculpa la demora en responder, gracias por tu aporte, ahora te contesto por partes ;-)

> Podrías indicar que autores
> hablan sobre la manipulación de
> propiedades (getter y setter) y
> si sus publicaciones son
> aceptados por la comunidad
> internacional?.

No, la verdad -disculpa mi honestidad- que no tengo tiempo ni ganas como para ponerme a buscar bibliografía concreta solo por que tú me lo preguntes ahora :-), no recuerdo en el momento autores concretos para citar, pero en todos mis años de estudio, tanto por materiales como por docentes, el tema "setter/getter" no es para nada extraño, es lo primero que te enseñan cuando vez POO.

> llevo tiempo tratando de
> entender esta manera de
> programar y sé que varía
> bastante entre lenguajes (para
> los que trabajamos con mas de 1
> lenguaje)

No sé a que te refieres con "esta manera de programar", lo dices por los get y set?

> y todo lo que he encontrado
> sobre el tema son blogs y mas
> blogs.

Estás buscando mal, busca libros impresos entonces.

> Yo tengo entendido que no
> existe una regla para el tema
> que tu tratas en tu blog,

Regla? qué regla? sobre cómo se usan o si se deben usar?

Cómo, así se usan, si se deben usar, no, no deberían.

Nota: gracias a tu comentario hoy mismo actualicé este post para evitar dudas, ya que hablé de un tema que he visto se comenten muchos errores conceptuales, pero tampoco quise decir con esto que se deberían usar siempre o que fuera algo recomendado su práctica.

> y el concepto de abstracción
> (público) e implementación
> (privado) donde quedá. porque
> según entendí, cuando tú hablas
> de requerimientos lo tratas como
> algo PRIVADO, es decir
> implementación, entonces, cuando
> haces abstracción en tu
> software?? con métodos públicos
> usando getter y setter? creo que
> eso es una mala práctica.

No entendí qué es lo que quieres transmitir, tampoco en qué te basas para decir que cuando hablo de "requerimientos"... "¿cuando hago abstraccion del software?". No entendí la relación ni la idea de la pregunta.

> Booch, Jacobson, Rumbaugh,
> estos tipos saben de lo que
> hablan.

Sí, como muchos más que saben y muchos otros más que no saben.

Un autor que discute sobre estos temas de diseño y muy recomendado es Martín Fowler, y justamente tiene un escrito que habla del tema de evitar a muerte los get/set.

> saludos cordiales

> Nebur Álvarez B.

Nebur, gracias por tu aporte, motivó la actualización de este post.

Quiero recordarte que no soy un gurú y no me gusta estar horas y horas discutiendo del "sexo de los ángeles" ni que autor la "tiene más grande" (del ego estoy hablando), me apoyo en la teoría pero bajo contantemente a la práctica, soy muy pragmático, y puedo cometer errores, por lo que soy abierto a los comentarios y -fuera de lo que creen muchos- los leo todos y los tomo en cuenta.

nbrx dijo...

hola enrique, parece que se agito un poco el tema. entiendo completamente a lo que refieres y tu opinión me parece bien. mis profes me enseñaron lo mismo, SOLO QUE, en esencia, se tiende a confundir implementación de componentes de software, en donde todos los atributos de las clases deben ser por regla privados y acceder por getter y setter, con el software que atenderá directamente los requerimientos del cliente. piensa en eso...

salu2 Enrique..

enrique_place dijo...

Estimado nbrx:

No me quedó claro lo que me quieres decir... cual es la confusión que dices y por qué?

Edwin E. dijo...

Enrique, muchas gracias por tus valiosos comentarios sobre la POO, escribes de forma clara y con ejemplos sencillos y muy apropiados.

continuaré consultando tu Blog y recomendadolo.

Saludos.

     dijo...

Buenas,

Mi forma de simular los Get y Set es la siguiente:

...

public function Name($accion,$valor = 0)
{
        if($accion == "Get")
                return $this->name;
        if($accion == "Set")
                $this->name = $valor;
}

...


$miPersona->Name("Set","Pepe");
$name = $miPersona->Name("Get");

Nose si sera la mas conveniente pero es la que uso normalmente.

SaluDOS! pac-man

enrique_place dijo...

Estimado Anónimo:

¿Simular los get/set? ¿con qué sentido?

Lo que haces no tiene ni pie ni cabeza, fuera de toda lógica.

¿Generar un método genérico para los get / set? ¿solo para ahorrar un par de líneas de código?

¿Entendiste algo de lo que decía este post?

Aquí generas el mismo efecto que los atributos públicos...

No te entiendo.

     dijo...

Buenas Enrique,

Pero en sí no ahorro codigo ...

public function getEdad() {
return $this->edad;
}
public function setEdad($edad){
$this->edad = $edad;
}

Y al fin y al cabo es lo mismo que el codigo anterior.No siempre uso el get y set, entiendo que sino para eso hago los atributos publicos y listo, tambien por lo que lei que si a la propiedad le aplicas logica deja de ser una propiedad y es un metodo, pero esa logica en tu caso no la aplicas en la definición de la propiedad usando getEdad , setEdad($valor)

Pero mas que nada queria mostrar mi forma de implementar mi Get and Set.

Saludos, las criticas son bienvenidas.

PAC-MAN

enrique_place dijo...

Estimado PACMAN:

> Pero en sí no ahorro codigo ...

¿Cual es la intención de crear un método genérico para obtener un get o set y no hacer los que correspondan según los atributos que lo necesiten?

> Y al fin y al cabo es lo mismo
> que el codigo anterior.No
> siempre uso el get y set,
> entiendo que sino para eso hago
> los atributos publicos y listo,

Pero lo haces, el efecto es el mismo.

> tambien por lo que lei que si a
> la propiedad le aplicas logica
> deja de ser una propiedad y es
> un metodo, pero esa logica en tu
> caso no la aplicas en la
> definición de la propiedad
> usando getEdad , setEdad($valor)

No te entiendo. Tienes un atributo y luego métodos getter y setter, lo que se busca con ellos que sean fundamentalmente "simples", acceder a ellos de forma restringida evitando que sean "públicos".

La idea es que si necesitas algo más complejo es que uses un método común y corriente y no digas que se llama "get" o "set".

Pero sigo sin entender qué me quieres decir.

> Pero mas que nada queria mostrar
> mi forma de implementar mi Get
> and Set.

Evita el "mi forma", hay demasiadas ruedas redondas como para inventar una cuadrada ahora.

¿No tienes nada más interesante que hacer que perder el tiempo con algo que no puede generar más aportes nuevos?

Desarrolla sistemas, resuelve problemas verdaderos, apóyate en experiencias, usa estándares y aprende buenas prácticas, no pierdas el tiempo con esto.

Saludos.

Rubén Moraleda dijo...

¡Hola Enrique!.

Sería interesante ampliar ésta información con algún post sobre los fluent interfaces, ya que tienen bastante que ver con los setters. ¿Qué te parece?.

En cuanto a lo que comenta Pac-man, hay que tener en cuenta que uno de los principales problemas de ser autodidactas es que a veces creemos entender algo que realmente no hemos entendido en absoluto, y como nadie nos dice nada, creemos que lo estamos haciendo bien porque funciona. Es cuestión de cambiar un poco el chip e intentar ver las cosas desde otra perspectiva.

Saludos,
Rubén.

Domingo Eduardo Domínguez dijo...

Hola que tal Enrique!,

Estoy de acuerdo contigo con que los atributos deben de ser privados, y no abusar del "getter/setter". Pero si le encuentro mucha utilidad, actualmente yo uso el fremework PRADO (www.pradosoft.com) un excelente fremework orientado a eventos similar al .NET. A grego una clase de Adodb adaptado para el fremework de PRADO (Class Adodb for PRADO):

class adodb extends TModule{
private $db;
private $_Driver;
private $_Host;
private $_Username;
private $_Password;
private $_Database;
private $_Persistent = false;


public function init($config){
if (!$this->Driver){
throw new TConfigurationException('Missing param: Driver');
}
if (!$this->Host){
throw new TConfigurationException('Missing param: Host');
}
if (!$this->Username){
throw new TConfigurationException('Missing param: Username');
}
if (!$this->Password){
throw new TConfigurationException('Missing param: Password');
}
if (!$this->Database){
throw new TConfigurationException('Missing param: Database');
}
parent::init($config);
}

//PHP magic function.
//This method will pass all method calls to ADODB class/library.
public function __call($method, $params){
$conn = $this->getDatabaseConnection();
return call_user_func_array(array($conn, $method), $params);
}

private function getDatabaseConnection(){
if (!isset($this->db)){
$this->db = NewADOConnection($this->Driver);
$this->db->SetFetchMode(ADODB_FETCH_ASSOC);
if ($this->Persistent){
//For more see: http://phplens.com/lens/adodb/docs-adodb.htm#pconnect
$this->db->PConnect($this->Host, $this->Username, $this->Password, $this->Database);
}
else{
//For more see: http://phplens.com/lens/adodb/docs-adodb.htm#connect
$this->db->Connect($this->Host, $this->Username, $this->Password, $this->Database);
}
}
return $this->db;
}

//Getter and setters for params.
public function getDriver(){
return $this->_Driver;
}
public function setDriver($value){
$this->_Driver = TPropertyValue::ensureString($value);
}
public function getHost(){
return $this->_Host;
}
public function setHost($value){
$this->_Host = TPropertyValue::ensureString($value);
}
public function getUsername(){
return $this->_Username;
}
public function setUsername($value){
$this->_Username = TPropertyValue::ensureString($value);
}
public function getPassword(){
return $this->_Password;
}
public function setPassword($value){
$this->_Password = TPropertyValue::ensureString($value);
}
public function getDatabase(){
return $this->_Database;
}
public function setDatabase($value){
$this->_Database = TPropertyValue::ensureString($value);
}
public function getPersistent(){
return $this->_Persistent;
}
public function setPersistent($value){
$this->_Persistent = TPropertyValue::ensureBoolean($value);
}
}
?>


Por ejemplo , dentro de la clase exite la función getDatabaseConnection:
Puedo acceder a la propiedad $this->Host que en realidad es $this->getHost(), o puedo asignarle $this->Host="localhost" que en realidad es $this->setHost("localhost"), el fremework ya reconoce si es un Setter o un Getter, la diferencia es que ya se encuentra validado para el tipo String.
Component PropertiesEstoy de acuerdo que mis atributos ($db, $_Driver , $_Host, $_Username, $_Password, $_Database, $_Persistent) ahora son públicos porque tienen el Get y el Set, sin embargo lo importante es que se asegura que estos atributos sean del tipo String o Boleanos, o en su defecto Enteros y de esa manera poder validarlos sin meterle tanta lógica. Para mi eso sería la ventaja de usar los Getter y Setter, asegurandonos de que los datos que se asignan a nuestros a tributos sean los correctos ademas de que es mucho más comodo con el fremework usar las propiedades.

enrique_place dijo...

Que tal Domingo ;-)

Sobre el tema, hay muchas variantes, pero lo básico y elemental es lo que comento para los get/set, ya que ese es el origen y su uso. Si tú le das más uso y te sirve, bienvenido sea.

Muy buen aporte ;-)

Saludos!

PaK0s dijo...

Saludos ya llevo como 3hr metido en tu blog y no me aburro :D aunque si entro en algunas polémicas contigo, en este post casi estoy de acuerdo con todo a excepción de "Otro error común es agregarle más lógica que asignar un valor o retornar el valor del atributo."
Como ya se dice en los comentarios esto cabe en la forma en que cada programador utilice los getter/setters, pero en el texto afirmas como un “error” y no específicamente es un error.
Por lo general lo que se hagrega de lógica de “mas” a los setters pueden ser bloques trows para crear exepciones o bueno yo lo utilizo así, para así evitar y validar desde la misma clase y cuidar que los datos que llegan a mi objeto sean del tipo de datos para el que se diseño la clase, a los getters puede que tengas información básica y lo que quieras mostrar sea el resultado de una operación y no precisamente el dato que bien puede estar abstracto del usuario de la clase,
De hecho muchos también dicen que es malo crear los set y geters para todos los atributos tal vez sí, pero volvemos a que es mas subjetivos y todo depende del diseño, aunque en lo personal creo que es mejor ponerlos todos, pero si modificar a prívate aquellos que no quiero que sean utilizando por fuera, esto siempre permite y aumenta el nivel de mantenimiento de la clase.
Pero en conclusión los get y set nos permite abstraer al usuario de la clase, el nivel de abstracción que deseemos lograr y la funcionalidad de los mismo métodos no la dira el diseño de la clase.

вoиιƒαcιo vεlαzquεz вαrcεиαs dijo...

Muy buen documento, me gustaria aportar un metodo ke me sirve mucho para estos famosos getters/setters

se trata de concentrar ambos en un solo (al estilo + o - de .NET), por ejemplo:


<?php
class Usuario{
private $nombre;
private $edad;
/**
* getter / setter de $nombre
*/
public function Nombre($val2assign = null){
if (isset($val2assign)){
$this->nombre = $val2assign;
}
return $this->nombre;
}
/**
* getter / setter de $edad
*/
public function Edad($val2assign = null){
if (isset($val2assign)){
$this->nombre = $val2assign;
}
return $this->nombre;
}
}
?>

вoиιƒαcιo vεlαzquεz вαrcεиαs dijo...

fe de erratas:

Muy buen documento, me gustaria aportar un metodo ke me sirve mucho para estos famosos getters/setters

se trata de concentrar ambos en un solo (al estilo + o - de .NET), por ejemplo:


<?php
class Usuario{
private $nombre;
private $edad;
/**
* getter / setter de $nombre
*/
public function Nombre($val2assign = null){
if (isset($val2assign)){
$this->nombre = $val2assign;
}
return $this->nombre;
}
/**
* getter / setter de $edad
*/
public function Edad($val2assign = null){
if (isset($val2assign)){
$this->edad = $val2assign;
}
return $this->edad;
}
}
?>