PHP orientado a objetos 2: ocultación, getter y setter

Segunda entrada de la serie dedicada a PHP orientada a objetos: la ventaja de ocultar las cosas.

hiroshige

archivado en: PHP/AJAX / 28 febrero, 2013 / taller:

En la primera entrada de esta serie dedicada a PHP orientado a objetos vimos qué una de las virtudes de esta manera de programar era tener «encapsulados», agrupados, los métodos (funciones) y propiedades (variables) de una misma entidad, es decir, las cosas que sirven para lo mismo.

Una de las ventajas de encapsular los elementos de una entidad es que podemos «ocultar», proteger, los que no se necesitan fuera de la clase. Esto nos puede parecer trivial para los que venimos del desarrollo de webs normales, páginas corporativas de contenidos, en las que normalmente escribe uno mismo desde la primera hasta la última línea del código, pero cuando la aplicación es monstruosa e intervienen varias manos resulta más clara su utilidad.

Entao, como vimos,  las propiedades de una clase pueden ser públicas (public), protegidas (protected) o privadas (private). Es lo que se conoce como «métodos de acceso». Según se especifique uno u otro a la hora de declarar la propiedad, se podrá «acceder» a ella desde fuera de la clase o no.

Por ejemplo, si tuviéramos esta clase:

  • class MetodosAcceso {
  • // esta propiedad es pública, accesible desde fuera
  • public $variable1 = 1;
  • // esta propiedad está protegida, solo es accesible dentro de la clase
  • private $variable2 = 2;
  • // esta propiedad es privada, accesible en la clase y sus subclases
  • protected $variable3 = 3;
  • }

Desde fuera de la clase, solo podríamos acceder a la primera propiedad, la que está declarada como public. Así, esto:

  • $compruebaMetodos = new MetodosAcceso();
  • echo $compruebaMetodos->variable1;

devolvería en pantalla «1».

Pero si intentáramos hacer lo mismo con las otras dos o nos daría un error fatal y con cierta impudicia el servidor nos respondería que «Cannot access private property».

Lo normal es que declaremos las propiedades protected o private, dado que para eso están las clases, y solo permitamos que se accedan a las que se necesitan de verdad fuera y cuando y cómo se necesiten; para lo cual conviene emplear los denominados métodos getter (que se podría traducir como accesor o lector) y setter, (definidor o modificador).

Mediante el método que se usa de getter se permite leer, y solo leer, las propiedades que se necesiten y mediante el método que se emplee de setter, el modificarlas. Y solo se podrán leer o modificar desde fuera a partir de estos dos métodos.

Esto queda claro con el siguiente ejemplo utilisérrimo en la vida real. Imaginemos que tenemos una clase con dos propiedades, en una se define la velocidad de los triceratops y en otra, mediante un array, lo que comen.

  • class Triceratops {
  • private $velocidad = 20;
  • private $alimentacion = array ("helechos", "eucaliptus", "musgo");
  • }

Años de investigación nos han permitido saber con certeza cuál es su velocidad media, pero no estamos tan seguros de lo que comían realmente, por lo que de momento solo queremos que se pueda saber desde fuera cuánto corrían. Así, vamos a crear un método getter que lea la velocidad, mientras que la alimentación quedará oculta pues —al ser una propiedad private y carecer de ningún método que la lea—, no hay forma de acceder a ella desde fuera de la clase.

  • class Triceratops {
  • private $velocidad = 20;
  • private $alimentacion = array ("helechos", "eucaliptus", "musgo");
  • public function getVelocidad() {
  • return $this->velocidad;
  • }
  • }
  • $mi_Triceratops = new Triceratops();
  • echo $mi_Triceratops->getVelocidad();
  • // obtendríamos 20.

Y, si lo que queremos es modificar la velocidad, podemos hacerlo mediante un método con funciones setter:

  • class Triceratops {
  • private $velocidad = 20;
  • private $alimentacion = array ("helechos", "eucaliptus", "musgo");
  • public function getVelocidad() {
  • return $this->velocidad;
  • }
  • public function setVelocidad($nueva_velocidad) {
  • $this->velocidad = $nueva_velocidad; 
  • }
  • }
  • $mi_Triceratops = new Triceratops();
  • $mi_Triceratops->setVelocidad(30);
  • echo $mi_Triceratops->getVelocidad();
  • // ahora obtendríamos 30.

Una ventaja de hacerlo así, de concentrar el acceso a las propiedades a partir de un solo método público, es que si luego queremos aplicar una restricción o cualquier otra historia antes de cambiar la propiedad, solo tenemos que acudir a un sitio.

Los métodos mágicos __get y __set

En la entrada anterior conocimos el método «mágico» __construct, que servía para inicializar una clase sin necesidad de llamar a ningún método en concreto. No es el único. Hay otros métodos «mágicos», que es como se denominan unos métodos especiales que hacen cosas de forma genérica, sin necesidad de especificar el nombre de la función en concreto. Se reconocen porque empiezan siempre por dos guiones bajos: __nombreMétodoMágico.

Hay dos métodos mágicos que cumplen las funciones de getter y setter, que son respectivamente __get y __set.

