Me he encontrado con el término "programar para una interfaz en lugar de una implementación", y creo que entiendo lo que significa. Pero quiero asegurarme de entender sus beneficios y sus posibles implementaciones.
"Programación en una interfaz" significa que, cuando sea posible, uno debe referirse a un nivel más abstracto de una clase (una interfaz, clase abstracta o, a veces, una superclase de algún tipo), en lugar de referirse a una implementación concreta.
Un ejemplo común en Java es usar:
List myList = new ArrayList();
en lugar de ArrayList myList = new ArrayList();
.
Tengo dos preguntas con respecto a esto:
Quiero asegurarme de comprender los principales beneficios de este enfoque. Creo que los beneficios son principalmente flexibilidad. Declarar un objeto como una referencia de más alto nivel, en lugar de una implementación concreta, permite una mayor flexibilidad y mantenimiento durante todo el ciclo de desarrollo y en todo el código. ¿Es esto correcto? ¿Es la flexibilidad el principal beneficio?
¿Hay más formas de 'programar en una interfaz'? ¿O es "declarar una variable como una interfaz en lugar de una implementación concreta" la única implementación de este concepto?
No estoy hablando de la interfaz de construcción Java . Estoy hablando del principio OO "programar en una interfaz, no en una implementación". En este principio, la "interfaz" mundial se refiere a cualquier "supertipo" de una clase : una interfaz, una clase abstracta o una superclase simple que es más abstracta y menos concreta que sus subclases más concretas.
fuente
Respuestas:
Esto no es correcto . O al menos, no es del todo correcto.
El punto más importante proviene de una perspectiva de diseño de programa. Aquí, "programar en una interfaz" significa enfocar su diseño en lo que está haciendo el código, no en cómo lo hace. Esta es una distinción vital que empuja su diseño hacia la corrección y la flexibilidad.
La idea principal es que los dominios cambian mucho más lentamente que el software. Digamos que tiene un software para realizar un seguimiento de su lista de compras. En los años 80, este software funcionaría contra una línea de comando y algunos archivos planos en disquete. Entonces tienes una interfaz de usuario. Entonces, tal vez ponga la lista en la base de datos. Más tarde, tal vez se trasladó a la nube o los teléfonos móviles o la integración de Facebook.
Si diseñó su código específicamente en torno a la implementación (disquetes y líneas de comando) no estaría preparado para los cambios. Si diseñó su código alrededor de la interfaz (manipulando una lista de compras), entonces la implementación es libre de cambiar.
fuente
List myList = new ArrayList()
lugar deArrayList myList = new ArrayList()
. (La pregunta está en el siguiente comentario)List
/ArrayList
No es lo que estoy hablando en absoluto. Es más como proporcionar unEmployee
objeto en lugar del conjunto de tablas vinculadas que usa para almacenar un registro de Empleado. O proporcionar una interfaz para iterar a través de las canciones, y no preocuparse si esas canciones se mezclan, o en un CD, o se transmiten desde Internet. Son solo una secuencia de canciones.Mi comprensión de "programar en una interfaz" es diferente de lo que sugieren la pregunta u otras respuestas. Lo que no quiere decir que mi comprensión sea correcta, o que las cosas en las otras respuestas no sean buenas ideas, solo que no son lo que pienso cuando escucho ese término.
La programación en una interfaz significa que cuando se le presenta alguna interfaz de programación (ya sea una biblioteca de clases, un conjunto de funciones, un protocolo de red o cualquier otra cosa), debe usar solo las cosas garantizadas por la interfaz. Es posible que tenga conocimiento sobre la implementación subyacente (puede que la haya escrito), pero nunca debe usar ese conocimiento.
Por ejemplo, supongamos que la API le presenta un valor opaco que es un "identificador" de algo interno. Su conocimiento podría decirle que este identificador es realmente un puntero, y podría desreferenciarlo y acceder a algún valor, lo que podría permitirle realizar fácilmente alguna tarea que desee hacer. Pero la interfaz no le brinda esa opción; es su conocimiento de la implementación particular que lo hace.
El problema con esto es que crea un fuerte acoplamiento entre su código y la implementación, exactamente lo que se suponía que la interfaz debía evitar. Dependiendo de la política, puede significar que la implementación ya no se puede cambiar, porque eso rompería su código, o que su código es muy frágil y sigue rompiendo en cada actualización o cambio de la implementación subyacente.
Un gran ejemplo de esto son los programas escritos para Windows. WinAPI es una interfaz, pero muchas personas usaron trucos que funcionaron debido a la implementación particular en, digamos Windows 95. Estos trucos quizás hicieron que sus programas fueran más rápidos o les permitieron hacer cosas en menos código de lo que hubiera sido necesario. Pero estos trucos también significaron que el programa colapsaría en Windows 2000, porque la API se implementó de manera diferente allí. Si el programa fuera lo suficientemente importante, Microsoft podría continuar y agregar algún truco a su implementación para que el programa continúe funcionando, pero el costo de esto es una mayor complejidad (con todos los problemas posteriores) del código de Windows. También hace la vida más difícil para la gente de Wine, porque también intentan implementar WinAPI, pero solo pueden consultar la documentación para saber cómo hacerlo,
fuente
Solo puedo hablar de mi experiencia personal, ya que nunca me la han enseñado formalmente.
Tu primer punto es correcto. La flexibilidad obtenida proviene de no poder invocar accidentalmente detalles de implementación de la clase concreta donde no deberían invocarse.
Por ejemplo, considere una
ILogger
interfaz que se implementa actualmente como unaLogToEmailLogger
clase concreta . LaLogToEmailLogger
clase expone todos losILogger
métodos y propiedades, pero también tiene una propiedad específica de implementaciónsysAdminEmail
.Cuando se utiliza su registrador en su aplicación, no debería ser la preocupación del código consumidor configurar el
sysAdminEmail
. Esta propiedad debe establecerse durante la configuración del registrador y debe ocultarse del mundo.Si estaba codificando contra la implementación concreta, podría establecer accidentalmente la propiedad de implementación al usar el registrador. Ahora, su código de aplicación está estrechamente acoplado a su registrador, y cambiar a otro registrador requerirá primero desacoplar su código del original.
En este sentido, la codificación de una interfaz afloja el acoplamiento .
Con respecto a su segundo punto: Otra razón que he visto para codificar una interfaz es reducir la complejidad del código.
Por ejemplo, imagina que tengo un juego con las siguientes interfaces
I2DRenderable
,I3DRenderable
,IUpdateable
. No es raro que los componentes de un solo juego tengan contenido renderizable en 2D y 3D. Otros componentes pueden ser solo 2D y otros solo 3D.Si un módulo realiza la representación en 2D, entonces tiene sentido que mantenga una colección de
I2DRenderable
s. No importa si los objetos de su colección también lo sonI3DRenderable
oIUpdateble
si otros módulos serán responsables de tratar esos aspectos de los objetos.Almacenar los objetos renderizables como una lista de
I2DRenderable
mantiene baja la complejidad de la clase de renderizado. La lógica de renderizado y actualización 3D no es de su incumbencia, por lo que esos aspectos de sus objetos secundarios pueden y deben ignorarse.En este sentido, la codificación de una interfaz mantiene baja la complejidad al aislar las preocupaciones .
fuente
Quizás hay dos usos de la palabra interfaz que se usa aquí. La interfaz a la que se refiere principalmente en su pregunta es una interfaz Java . Ese es específicamente un concepto de Java, más generalmente es una interfaz de lenguaje de programación.
Diría que programar en una interfaz es un concepto más amplio. Las API REST ahora populares que están disponibles para muchos sitios web son otro ejemplo del concepto más amplio de programación para una interfaz en un nivel superior. Al crear una capa entre el funcionamiento interno de su código y el mundo exterior (personas en Internet, otros programas, incluso otras partes del mismo programa) puede cambiar cualquier cosa dentro de su código siempre que no cambie lo que el mundo exterior está esperando, donde eso está definido por una interfaz o contrato que pretendes cumplir.
Eso le proporciona la flexibilidad para refactorizar su código interno sin tener que contar todas las demás cosas que dependen de su interfaz.
También significa que su código debería ser más estable. Al apegarse a la interfaz, no debería romper el código de otras personas. Cuando realmente tiene que cambiar la interfaz, puede lanzar una nueva versión principal (1.abc a 2.xyz) de la API que indica que hay cambios importantes en la interfaz de la nueva versión.
Como @Doval señala en los comentarios sobre esta respuesta, también hay una localidad de errores. Creo que todo esto se reduce a la encapsulación. Tal como lo usaría para objetos en un diseño orientado a objetos, este concepto también es útil en un nivel superior.
fuente
Una analogía del mundo real podría ayudar:
Un enchufe de electricidad de red en una interfaz.
Sí; esa cosa de tres clavijas en el extremo del cable de alimentación de su televisor, radio, aspiradora, lavadora, etc.
Cualquier dispositivo que tenga un enchufe principal (es decir, implemente la interfaz "tiene un enchufe de red") puede tratarse exactamente de la misma manera; todos se pueden enchufar a una toma de corriente y pueden extraer energía de esa toma.
Lo que cada aparato individuo hace es completamente diferente. No va a llegar muy lejos limpiando sus alfombras con el televisor y la mayoría de la gente no mira su lavadora para entretenerse. Pero todos estos dispositivos comparten el comportamiento común de poder enchufarse a una toma de corriente.
Eso es lo que te dan las interfaces. Comportamientos
unificados que pueden ser llevados a cabo por muchas clases diferentes de objetos, sin la necesidad de las complicaciones de la herencia.
fuente
El término "programación a una interfaz" está abierto a mucha interpretación. Interfaz en el desarrollo de software es una palabra muy común. Así es como explico el concepto a los desarrolladores junior que he entrenado a lo largo de los años.
En la arquitectura de software hay una amplia gama de límites naturales. Los ejemplos comunes incluyen
Lo que importa es que cuando existen estos límites naturales, se identifican y se especifica el contrato de cómo se comporta ese límite. Usted prueba su software no si el "otro lado" se comporta, sino si sus interacciones coinciden con la especificación.
Las consecuencias de esto son:
Si bien gran parte de esto puede relacionarse con clases e interfaces, es tan importante darse cuenta de que también se relaciona con modelos de datos, protocolos de red y, de manera más general, trabajar con múltiples desarrolladores
fuente