2009-12-16 13 views
36

Una discusión que tuve con un colega hoy.¿Debería incluir bibliotecas de terceros que adopte en su proyecto?

Él dice que cada vez que utiliza una biblioteca de terceros, siempre debe escribir para ello un contenedor. Por lo tanto, siempre puede cambiar las cosas más tarde y acomodar las cosas para su uso específico.

no estoy de acuerdo con la palabra siempre, surgió la discusión con respecto a log4j y afirmó que log4j ha probado bien y la hora de la API y la aplicación probada, y todo lo imaginable puede ser configurado a posteriori y no hay nada que debe envolver. Incluso si quisiera envolver, existen contenedores probados como commons-logging y log5j.

Otro ejemplo que hemos tocado en nuestra discusión es Hibernate. Afirmé que tiene una API muy grande para envolver. Además, tiene una API en capas que le permite modificar su interior si así lo necesita. Mi amigo afirmó que todavía cree que debe ser envuelto, pero no lo hizo debido al tamaño de la API (este compañero de trabajo es mucho más veterano que yo en nuestro proyecto actual).

reclamé this, y que envoltorio se debe hacer en casos específicos:

  • no está seguro de cómo la biblioteca se ajuste a sus necesidades
  • va a utilizar sólo una pequeña porción de una libary (en en ese caso, solo puede exponer una parte de su API).
  • no está seguro de la calidad de la API o la implementación de la biblioteca.

También sostuve que a veces puede envolver su código en lugar de la biblioteca. Por ejemplo, poner su código relacionado con la base de datos en una capa DAO, en lugar de colocar de forma preventiva todo el hibernate.

Bueno, al final esto no es realmente una pregunta, pero sus ideas, experiencias y opiniones son muy apreciadas.

+8

+1 por tener problema con la palabra "siempre" – BryanD

+0

