¿Debo escribir una API de interfaz antes de una implementación?

14

He estado profundizando en una programación más "organizada" recientemente y he estado aprendiendo que debería programar en una interfaz, no en una implementación. Con eso en mente, ¿sería mejor "esbozar" un proyecto en las interfaces antes de escribir la implementación para ello cuando sea posible?

Y si este es el caso, en el caso de usar bibliotecas de terceros (es decir, Lidgren), ¿debería envolverlas también en las interfaces y resolverlas a través de contenedores IOC, o está bien exponerlas a las interfaces?

Dan Pantry
fuente
En mi experiencia personal, es bueno diseñar primero la arquitectura, la responsabilidad de cada clase. No tiene que escribirlo, solo pensarlo o dibujarlo en un papel. Entonces se trata de preferencias personales, pero recomiendo escribir comentarios de documentos primero para cada método que comience a implementar. Escribir documentos realmente te hace pensar en la funcionalidad antes de comenzar a escribir código.
Sulthan
Sí, y programe las interfaces (o clases abstractas para el caso) antes de implementarlas. Ayuda a que el mensaje fluya de cliente a servidor y viceversa "correcto" antes de ser bloqueado (e invertido) en implementaciones. Muy buena presentación de diapositivas sobre el tema: Cómo diseñar una buena API y por qué es importante
Marjan Venema

Respuestas:

8

Desafortunadamente, encontrará que esto a menudo se reduce a preferencias personales.

Sin embargo, lo que has descrito hasta ahora parece bueno. De hecho, si quisieras (y lo recomiendo) podrías usar el siguiente enfoque:

  1. Escriba el esqueleto de su aplicación como Interfaces, clases abstractas (stubbed) y clases (también stubbed)
  2. Escriba sus pruebas contra esas interfaces y stubs (fallarán por ahora)
  3. Escriba sus implementaciones (sus pruebas comenzarán a pasar cuando termine su implementación)

Te estás enfocando en tratar de escribir más código "organizado". Seguir TDD lo ayudará con esto.

Algunos puntos extra:

  • Los contenedores de IoC son convenientes. Úsalos y DI tanto como puedas.
  • No envuelva bibliotecas 3 ª parte. Esto aflojará el acoplamiento entre su código (código que controla) y el código de terceros (código que no controla)
MetaFight
fuente
1
Esto era lo que originalmente había pensado, pero me dijeron que violaría el principio de YAGNI. El problema que encuentro con muchos de mis proyectos que nunca terminan es que rápidamente se vuelven imposibles de mantener con la cantidad de código de blob que he escrito, porque no lo organizo correctamente o planifico mi plan de ataque.
Dan Pantry
¿Qué parte violaría a YAGNI?
MetaFight
Envolviendo bibliotecas de terceros.
Dan Pantry
2
Supongo que todo se reduce a: ¿Cuáles son las probabilidades de que cambie la biblioteca de terceros? Si hay un 0% de posibilidades de esto, entonces seguro YAGNI. Pero, rara vez es el caso. Además, envolver sus bibliotecas de terceros puede hacer que su otro código sea más fácil de probar (si, por ejemplo, no puede burlarse de la biblioteca de terceros)
MetaFight
1
@DanPantry: Envolver bibliotecas de terceros no es una violación de YAGNI, sino una protección muy necesaria contra la "infestación de su propio código en la biblioteca de terceros". No se trata solo de poder intercambiar una biblioteca, sino que MetaFight también dice una defensa contra los cambios en las versiones más nuevas de la biblioteca que de lo contrario requerirían cambios en su propio código. Al ajustar la biblioteca (y especialmente sus tipos específicos: clases, enumeraciones, estructuras, etc.), aísla su propio código y tiene un único punto para cambiar cuando la biblioteca cambia (por cualquier razón).
Marjan Venema
13

Sí, debe codificar contra interfaces en lugar de implementaciones conocidas, y sí, debe construir interfaces primero en lugar de hacer que emerjan de su propio código.

Las razones de ambas recomendaciones son en gran medida las mismas: la programación de computadoras se trata principalmente de factores humanos. Muchos encuentran esto sorprendente, pero consideren: hay un número casi infinito de formas diferentes de resolver el mismo problema informático que funcionan igualmente bien. Casi todos son completamente imposibles de entender para cualquiera que no los haya escrito (o de hecho para el autor poco tiempo después).

De ello se deduce que una buena ingeniería de software se trata en gran medida de cómo lograr el efecto deseado (cálculo correcto con eficiencia razonable) de una manera que permita trabajar con el código fuente más adelante. Las interfaces y las API son una parte crucial de esa disciplina: le permiten pensar en un problema en un nivel de descripción a la vez. Esto es mucho más fácil que pensar en reglas de coherencia empresarial y en implementaciones de listas vinculadas al mismo tiempo, y por lo tanto, imponer tal separación de preocupaciones a la fuerza es mejor que permitir que el programador del cliente use su código de la manera que desee.

