En caso de duda, composiciÛn ( II )
øQuÈ pasa si nuestra jerarquÌa de clases es muy profunda o est· muy extendida?. Pues que corremos un riesgo muy grande de tener problemas si cambia el interfaz de la superclase.
Seguimos teniendo a nuestros programadores y nuestros camioneros levant·ndose por las maÒanas, yendo a trabajar, y volviendo a casa por las noches. Supongamos que en la clase base ( Persona ) hay un mÈtodo como Èste:
public funcion pagar( cant: Number ): Billete
{
return new Billete( cant );
}
De manera que, cuando cualquier persona tiene que pagar algo ( la comida, el autob˙s, etc ), lo hace entregando un billete de la cantidad que corresponda. Recordemos que tanto los programadores como los camioneros extienden de persona, por lo que ya habr·n heredado ese mÈtodo.
Por tanto, todas las clases que interact˙en con las personas ( programadores o camioneros ), tendr·n que estar preparados para poder recoger billetes como medio de pago.
Pero øquÈ ocurre si, por cualquier circunstancia ( necesidades del programa, Ûrdenes del cliente,Ö ) necesitamos cambiar la forma de pago?. Es decir, øquÈ pasarÌa si ahora, en vez de pagar con billetes, las personas deben pagar con monedas, o con tarjeta de crÈdito?. Pues cualquier cambio en el interfaz de la clase base, por pequeÒo que sea, se propagar· a las clases hijas, y puede dar lugar a efectos inesperados en cualquier parte de nuestro programa. Porque, por ejemplo, el autob˙s no se puede pagar con tarjeta de crÈdito ( aunque el restaurante sÌ ).
Por tanto, cualquier cambio, por pequeÒo que sea, en la interfaz de la clase base puede requerir que hagamos cambios en muchos otras partes de nuestra aplicaciÛn. Y eso hay que intentar evitarlo.
Veamos una forma de hacerlo ( como casi siempre, no la ˙nica ). Supongamos que delegamos la responsabilidad de realizar los pagos en otra clase, llamada PagosManager
class PagosManager
{
var saldo: Number;
function PagosManager( )
{
}
public function init( saldoInicial: Number )
{
this.saldo = saldoInicial;
}
public funcion pagar( cant: Number ): Billete
{
return new Billete( cant );
}
}
Y tanto las clases Programador como Camionero podrÌan implementar los pagos de la siguiente forma:
class Programador extends Persona
{
var pagosManager: PagosManager = new PagosManager( );
function Programador( )
{
this.pagosManager = new PagosManager( );
this.pagosManager.init( 1000 );
}
public function pagar( ): Billete
{
return this.pagosManager.pagar( 100 );
}
}
Y si necesit·ramos que el Programador pagara con tarjeta de crÈdito, el ˙nico cambio a realizar serÌa en la propia implementaciÛn de la clase Programador:
public function pagar( ): Tarjeta
{
return new Tarjeta( this.pagosManager.pagar( 100 ) );
}
El ejemplo no es demasiado bueno ( ni completo ), pero espero que pueda servir para entender el concepto que intento transmitir.
Igualmente, si hemos utilizado la herencia para reutilizar cÛdigo, nos podemos encontrar con el problema inverso: que necesitemos que alguna de las clases hijas cambie su perfil, para lo cual debamos cambiar el perfil de la clase base, con lo cual, inducimos cambios en el resto de clases hijas ( que pueden no ser bienvenidos por el compilador, o por el resto del programa ).
Ve·moslo con un ejemplo. Si la clase base ( Persona ), implementa el siguiente mÈtodo:
public function getDNI( ): String
{
return this.dni;
}
Ese mÈtodo estar· presente en la implementaciÛn de Programador y Camionero. Pero øquÈ pasa si al programador le pide su jefe que le diga su DNI pero encriptado por un algoritmo md5? ( lo sÈ, el ejemplo es malo, pero los jefes son capaces de cualquier cosa ). Pues que, al ser ese mÈtodo parte de la implementaciÛn heredada de la clase base, si cambiamos la implementaciÛn de ese mÈtodo, cambiar· tambiÈn para el Camionero, que empezar· a devolver su DNI encriptado. Obviamente, se puede sobrescribir el mÈtodo sÛlo en la clase Programador, pero para eso no hacÌa falta heredarlo, øno?, porque entonces ya no es funcionalidad com˙n, es interfaz com˙n, y eso, como dice la palabra, se puede hacer que sea parte de un interfaz com˙n para ambas clases hijas.
Y el tema todavÌa da para otro post