Omitir navegación.
Principal

Implementacion De Interfaces Implícitas


Personajes

Escrito por Martin Fowler
Traducido por Jorge Ferrer

(17 de Enero de 2006 – Actualización general a la luz de varios comentarios)

Tanto Java como C# tienen el mismo model de interfaces puras. Se declara una interfaz escribiendo interface Mailable, y después puede implementarse escribiendo class Customer implements Mailable (en Java). Una clase puede implementar varias interfaces.

Una de las cosas que este modelo ignora es que pueden tenerse interfaces implícitas en una clase. La interfaz implícita pública de la clase Customer la componen todos los métodos públicos de Customer. (Esta interfaz implícita está presente en todos los lenguajes orientados a objetos que he visto.) Una cosa que ni Java ni C# te permiten hacer es implementar una interfaz implícita – es decir no te permite escribir class ValuedCustomer implements Customer.

¿Qué significaría implementar una interfaz implícita? La esencia sería que le diría al sistema de tipado que la clase ValuedCustomer implementa todos los métodos declarados en la interfaz pública de Customer pero que no usa sus implementaciones, es decir los cuerpos de los métodos públicos y los datos y métodos no públicos. Dicho de otra manera se trata de herencia de interfaz pero no de implementación.

Sería equivalente a convertir Customer en una interfaz que contiene todos los métodos públicos de Customer y crear una clase CustomerImpl que implementa dicha interfaz.

¿Porqué sería esto útil? Un caso que recuerdo del pasado era en los viejos tiempos de Java, antes del framework de colecciones que tiene actualmente. Queríamos reemplazar la clase Vector con una implementación propia, pero no pudimos porque Vector era una clase [no una interfaz] y por tanto sólo era posible crear una subclase. De tanto en tanto te encuentras con casos como este en el que las librerías no proporcionan interfaces para permitir sustituciones libres y [en estos casos] estás bloqueado si no tienes está funcionalidad.

En estos momentos esto aparece en particular cuando se desarrollan pruebas. Muchas veces cuando quieres reemplazar una implementación por otra que la simula (stub) es dificil o imposible si no dispones de una interfaz. También lleva a definir interfaces sólo para permitir dicho reemplazo para las pruebas. Aunque usar un InterfaceImplementationPair es un enfoque habitual es uno del que muchos de nosotros no somos partidarios. Implementación de interfaces implícitas sería una aproximación mucho más limpia.

Así que ¿porqué los lenguajes no lo permiten? Realmente no lo se – pero yo no soy diseñador de lenguajes. Una vez pude preguntarle a Anders Heljsberg sobre este tema y su respuesta iba en la línea de que prefería que sólo fuera posible sobrecargar un miembro de una clase si era declarado como virtual explícitamente. Esta es en esencia una preocupación de que las subclases (o implementadores en nuestro caso) puedan romper la superclase, algo que toca un tema mucho más amplio sobre cómo usar herencia. De todas formas fue sólo una conversación breve en una cena así que no estoycompletamente convencido de que elaboráramos lo suficiente la discusión.

Después de escribir esta entrada, mi viejo amigo Mike Rettig me comentó que uno de los problemas es que las clases en realidad definen varias interfaces implícitas. En Java, por ejemplo, la clase Customer define cuatro interfaces implícitas: public, protected, package y private. Cuando un objeto interactúa con un Customer puede hacerlo a través de cualquiera de estas interfaces (otra instancia de Customer puede usar métodos o campos privados). Si queremos implementar una interfaz implícita debemos o bien implemtar todo o bien especificar cómo de lejos queremos llegar. No estoy seguro de cómo de difícil sería para los sistemas de tipado llevar un control de eso.

Por supuesto para los ejemplos que he dado usar únicamente interfaces implícitas públicas es suficiente así que quizá en la práctica eso sea suficiente.

Ian Griffiths apuntó que este sea un asunto sobre cómo mezclar clases e interfaces. La tecnología COM de Microsoft de hecho tiene una delgada separación entre las dos: “si quieres usar un objeto en COM tienes que hacerlo a través de una interfaz. Así­ que siempre es posible crear tu propia implementación”. Esto se realizaba de forma muy transparente en Visual Basic 6 (VB6) donde las interfaces COM podían generarse por debajo automáticamente.

Este asunto no aparece con lenguajes con tipado dinámico. Si quieres implementar la interfaz de otra clase basta con implemntar los mismos metodos y usar el objeto allá donde se necesite. No necesitas implemntar todos los métodos, sólo aquellos que vayan a ser usados en la interacción en concreto en la que estás interesado. Este es un esquema que funciona muy bien para pruebas. Los Smalltalkers [NDT: programadores habituales de Smalltalk] se referían a esto como el patrón Impostor (Imposter). También es bastante común usar proxies dinámicos para hacer este tipo de cosas en Java, aunque tengo la impresión de que la implementación de interfaces implícitos sería más comunicativa.

¿Realmente importa alto de esto? Fundamentalmente parece un problema para hacer pruebas, es mucho más difícil introducir un DobleParaPruebas si no puedes hacer una reimplementación – y a menudo crear una subclase no es suficiente si la superclase necesita una conexión real con una base de datos. Quizá este sea un tema que sólo afecta a las pruebas – Robert Conley me comentó que usa la capacidad de VB6 [comentada anteriormente] a menudo para hacer reimplementación para pruebas, pero nunca tuvo que usarla para código en producción.

Artículo Original