vea SLF4J para el contenedor de log4j defacto. El appender que desea está siempre con otro registrador que el que usa :( –

Respuesta

52

Es un ejemplo perfecto de YAGNI:

  • es más trabajo
  • se infla su proyecto
  • que puede complicar su diseño
  • no tiene ningún beneficio inmediato
  • la scenarion se escríbalo para que nunca se manifieste
  • cuando lo haga, lo más probable es que su envoltorio deba ser reescrito completo porque está muy cerca de la biblioteca concreta que estaba usando y la API nueva simplemente no coincide con la suya.
+2

+ 1 Esta es absolutamente la mejor razón. ¿Por qué diseñaría, implementaría y luego mantendría el código en caso de que ocurra? Perdió tiempo y esfuerzo. – wheaties

+6

+100 en su último punto – ChssPly76

+3

Reescribiendo un contenedor por completo es un * lote * menos trabajo que reescribir tu aplicación por completo. De todos modos, estoy de acuerdo con el principio de YAGNI y no esperaría a escribir un envoltorio hasta la primera vez * que * lo necesites. –

4

El factor principal para decidir envolver una biblioteca o no es el impacto que un cambio de biblioteca tendrá en el código. Cuando solo se llama a una biblioteca de 1 clase, el impacto de cambiar la biblioteca será mínimo. Si en el otro lado se llama a una biblioteca en todas las clases, es mucho más probable que haya un contenedor.

+0

este es un argumento para envolver log4j, que generalmente se llama desde cualquier clase que hace algo remotamente complejo. – flybywire

4
  1. Cualquier incertidumbre en torno a la elección de la biblioteca tercera parte debe lavarse al comienzo del proyecto el uso de prototipos para probar la capacidad de ampliación/adecuación/lo que sea de la biblioteca de tercera parte.

  2. Si decide seguir adelante y proporcionar soporte total de desacoplamiento/abstracción, debe costar y finalmente ser aprobado por el patrocinador del proyecto; en última instancia, es una decisión comercial ya que alguien tiene que pagarla y el trabajo requerido para hazlo (a menos que sea absolutamente trivial, en cuyo caso la API probablemente sea de bajo riesgo).

  3. En general, un arquitecto experimentado elegirá una tecnología con la que pueda confiar razonablemente, y tenga experiencia, y que confíe en que durará la aplicación O bien, eliminará cualquier riesgo en la decisión anticipada en en el proyecto, eliminando así cualquier necesidad de hacer esto, la mayoría de las veces

3

me gustaría más bien de acuerdo con la mayoría de sus puntos.Usar absolutos a menudo te mete en problemas y decir que debes "siempre" hacer algo limita tu flexibilidad. Agregaría algunos puntos más a tu lista.

Cuando utiliza el código de ajuste alrededor de una API muy común, como Hibernate o log4j, hace que sea más difícil crear nuevos desarrolladores. Los nuevos desarrolladores ahora tienen que aprender una API completamente nueva, donde si no hubieras ajustado el código, te habrían familiarizado de inmediato.

Por otro lado, también limita la vista de los desarrolladores a la API. Usar una característica avanzada de la API lleva más tiempo porque debe asegurarse de que su envoltorio se implemente de manera que pueda manejarlo.

Muchas de las capas de envoltura que he visto también son muy específicas para la implementación subyacente. Entonces, si escribe un contenedor de log alrededor de log4j, está pensando en términos log4j. Si sale un nuevo marco interesante, puede cambiar todo el paradigma, por lo que su código de ajuste no migrará tan bien como había pensado.

Definitivamente no estoy diciendo que el código de envoltura sea siempre malo, pero como ha indicado, hay muchos factores que debe tener en cuenta.

8

Bueno, el beneficio obvio es para las tecnologías de conmutación. Si tiene una biblioteca que se ha quedado obsoleta y desea cambiar, puede terminar reescribiendo una gran cantidad de código para acomodar el cambio, mientras que si estuviese empaquetada, le resultaría más fácil escribir un nuevo contenedor para la nueva versión. , que cambiar todo tu código

Por otro lado, significa que debe escribir un contenedor para cada biblioteca trivial que incluya, que probablemente sea una cantidad inaceptable de sobrecarga.

Mi industria tiene que ver con la velocidad, por lo que el único momento en que podría justificar la redacción de un contenedor es si se trataba de alguna biblioteca crítica que probablemente cambiara drásticamente de forma regular. O, más comúnmente, si necesito tomar una nueva biblioteca y calzarla en código antiguo, lo cual es una desafortunada realidad.

Definitivamente no es una situación "siempre". Es algo que puede ser deseable. Pero el tiempo no siempre va a estar allí, y, al final, si escribir un contenedor lleva horas y los cambios en la biblioteca de códigos a largo plazo serán pocos y triviales ... ¿Por qué molestarse?

+0

Mira con imparcialidad a ambos lados. – BryanD

1

Debe hacerlo siempre, a menudo, a veces, rara vez o nunca. Ni siquiera tu colega lo hace siempre, pero los casos instructivos son siempre y nunca. Supongamos que a veces es necesario. Si nunca envolvió una biblioteca, la peor consecuencia es que un día descubrió que era necesario para una biblioteca que había usado por todas partes. Te tomaría un tiempo envolver esa biblioteca y realizar una cirugía de disparo en los clientes. La pregunta es si esa eventualidad tomaría más tiempo que el suministro habitual de envoltorios que rara vez son necesarios, pero nunca tener que realizar la cirugía de escopeta.

Mi instinto es apelar al principio YAGNI (no lo va a necesitar) y optar por "raramente".

2

Envasar una biblioteca completa es un texto estándar, ineficaz y erróneo en la mayoría de los casos. Se puede hacer de una manera muy inteligente. Diría que envolver una biblioteca es apropiado sobre todo en el caso de bibliotecas de componentes de UI, y de nuevo, debe agregar algunas funciones adicionales core a todos los componentes para que esto sea necesario.

  • si se necesitan demasiado modificaciones y adiciones, esto es lo más probable es que no la biblioteca que busca
  • si hay una cantidad moderada de adiciones y modificaciones - no siempre son los patrones de diseño que vienen a mano en esos casos. El Decorator pattern (permite que se agregue dinámicamente un comportamiento nuevo/adicional a un objeto existente), por ejemplo, es bastante adecuado para la mayoría de los casos.
  • Las funciones IDE de búsqueda/reemplazo y refactorización ofrecen una manera fácil de cambiar su código en todos los lugares requeridos si se necesita algún cambio importante y aparece un objeto de ajuste. (por supuesto, las pruebas unitarias serían útiles aquí;))