Esto es difícil de creer para muchos programadores de vaqueros, que están convencidos de que entienden todo lo que escriben, son mucho mejores que los pensadores promedio y pueden manejar toda la complejidad que les da problemas a los programadores "menores". No ser consciente de los propios límites cognitivos es un fenómeno extremadamente común; esta es la razón por la cual las mejores prácticas en la organización de códigos son tan importantes (y tan a menudo ignoradas).

Para repetir, las interfaces y las barreras API son en gran medida buenas , incluso cuando solo cooperas contigo mismo. En cuanto a las bibliotecas externas, si traen consigo una API bien pensada, no veo ningún problema en usarla, siempre y cuando no anticipe tener que cambiar esa biblioteca por otra. De lo contrario, una capa de envoltura o anticorrupción puede ser una muy buena idea.

Kilian Foth
fuente
Me gusta su punto de vista acerca de que SE se trata principalmente de lograr el efecto deseado de una manera que permita trabajar con el código fuente más adelante. ¡Ojalá hubiera podido expresarlo bien en mi último trabajo, donde siempre estaba luchando por un código limpio!
MetaFight
¿Existe una convención de nomenclatura para las API que son solo interfaces que terminaré usando en todas partes? Por ejemplo, si estoy haciendo un patrón de comando, ¿lo llamo "comandables"?
Snoop
@StevieV Hay varios, por ejemplo, IBlahimplementado por Blah, o Blahimplementado por BlahImpl. No me gustan ambos, y tiendo a usar Blahimplementado por OralBlah, WrittenBlaho ASLBlah. Pero, como de costumbre, es más importante ajustarse a su base de código y expectativas existentes que a cualquier estándar general.
Kilian Foth
4

En lugar de simplemente servir como programador a interfaces, ¿por qué no buscar en el Desarrollo / Diseño de Prueba (TDD)?

Mucha gente piensa en TDD como una práctica de prueba, pero en realidad es un enfoque de diseño en el que permite que las pruebas expongan cómo se usará su código a través de pruebas (inicialmente a través de pruebas unitarias, pero también puede ser a través de pruebas de integración).

La programación para interfaces es un arma importante en su conjunto de herramientas, pero como la mayoría de las cosas, no siempre es la solución / técnica / práctica adecuada, ya que no siempre es necesaria. Debe programar en las interfaces donde lo necesite.

El uso de TDD lo obligará a explorar dónde tales interfaces son importantes y dónde, francamente, no importa. Y al final, debe tener un conjunto bastante bueno de pruebas unitarias en su base de código.

En cuanto al uso de bibliotecas de terceros, recomiendo encarecidamente incluirlas en sus propias abstracciones cuando corresponda; y no dejar que los clientes de su API "sepan" sobre ellos.

¡Buena suerte!

[editar: vi la respuesta de megaflight - completamente de acuerdo]

rupjones
fuente
2
TDD implícitamente te hace pensar en términos de interfaz en lugar de implicación, aunque puede que no sea una declaración formal de "interfaz".
DougM
1
Esta es una respuesta genial. +1 por sugerir TDD, que creo que es la solución al problema real del OP de dónde comenzar cuando se trabaja en un nuevo proyecto, y volvería a +1 si pudiera para "Usar TDD te obligará a explorar dónde tales interfaces son importantes y donde, francamente, no importa ".
Benjamin Hodgson
2

Creo que es exagerado. Si el usuario de su API no necesita ser forzado a implementar / usar algo de cierta manera, entonces lo dejaría fuera. Las interfaces son contratos, si no lo necesito, ¿por qué darme uno?

Creo que las personas usan en exceso las interfaces. Está agregando una capa de complejidad que no es necesaria en la mayoría de los casos.

Kyle Johnson
fuente
Creo que las personas utilizan las interfaces de manera insuficiente . Si desea crear piezas reutilizables, la interfaz no solo es una adición "agradable de tener", sino lo más importante que debe cuidar. Además de la implementación real, por supuesto.
JensG
1

La programación contra un contrato es casi siempre una buena idea. Ese contrato no necesita ser una interfaz, sino que una clase puede cumplirlo. En mi opinión, las interfaces se han usado en exceso junto con DI debido a preocupaciones de prueba de unidad y marcos de trabajo burlones.

Personalmente, prefiero traer solo interfaces cuando es muy probable que tenga o tenga más de 1 implementación de un contrato. Las interfaces son excelentes para repositorios donde deseo abstraer el acceso a datos, pero probablemente no tanto para mi lógica comercial estándar que probablemente sea relativamente inflexible.

Ahora no tener una interfaz puede causar problemas con las pruebas unitarias, especialmente para los puristas. Pero estoy interesado en burlarme de las dependencias externas de mis programas, no de sus dependencias internas. Quiero que mis pruebas realicen la validación del código, no hagan eco de la estructura del código.

Peter Smith
fuente