( Para el propósito de esta pregunta, cuando digo 'interfaz' me refiero a la construcción del lenguajeinterface
, y no a una 'interfaz' en el otro sentido de la palabra, es decir, los métodos públicos que una clase ofrece al mundo exterior para comunicarse con y manipularlo )
El acoplamiento flojo se puede lograr haciendo que un objeto dependa de una abstracción en lugar de un tipo concreto.
Esto permite un acoplamiento flojo por dos razones principales: 1- las abstracciones tienen menos probabilidades de cambiar que los tipos concretos, lo que significa que es menos probable que el código dependiente se rompa. Se pueden usar 2 tipos de concreto diferentes en tiempo de ejecución, porque todos se ajustan a la abstracción. También se pueden agregar nuevos tipos de concreto más adelante sin necesidad de alterar el código dependiente existente.
Por ejemplo, considere una clase Car
y dos subclases Volvo
y Mazda
.
Si su código depende de a Car
, puede usar a Volvo
o a Mazda
durante el tiempo de ejecución. También más adelante se podrían agregar subclases adicionales sin necesidad de cambiar el código dependiente.
Además, Car
que es una abstracción, es menos probable que cambie que Volvo
o Mazda
. Los autos han sido generalmente los mismos durante bastante tiempo, pero Volvos y Mazdas tienen muchas más probabilidades de cambiar. Es decir, las abstracciones son más estables que los tipos concretos.
Todo esto fue para demostrar que entiendo qué es el acoplamiento flojo y cómo se logra al depender de abstracciones y no de concreciones. (Si escribí algo inexacto, por favor dígalo).
Lo que no entiendo es esto:
Las abstracciones pueden ser superclases o interfaces.
Si es así, ¿por qué se elogia específicamente a las interfaces por su capacidad de permitir un acoplamiento suelto? No veo cómo es diferente que usar una superclase.
Las únicas diferencias que veo son: 1- Las interfaces no están limitadas por una sola herencia, pero eso no tiene mucho que ver con el tema del acoplamiento flexible. 2- Las interfaces son más 'abstractas' ya que no tienen lógica de implementación en absoluto. Pero aún así, no veo por qué eso hace una gran diferencia.
Explíqueme por qué se dice que las interfaces son excelentes para permitir un acoplamiento flojo, mientras que las superclases simples no lo son.
fuente
interfaces are essential for single-inheritance languages like Java and C# because that's the only way in which you can aggregate different behaviors into a single class
(lo que me lleva a la comparación con C ++, donde las interfaces son solo clases con funciones virtuales puras).Respuestas:
Terminología: me referiré a la construcción del lenguaje
interface
como interfaz , y a la interfaz de un tipo u objeto como superficie (a falta de un término mejor).Correcto.
No del todo correcto. Los lenguajes actuales generalmente no anticipan que una abstracción cambiará (aunque hay algunos patrones de diseño para manejar eso). Separar detalles de cosas generales es abstracción. Esto generalmente se hace mediante una capa de abstracción . Esta capa se puede cambiar a otros detalles sin romper el código que se basa en esta abstracción: se logra un acoplamiento flexible. Ejemplo que no es OOP: una
sort
rutina podría cambiarse de Quicksort en la versión 1 a Tim Sort en la versión 2. El código que solo depende del resultado que se está ordenando (es decir, se basa en lasort
abstracción) se desacopla de la implementación de clasificación real.Lo que llamé superficie arriba es la parte general de una abstracción. Ahora sucede en OOP que un objeto a veces debe soportar múltiples abstracciones. Un ejemplo no bastante óptimo: Java
java.util.LinkedList
admite tanto laList
interfaz que trata sobre la abstracción de "colección ordenada e indexable", como laQueue
interfaz que (en términos generales) trata sobre la abstracción "FIFO".¿Cómo puede un objeto soportar múltiples abstracciones?
C ++ no tiene interfaces, pero tiene herencia múltiple, métodos virtuales y clases abstractas. Una abstracción se puede definir como una clase abstracta (es decir, una clase que no se puede instanciar de inmediato) que declara, pero no define métodos virtuales. Las clases que implementan los detalles de una abstracción pueden heredar de esa clase abstracta e implementar los métodos virtuales requeridos.
El problema aquí es que la herencia múltiple puede conducir al problema del diamante , donde el orden en el que se buscan las clases para la implementación de un método (MRO: orden de resolución del método) puede conducir a "contradicciones". Hay dos respuestas a esto:
Defina un orden sensato y rechace aquellos que no pueden ser linealmente sensatos. El C3 MRO es bastante sensible y funciona bien. Fue publicado en 1996.
Tome la ruta fácil y rechace la herencia múltiple en todo momento.
Java tomó la última opción y eligió la herencia de comportamiento único. Sin embargo, todavía necesitamos la capacidad de un objeto para soportar múltiples abstracciones. Por lo tanto, deben usarse interfaces que no admiten definiciones de métodos, solo declaraciones.
El resultado es que el MRO es obvio (solo mira cada superclase en orden), y que nuestro objeto puede tener múltiples superficies para cualquier cantidad de abstracciones.
Esto resulta ser bastante insatisfactorio, porque a menudo un poco de comportamiento es parte de la superficie. Considere una
Comparable
interfaz:Esto es muy fácil de usar (una buena API con muchos métodos convenientes), pero es tedioso de implementar. Nos gustaría que la interfaz solo incluya
cmp
e implemente los otros métodos automáticamente en términos de ese método requerido. Los mixins , pero más importante, los rasgos [ 1 ], [ 2 ] resuelven este problema sin caer en las trampas de la herencia múltiple.Esto se hace definiendo una composición de rasgos para que los rasgos no terminen participando en el MRO; en cambio, los métodos definidos se componen en la clase de implementación.
La
Comparable
interfaz podría expresarse en Scala comoCuando una clase usa ese rasgo, los otros métodos se agregan a la definición de clase:
Así
Inty(4) cmp Inty(6)
sería-2
yInty(4) lt Inty(6)
seríatrue
.Muchos idiomas tienen algún soporte para rasgos, y cualquier lenguaje que tenga un "Protocolo de Metaobjetos (MOP)" puede tener rasgos agregados. La reciente actualización de Java 8 agregó métodos predeterminados que son similares a los rasgos (los métodos en las interfaces pueden tener implementaciones alternativas para que sea opcional implementar clases para implementar estos métodos).
Desafortunadamente, los rasgos son una invención bastante reciente (2002) y, por lo tanto, son bastante raros en los idiomas principales más grandes.
fuente
Primero, el subtipo y la abstracción son dos cosas diferentes. Subtipar simplemente significa que puedo sustituir los valores de un tipo por valores de otro tipo; ninguno de los tipos debe ser abstracto.
Más importante aún, las subclases dependen directamente de los detalles de implementación de su superclase. Ese es el tipo de acoplamiento más fuerte que existe. De hecho, si la clase base no está diseñada teniendo en cuenta la herencia, los cambios en la clase base que no cambian su comportamiento aún pueden romper las subclases, y no hay forma de saber a priori si se producirá una ruptura. Esto se conoce como el problema de la clase base frágil .
La implementación de una interfaz no lo acopla a nada, excepto a la interfaz en sí, que no contiene ningún comportamiento.
fuente
you want an object named A to depend on an abstraction named B instead of a concrete implementation of that abstraction named C
que estás asumiendo que las clases de alguna manera no son abstractas. Una abstracción es cualquier cosa que oculta detalles de implementación, por lo que una clase con campos privados es tan abstracta como una interfaz con los mismos métodos públicos.Existe un acoplamiento entre las clases padre e hijo, ya que el hijo depende del padre.
Digamos que tenemos una clase A, y la clase B hereda de ella. Si entramos en la clase A y cambiamos las cosas, la clase B también cambia.
Digamos que tenemos una interfaz I, y la clase B la implementa. Si cambiamos la interfaz I, aunque la clase B podría no implementarla más, la clase B no cambia.
fuente