¿Por qué un programador querría separar la implementación de la interfaz?

18

El patrón de diseño del puente separa la implementación de la interfaz de un programa.

¿Por qué es esto ventajoso?

David Faux
fuente
Para evitar una fuga de abstracción
SK-logic
1
Esto parece ser entendido por todos, pero para los nuevos espectadores, Bridge Design no se trata de la implementación y la interfaz "de un programa" sino de partes, quizás incluso partes pequeñas, de un programa. Todo un programa será una colección de interfaces e implementaciones (cada interfaz tendrá una o más implementaciones). La primera oración quizás debería ser: "El patrón de diseño del puente separa las interfaces de sus implementaciones en todo el código fuente".
RalphChapin

Respuestas:

35

Le permite cambiar la implementación independientemente de la interfaz. Esto ayuda a lidiar con los requisitos cambiantes.

El ejemplo clásico es reemplazar la implementación de almacenamiento en una interfaz con algo más grande, mejor, más rápido, más pequeño o diferente sin tener que cambiar el resto del sistema.

Daniel Pittman
fuente
23

Además de la respuesta de Daniel, separar la interfaz de la implementación a través de conceptos como el polimorfismo le permite crear varias implementaciones de la misma interfaz que hacen cosas similares de diferentes maneras.

Por ejemplo, muchos idiomas tienen el concepto de una secuencia en algún lugar de la biblioteca estándar. Una secuencia es algo que contiene datos para el acceso en serie. Tiene dos operaciones básicas: Lectura (carga el próximo número X de bytes de la secuencia) y Escritura (agrega X número de bytes de datos a la secuencia), y a veces un tercero, Búsqueda (restablece la "posición actual" de la secuencia a una nueva ubicación).

Es un concepto bastante simple, pero piense en todas las cosas que podría hacer con él. La más obvia es interactuar con archivos en el disco. Una secuencia de archivos le permitiría leer datos de un archivo o escribir en él. Pero, ¿qué sucede si desea enviar datos a través de una conexión de red?

Si confiaba en implementaciones directamente, tendría que escribir dos rutinas completamente diferentes para guardar los mismos datos en un archivo o enviarlos a través de la red. Pero si tiene una interfaz de transmisión, puede crear dos implementaciones diferentes de la misma ( FileStreamy NetworkStream) que encapsulan los detalles específicos del envío de los datos a donde debe ir, y luego solo necesita escribir el código que se ocupa de guardar un archivo una vez . De repente, tu SaveToFiley las SendOverNetworkrutinas son mucho más simples: solo configuran un flujo del tipo apropiado y lo pasan a la SaveDatarutina, que acepta una interfaz de flujo; no necesita preocuparse de qué tipo, siempre que pueda realizar el Operación de escritura, y guarda los datos en la secuencia.

Eso también significa que si su formato de datos cambia, no tiene que cambiarlo en varios lugares diferentes. Si centraliza su código de ahorro de datos en una rutina que toma una secuencia, entonces ese es el único lugar donde necesita actualizarse, por lo que no puede introducir un error accidentalmente cambiando solo un lugar cuando necesita cambiar ambos. Por lo tanto, separar las interfaces de las implementaciones y usar el polimorfismo hace que el código sea más fácil de leer y comprender, y que sea menos probable que tenga errores.

Mason Wheeler
fuente
1
Esta es una de las características más fuertes de OOP. Simplemente me encanta ...
Radu Murzea
1
¿Cómo es esta forma diferente usando interfaces simples?
vainolo
@Vainolo: Ayuda con la reutilización del código. Incluso si tiene múltiples tipos de transmisiones, todas harán las mismas cosas entre sí. Si comienza con una IStreaminterfaz, debe reinventar todo el conjunto de funcionalidades para cada transmisión. Pero si comienza con una clase de flujo abstracto base, puede contener todos los estados y funcionalidades comunes, y luego dejar que los descendientes implementen las distintas características.
Mason Wheeler
2
@RaduMurzea esto no es específico de OOP. Las clases de tipos te permiten hacer lo mismo de una manera completamente distinta a OOP.
Wes
@Wes exactamente, solo quería decir lo mismo, y luego leí tu comentario.
jhegedus
4

Realmente tienes dos preguntas muy diferentes aquí, aunque están relacionadas.

La pregunta más general es la del título, ¿por qué separaría la interfaz de la implementación en general? La segunda pregunta es por qué el patrón del puente es útil. Están relacionados porque el patrón de puente es una forma específica de separar la interfaz de la implementación, que también tiene otras consecuencias específicas.

La pregunta general es algo vital para que cada programador entienda. Es lo que evita que los cambios en un programa se propaguen a todas partes. No puedo imaginar que los humanos puedan programar sin usar esto.

Cuando escribe una declaración de suma simple en un lenguaje de programación, eso ya es una abstracción (incluso si no está utilizando la sobrecarga del operador para agregar matrices o algo así) que pasa por un montón de otro código antes de que finalmente se ejecute en un circuito en tu computadora. Si no hubo separación de la interfaz (por ejemplo, "3 + 5"), de la implementación (un montón de código de máquina), entonces tendría que cambiar su código cada vez que la implementación cambiara (como si quisiera ejecutarse en un nuevo procesador).

Incluso dentro de una aplicación CRUD simple, cada firma de método es, en un sentido amplio, la interfaz para su implementación.

Todos estos tipos de abstracción tienen el mismo objetivo básico: hacer que el código de llamada exprese su intención de la manera más abstracta posible que le da al implementador tanta información como sea necesaria. Eso proporciona el mínimo acoplamiento posible entre ellos y limita el efecto dominó cuando el código debe cambiarse tanto como sea posible.

Suena sencillo, pero en la práctica se complica.

El patrón de puente es una forma específica de separar ciertos bits de implementación en interfaces. El diagrama de clase del patrón es más informativo que la descripción. Es más como una forma de tener módulos conectables que un puente, pero lo llamaron puente porque a menudo se usa donde los módulos se han creado antes de la interfaz. Por lo tanto, la creación de una interfaz común para implementaciones similares similares "cierra" la diferencia y le permite al código trabajar con cualquiera de las implementaciones.

Entonces, supongamos que desea escribir un complemento en un procesador de textos, pero desea que funcione en múltiples procesadores de texto. Puede crear una interfaz que resuma la funcionalidad basada en el procesador de texto que necesita (y que debe ser implementable por cada procesador de texto ya que no puede cambiarlas), y un implementador de esa interfaz para cada procesador de texto que desee admitir. Entonces su aplicación puede llamar a esa interfaz y no preocuparse por los detalles de cada procesador de texto.

En realidad, es un poco más detallado que eso, porque cada clase realmente podría ser una jerarquía de clases (por lo tanto, puede que no solo haya un procesador de texto abstracto, sino un documento abstracto, una selección de texto abstracta, etc., con implementaciones concretas para cada uno), pero es La misma idea.

Es un poco como una fachada, excepto que en este caso la capa de abstracción se centra en proporcionar la misma interfaz a múltiples sistemas subyacentes.

Está relacionado con la Inversión de control, ya que el implementador concreto se pasará a métodos o constructores y determinará la implementación real llamada.

psr
fuente