Mediante __get podemos leer cualquier propiedad de una clase, aunque sea private o protected.

  • class Triceratops {
  • private $velocidad = 20;
  • private $alimentacion = array ("helechos", "eucaliptus", "musgo");
  • public function __get($name) {
  • return $this->$name;
  • }
  • }
  • $mi_Triceratops = new Triceratops();
  • // esto devolvería 20
  • echo $mi_Triceratops->velocidad;
  • // y esto otro "helechos"
  • echo $mi_Triceratops-> alimentacion[0];

Llamar $name al parámetro de __get da igual, es una convención, ahí podríamos haber puesto $turumbulillos. Lo  importante es fijarse en que con este método podemos acceder a cualquier propiedad de la clase Tricerarops, sea o no pública.

El método __set funciona de manera similar, solo que hay que pasarle dos parámetros, uno con la propiedad a modificar y otro con el valor que debe tomar esa propiedad.

  • class Triceratops {
  • private $velocidad = 20;
  • private $alimentacion = array ("helechos", "eucaliptus", "musgo");
  • public function __get($name) {
  • return $this->$name;
  • }
  • public function __set($name, $value) { 
  • return $this->$name = $value;
  • }
  • }
  • $mi_Triceratops = new Triceratops();
  • // cambiamos la velocidad a 30
  • $mi_Triceratops->velocidad = 30;
  • echo $mi_Triceratops->velocidad;

El problema de usar estos dos métodos mágicos, como ya se habrá deducido, es que se puede acceder a todas las propiedades, que para el caso es como si se hubieran declarado públicas, perdiendo así las ventajas de la ocultación.

Retomemos el ejemplo del facturómetro del post anterior para terminar de comprender las ventajas de que algunas propiedades permanezcan ocultas y solo se pueda acceder a las demás mediante métodos específicos.

Recordemos que era una aplicación que calculaba el importe que nos quedaba en una factura una vez que hubiéramos deducido el impuesto IRPF.

  • class Facturometro {
  • private $irpf; // La cantidad a descontar
  • private $bruto; // El total a pagar
  • private $limpio; // La miseria que te queda
  • // Recogemos los datos inicializando el nuevo objeto
  • public function __construct ($rec_irpf, $rec_bruto) {
  • $this->irpf = $rec_irpf;
  • $this->bruto = $rec_bruto;
  • }
  • public function devuelveFactura () {
  • $irpf_a_descontar = ($this->bruto*$this->irpf)/100;
  • $this->limpio = $this->bruto-$irpf_a_descontar;
  • return $this->limpio;
  • }
  • }
  • //Creamos el objeto $nueva_factura enviándole el IRPF y la cantidad bruta
  • $nuevaFactura = new Facturometro (21, 1000);
  • // Llamamos al método devuelveFactura
  • echo $nuevaFactura ->devuelveFactura ();

Imaginemos ahora que en vez de ser el usuario quien indica el IRPF a descontar, este debe ser siempre el mismo, el indicado por Hacienda y no queremos que nadie lo varíe accidentalmente. Si usáramos el método __set o si le pasáramos ese parámetro en el constructor, tal y como está escrito, estaríamos permitiendo que alguien lo cambiase. En cambio, de esta manera...

  • class Facturometro {
  • private $irpf = 21; // La cantidad a descontar
  • private $bruto; // El total a pagar
  • private $limpio; // La miseria que te queda
  • // El IRPF se puede leer, pero no modificar
  • public function getIrpf () {
  • return $this->irpf;
  • }
  • // El bruto se puede cambiar
  • public function setBruto ($nuevo_bruto) {
  • $this->bruto = $nuevo_bruto;
  • }
  • public function devuelveFactura () {
  • $irpf_a_descontar = ($this->bruto*$this->irpf)/100;
  • $this->limpio = $this->bruto-$irpf_a_descontar;
  • return $this->limpio;
  • }
  • }
  • // Creamos el objeto $nueva_factura sin enviar nada
  • $nuevaFactura = new Facturometro ();
  • // Indicamos el bruto con un método con funciones setter
  • $nuevaFactura->setBruto(100);
  • // Llamamos al método devuelve_factura
  • echo $nuevaFactura->devuelveFactura ();

...el IRFP permanece «oculto», es decir, protegido, ya que es una propiedad privada a la que no se puede acceder por ningún método público. El usuario solo puede modificar la cantidad a cobrar, por lo que la aplicación ha ganado en integridad.

En síntesis:

  • Conviene declarar siempre todas las propiedades protegidas o privadas, ya que una gran ventaja de la encapsulación es la posibilidad de ocultar lo que no se necesita fuera.
  • Si alguna se necesita leer desde fuera, crearemos un método con funciones getter específico.
  • Si hay que cambiar el valor de alguna desde fuera, crearemos un método específico con funciones setter.

|| Tags: , , , , , ,

valoración de los lectores sobre PHP orientado a objetos 2: ocultación, getter y setter

  • estrellica valoración positiva
  • estrellica valoración positiva
  • estrellica valoración positiva
  • estrellica valoración positiva
  • estrellica valoración positiva
  • 4.8 sobre 5 (40 votos)

¿Te ha parecido útil o interesante esta entrada?
dormido, valoración 1 nadapensativo, valoración 2 un poco sonrisa, valoración 3 a medias guiño, valoración 4 bastante aplauso, valoración 5 mucho

Tú opinión es muy importante, gracias por compartirla!

Los comentarios están cerrados.