+1

Estoy de acuerdo en su mayoría. Pero incluso cuando es fácil hacer modificaciones masivas con IDE (incluso si son 100% precisas cuando se usa un lenguaje fuertemente tipado como Java), son muy dolorosas y contaminan el control de la fuente y dificultan la comprensión posterior de los cambios de código. que abarca su "refactorización masiva". – flybywire

+0

sí, pero si eso se hace con bastante poca frecuencia y si en los casos en que es absolutamente necesario, el problema no es tan grande. – Bozho

5

El problema aquí es parcialmente la palabra 'envoltura', parcialmente una dicotomía falsa, y parcialmente una distinción falsa entre el JDK y todo lo demás.

La palabra 'envoltorio'

Envolviendo todos Hibernate, como usted dice, es una empresa totalmente impracticable.

Restringir las dependencias de Hibernate a un conjunto identificado, controlado, de archivos fuente, por otro lado, puede ser práctico y lograr los mismos resultados.

La falsa dicotomía

La falsa dicotomía es la falta de reconocimiento de una tercera opción: normas. Si usa, por ejemplo, anotaciones JPA, puede cambiar Hibernate por otras cosas. Si está escribiendo un servicio web y usa anotaciones JAX-WS y JAX-B, puede intercambiar entre JDK, CXF, Glassfish o lo que sea.

La falsa distinción

Claro, el JDK cambia lentamente y es poco probable que muera. Pero los principales paquetes de código abierto también cambian lentamente y es poco probable que mueran. Miles de desarrolladores y proyectos no contados usan Hibernate. Realmente no hay más riesgo de que Hibernate desaparezca o de que se produzcan cambios incompatibles de API que el propio Java.

3

El propósito de ajustar incluso una biblioteca de terceros bien probada y comprobada es que puede decidir cambiar de biblioteca en algún momento en el futuro. Envolverlo hace que sea más fácil cambiar sin cambiar ningún código en su aplicación principal. Solo el envoltorio necesita cambiar.

Si está absolutamente seguro que nunca (otro absoluto) utilizará un marco de trabajo de registro diferente en su proyecto, adelante y omita el envoltorio. Incluso habiendo dicho eso, probablemente me mantendría al margen escribiendo el envoltorio hasta que yo supiera lo necesitaba, como la primera vez que necesito cambiar.

5

Si la biblioteca que planea envolver es única en sus "principios de acceso, metáforas y expresiones idiomáticas" de otras ofertas en el mismo dominio, entonces su contenedor será más o menos similar a esa biblioteca y no lo hará es bueno si algún día cambias a una biblioteca diferente ya que necesitarás un nuevo contenedor.

Si se accede a la biblioteca de forma similar a otras bibliotecas y el mismo contenedor se puede aplicar a estas bibliotecas, probablemente se escriban basándose en algún estándar existente y ya existe alguna capa común para acceder a ambas. .

Solo iría con envoltorios si supiera con certeza que tendría que admitir bibliotecas múltiples y sustancialmente diferentes en producción.

+1

+1 para la expresión "acceder a principios, metáforas y modismos" – flybywire

3

Esto es una pregunta divertida.

He trabajado en sistemas en los que hemos encontrado errores de showtopper en las bibliotecas que estábamos usando, y los que están río arriba ya no se mantienen o no están interesados ​​en corregirlos. En un lenguaje como Java, por lo general no se pueden corregir los errores internos de un contenedor. (Afortunadamente, si son de código abierto, al menos puedes arreglarlos tú mismo). Así que no es de ayuda aquí.

