Mientras escribo código OOP aplico algunas prácticas de Elegant Objects.
Una de ellas es que las clases deben ser definitivas. Esto quiere decir que no pueden extenderse por herencia sino sólo por composición.
La ventaja es la simplicidad. Quiero decir que, de esta manera, cada objeto se ve como un bloque cohesivo. Lo que interesa a sus clientes es su comportamiento expuesto. Nada mas. En cambio, a través de la extensión, un cliente puede romperlo.
Por ejemplo, un objeto puede interrelacionar dos de sus métodos. Entonces, si podemos reemplazar a través de la extensión de uno de ellos, podemos romper el otro. Por esta razón, para estar seguros, debemos verificar su implementación. De esta forma aumentamos el acoplamiento entre el extendido y la extensión.
En otras palabras, las clases finales refuerzan la idea de que solo debemos preocuparnos por el comportamiento expuesto. Y no de implementación. No obstante, requiere un cambio de cómo razonamos sobre ellos. El patrón Alias simplifica un aspecto de este cambio.
El patrón Alias permite extender la forma en que una clase puede construir sus objetos sin subclasificarlos o modificarlos.
Supongamos una clase final que crea sus objetos usando algunos parámetros obligatorios. ¿Cómo podemos agregar otra forma de crear sus objetos? Por ejemplo, ¿cómo podemos agregar un constructor que use un valor predeterminado para uno o más de sus parámetros faltantes?
Un enfoque podría ser agregar otro constructor a la clase. Pero esto podría salirse de control. Además, podría no ser posible. Por ejemplo, la clase final antes mencionada podría estar en una biblioteca externa.
Otro inconveniente de este enfoque es que podemos contaminar la clase final. Por ejemplo, podemos tener una clase final que construya sus objetos dado un JSON. Pero después de un tiempo necesitamos agregar también XML. Como puede imaginar, agregar el código para mapear XML a JSON contaminará inevitablemente esa clase.
Sin embargo, el patrón Alias no se limita a las clases finales. Por ejemplo, no podemos tener dos constructores con los mismos parámetros pero diferente semántica.
Para resolver este problema, podemos agregar métodos de fábrica estáticos al código de la clase. Pero los mismos inconvenientes antes mencionados afectan a este enfoque. Es decir: esto se sale de control; esto no podría ser siempre posible; esto contaminará la clase.
Un mejor enfoque para ambos problemas es crear otra clase con el comportamiento de constructor deseado. Esta clase encapsula su propia lógica de construcción. Y delegará todo a la otra clase, incluida la creación real. Este es el patrón Alias.
Utilice el patrón Alias cuando:
Debe agregar o modificar un constructor de una clase final;
Desea agregar o modificar un constructor de una clase sin modificarlo ni subclasificarlo;
Necesita dos o más constructores de una clase con los mismos parámetros sin modificarlos ni subclasificarlos.
La estructura es sencilla. Necesitamos al menos dos clases que implementen la misma interfaz: un Alias
y un Aliased.
AnInterface
declara una interfaz.
Aliased
implementa AnInterface
;
expone uno o más constructores.
Alias
AnInterface
;Aliased
;Aliased
al que se hace referencia.Alias
construye, según sus propias reglas, un objeto Aliased
y mantiene una referencia del mismo. Luego delega todo al alias.
El Patrón Alias tiene las siguientes consecuencias:
Para implementar el patrón Alias necesitas:
para definir una interfaz;
para implementar la interfaz previamente definida con una clase. Este será el alias;
para implementar la interfaz previamente definida con una clase de alias, y necesita:
El siguiente código Java-ish expresa el patrón Alias. En este código, el alias inyecta un valor predeterminado para un parámetro obligatorio:
interface AnInterface { void aMethod(); Something anotherMethod(); } final class Aliased implements AnInterface { private final A a; private final B b; Aliased(final A a, final B b) { this.a = a; this.b = b; } void aMethod() { // implementation } Something anotherMethod() { // implementation } } final class Alias implements AnInterface { private final Aliased aliased; Alias(final A a) { this( new Aliased( a, new InstanceOfB(...) ) ); } private Alias(final Aliased aliased) { this.aliased = aliased; } void aMethod() { this.aliased.aMethod(); } Something anotherMethod() { return this.aliased.anotherMethod(); } }
Hasta cierto punto, el patrón Alias también podría verse como una forma de decorar la construcción de los objetos. Esta visión es especialmente cierta si vemos una clase como un objeto cuya responsabilidad es crear objetos.