Pero a menudo trabajo en un lenguaje donde se pueden modificar fácilmente las bibliotecas en cualquier momento, sin ver ni tener su código fuente. Normalmente agrego nuevos métodos a las clases existentes, por ejemplo. Entonces, en este caso, no tiene sentido envolver: simplemente realice el cambio que desee.

Además, ¿marca su colega las cosas llamadas "bibliotecas"? ¿Qué pasa con Java en sí? ¿Él envuelve las clases integradas? ¿Él envuelve el sistema de archivos? El programador de hilos? El kernel? (Es decir, con sus propias envolturas, en cierto sentido, todo es un envoltorio alrededor de la CPU, pero parece que está hablando de envoltorios en su repositorio de origen que están completamente bajo su control.) He tenido en funcionalidad, cambie o desaparezca cuando aparezcan nuevas versiones de la misma. Java es no inmune a esto.

Así que la idea de siempre escribir un contenedor se reduce a una apuesta. Suponiendo que sólo ha de envolver bibliotecas de terceros, que parece estar apostando implícitamente que:

  • funcionalidad de "primera parte" (como Java en sí, el núcleo, etc.) nunca va a cambiar
  • cuando "tercera fiesta "cambia la funcionalidad, siempre se hará de una manera que se puede arreglar en un contenedor

¿Es cierto en su caso? No lo sé. De los proyectos de Java medianamente grandes que he hecho, raramente es cierto para mí. No me tomaría el esfuerzo de envolver todas las bibliotecas de terceros, porque parece una apuesta pobre, pero su situación es ciertamente diferente a la mía.

1

No lo envolvería como una cosa de una a una, pero colocaría capas en la aplicación para que cada parte sea reemplazable tanto como sea posible. El modelo ISO OSI funciona bien para todo tipo de software :-)

3

Hay una situación en la que con buenas razones puede envolver. A saber, si necesita probar cosas, y el objeto de terceros predeterminado es muy pesado. Entonces tener una interfaz realmente puede hacer la diferencia.

Tenga en cuenta que esto no reemplaza la biblioteca, sino que la hace más manejable donde no importa demasiado.

4

No. Los arquitectos/wanna-bee de Java están demasiado ocupados diseñando en contra de los cambios imaginarios.

Con IDE moderno, es pan comido cuando necesitas un cambio. Hasta entonces, mantenlo simple.

+1

+1 para "diseño demasiado ocupado contra cambios imaginarios" – flybywire

5

Estoy de acuerdo con todo lo que se ha dicho más o menos.

La única vez que el código de terceros es útil (la barra no cumple con YAGNI) es para la prueba unitaria.

Las estáticas de burla y demás requieren que se ajuste el código, esta es una razón válida para escribir envoltorios para código de terceros.

En el caso del código de registro, no es necesario.

1

En mi experiencia, la pregunta es bastante discutible si está utilizando las abstracciones lo suficiente. Acoplamiento a una biblioteca es como unir a cualquier otra interfaz. Por lo tanto, desea reducir el acoplamiento accidental y el alcance de la reescritura necesarios si necesita cambiar la implementación. No vincule la lógica de su aplicación a un constructo, pero no se limite a formar un montón de envoltorios estúpidos (literalmente) alrededor de algo y espere obtener algún beneficio.

Un contenedor no suele ganar nada a menos que responda a un propósito específico (como polimorfizar una construcción no polimórfica). A menudo aparecen en refactorización, pero no recomendaría formar una arquitectura sobre ellos. Hay algunas excepciones, por supuesto, pero lo hay con cualquier principio.

Esto no se refiere a los adaptadores. Un adaptador puede ser un componente bastante importante para cuando realmente quiere alterar la interfaz de una biblioteca y su uso para estar en línea con la arquitectura, el código o los conceptos de dominio en su proyecto.

Cuestiones relacionadas