Uso de bibliotecas de terceros: ¿siempre usa un contenedor?


78

La mayoría de los proyectos en los que estoy involucrado utilizan varios componentes de código abierto. Como principio general, ¿es una buena idea evitar siempre vincular todos los componentes del código a las bibliotecas de terceros y, en su lugar, utilizar un contenedor de encapsulación para evitar el dolor del cambio?

Como ejemplo, la mayoría de nuestros proyectos PHP usan directamente log4php como marco de registro, es decir, crean instancias a través de \ Logger :: getLogger (), usan los métodos -> info () o -> warn (), etc. En el futuro, sin embargo, puede aparecer un marco de registro hipotético que es mejor de alguna manera. Tal como están las cosas, todos los proyectos que se acoplan estrechamente a las firmas del método log4php tendrían que cambiar, en docenas de lugares, para adaptarse a las nuevas firmas. Obviamente, esto tendría un gran impacto en la base de código y cualquier cambio es un problema potencial.

Para las nuevas bases de código de este tipo de escenario a prueba del futuro, a menudo considero (y a veces implemento) una clase de envoltura para encapsular la funcionalidad de registro y hacer que sea más fácil, aunque no infalible, alterar la forma en que el registro funciona en el futuro con un cambio mínimo ; el código llama al contenedor, el contenedor pasa la llamada al marco de registro del día .

Teniendo en cuenta que hay ejemplos más complicados con otras bibliotecas, ¿estoy sobreingeniería o es una precaución sabia en la mayoría de los casos?

EDITAR: Más consideraciones: el uso de la inyección de dependencia y los dobles de prueba prácticamente requiere que resumamos la mayoría de las API de todos modos ("Quiero comprobar si mi código se ejecuta y actualiza su estado, pero no escribir un comentario de registro / acceder a una base de datos real"). ¿No es esto un decisivo?


3
log4XYZ es una marca tan fuerte. Su API cambiará no antes de que lo haga la API de una lista vinculada. Ambos son un problema resuelto ahora.
Trabajo

1
Duplicado exacto de esta pregunta SO: stackoverflow.com/questions/1916030/…
Michael Borgwardt

1
Si solo lo está utilizando internamente, ya sea que se ajuste o no, es solo una compensación entre el trabajo conocido ahora y el posible trabajo más adelante. Una llamada de juicio. Pero algo de lo que otros respondedores parecen haber descuidado hablar es si se trata de una dependencia API o una dependencia de implementación . En otras palabras, ¿está filtrando clases de esta API de terceros a través de su propia API pública y la está exponiendo a los usuarios? En este caso, ya no es solo una cuestión de trabajo arduo moverse a una biblioteca diferente, el problema es que ahora es imposible sin romper su propia API. ¡Esto es muy malo!
Elias Vasylenko

1
Para mayor referencia: este patrón se llama arquitectura de cebolla, donde la infraestructura externa (lo llama lib externo) está oculta detrás de una interfaz
k3b

Respuestas:


42

Si solo usa un pequeño subconjunto de la API de terceros, tiene sentido escribir un contenedor: esto ayuda con la encapsulación y la ocultación de información, asegurando que no exponga una API posiblemente grande a su propio código. También puede ayudar a garantizar que cualquier funcionalidad que no desee utilizar esté "oculta".

Otra buena razón para un contenedor es si espera cambiar la biblioteca de terceros. Si se trata de una infraestructura que sabe que no va a cambiar, no escriba un contenedor para ello.


Puntos buenos, pero se nos enseña que el código estrechamente acoplado es malo, por muchas razones bien entendidas (más difíciles de probar, más difíciles de refactorizar, etc.). Una redacción alternativa de la pregunta es "si el acoplamiento es malo, ¿por qué está bien acoplarse a una API?".
lotsoffreetime

77
@lotsoffreetime No puede evitar algunos acoplamientos a una API. Por lo tanto, es mejor acoplarse a su propia API. De esa manera, puede cambiar la biblioteca y, en general, no es necesario cambiar la API proporcionada por el contenedor.
George Marian

@ george-marian Si no puedo evitar usar una API determinada, ciertamente puedo minimizar los puntos de contacto. La pregunta es, ¿debería estar tratando de hacer esto todo el tiempo o es exagerar las cosas?
lotsoffreetime

2
@lotsoffreetime Esa es una pregunta difícil de responder. He ampliado mi respuesta a ese fin. (Básicamente, se debe a muchos ifs.)
George Marian

2
@lotsoffreetime: si tienes mucho tiempo libre, entonces puedes hacerlo. Pero recomiendo no escribir un contenedor de API, excepto en esta condición: 1) la API original es de muy bajo nivel, por lo que debe escribir una API de nivel superior que se adapte mejor a su proyecto específico, o 2) tiene un plan en el En un futuro próximo para cambiar bibliotecas, está utilizando la biblioteca actual solo como un trampolín mientras busca una mejor.
Lie Ryan

28

Sin saber qué nuevas características supergrandes tendrá este supuesto registrador mejorado futuro, ¿cómo escribiría el contenedor? La opción más lógica es hacer que su contenedor cree una instancia de algún tipo de clase de registrador, y tener métodos como ->info()o ->warn(). En otras palabras, esencialmente idéntico a su API actual.

En lugar de código a prueba de futuro que quizás nunca necesite cambiar, o que requiera una reescritura inevitable de todos modos, prefiero el código "a prueba de pasado". Es decir, en las raras ocasiones en que cambio significativamente un componente, es cuando escribo un contenedor para hacerlo compatible con el código pasado. Sin embargo, cualquier código nuevo usa la nueva API, y refactorizo ​​el código viejo para usarlo siempre que haga un cambio en el mismo archivo de todos modos, o según lo permita la programación. Después de unos meses, puedo quitar el envoltorio y el cambio ha sido gradual y robusto.

Dicho de otra manera, los envoltorios realmente solo tienen sentido cuando ya conoce todas las API que necesita envolver. Un buen ejemplo es si su aplicación actualmente necesita admitir diferentes controladores de bases de datos, sistemas operativos o versiones de PHP.


"... los envoltorios solo tienen sentido cuando ya conoces todas las API que necesitas envolver". Esto sería cierto si estuviera haciendo coincidir la API en el contenedor; quizás debería usar el término "encapsulación" con más fuerza que envoltorio. Estaría abstrayendo estas llamadas a la API para "registrar este texto de alguna manera" en lugar de "llamar a foo :: log () con este parámetro".
lotsoffreetime

"Sin saber qué nuevas y excelentes características tendrá este supuesto registrador mejorado en el futuro, ¿cómo escribiría el contenedor?" @ kevin-cline a continuación menciona un registrador futuro con mejor rendimiento, en lugar de una función más nueva. En este caso, no hay una nueva API para ajustar, solo un método de fábrica diferente.
lotsoffreetime

27

Al envolver una biblioteca de terceros, agrega una capa adicional de abstracción sobre ella. Esto tiene algunas ventajas:

  • Su código base se vuelve más flexible a los cambios.

    Si alguna vez necesita reemplazar la biblioteca con otra, solo necesita cambiar su implementación en su contenedor, en un solo lugar . Puede cambiar la implementación del reiniciador y no tiene que cambiar nada sobre otra cosa, en otras palabras, tiene un sistema débilmente acoplado. De lo contrario, tendría que revisar toda su base de código y realizar modificaciones en todas partes, lo que obviamente no es lo que desea.

  • Puede definir la API del contenedor independientemente de la API de la biblioteca

    Las diferentes bibliotecas pueden tener API muy diferentes y, al mismo tiempo, ninguna de ellas puede ser exactamente lo que necesita. ¿Qué sucede si alguna biblioteca necesita un token para pasar junto con cada llamada? Puede pasar el token en su aplicación donde necesite usar la biblioteca o puede protegerlo en algún lugar más centralizado, pero en cualquier caso necesita el token. Su clase de envoltura hace que todo esto sea simple nuevamente, porque puede mantener el token dentro de su clase de envoltura, nunca exponerlo a ningún componente dentro de su aplicación y eliminar por completo la necesidad de ello. Una gran ventaja si alguna vez usó una biblioteca que no enfatiza un buen diseño de API.

  • Las pruebas unitarias son mucho más simples

    Las pruebas unitarias solo deben probar una cosa. Si desea probar la unidad de una clase, debe burlarse de sus dependencias. Esto se vuelve aún más importante si esa clase realiza llamadas de red o accede a algún otro recurso fuera de su software. Al envolver la biblioteca de terceros, es fácil burlarse de esas llamadas y devolver datos de prueba o lo que requiera esa prueba de unidad. Si no tiene esa capa de abstracción, se vuelve mucho más difícil hacer esto, y la mayoría de las veces esto resulta en una gran cantidad de código feo.

  • Creas un sistema débilmente acoplado

    Los cambios en su contenedor no tienen efecto en otras partes de su software, al menos siempre que no cambie el comportamiento de su contenedor. Al introducir una capa de abstracción como este contenedor, puede simplificar las llamadas a la biblioteca y eliminar casi por completo la dependencia de su aplicación en esa biblioteca. Su software solo usará el contenedor y no hará una diferencia en cómo se implementa el contenedor o cómo hace lo que hace.


Ejemplo práctico

Seamos honestos. Las personas pueden discutir sobre las ventajas y desventajas de algo como esto durante horas, por lo que prefiero mostrarles un ejemplo.

Supongamos que tiene algún tipo de aplicación de Android y necesita descargar imágenes. Hay un montón de bibliotecas que facilitan la carga y el almacenamiento en caché de imágenes, por ejemplo, Picasso o Universal Image Loader .

Ahora podemos definir una interfaz que vamos a utilizar para ajustar la biblioteca que terminemos usando:

public interface ImageService {
    Bitmap load(String url);
}

Esta es la interfaz que ahora podemos usar en toda la aplicación siempre que necesitemos cargar una imagen. Podemos crear una implementación de esta interfaz y usar la inyección de dependencia para inyectar una instancia de esa implementación en todos los lugares donde usamos ImageService.

Digamos que inicialmente decidimos usar Picasso. Ahora podemos escribir una implementación para la ImageServicecual utiliza Picasso internamente:

public class PicassoImageService implements ImageService {

    private final Context mContext;

    public PicassoImageService(Context context) {
        mContext = context;
    }

    @Override
    public Bitmap load(String url) {
        return Picasso.with(mContext).load(url).get();
    }
}

Bastante sencillo si me preguntas. El envoltorio alrededor de las bibliotecas no tiene que ser complicado para ser útil. La interfaz y la implementación tienen menos de 25 líneas de código combinadas, por lo que apenas fue un esfuerzo crear esto, pero ya ganamos algo al hacer esto. Ver el Contextcampo en la implementación? El marco de inyección de dependencia de su elección ya se encargará de inyectar esa dependencia antes de que usemos nuestra ImageService, su aplicación ahora no tiene que preocuparse por cómo se descargan las imágenes y las dependencias que pueda tener la biblioteca. Todo lo que ve su aplicación es una ImageServicey, cuando necesita una imagen, llama load()con una url, simple y directo.

Sin embargo, el beneficio real llega cuando comenzamos a cambiar las cosas. Imagine que ahora necesitamos reemplazar Picasso con Universal Image Loader porque Picasso no es compatible con alguna característica que absolutamente necesitamos en este momento. ¿Ahora tenemos que revisar nuestra base de código y reemplazar tediosamente todas las llamadas a Picasso y luego tratar con docenas de errores de compilación porque olvidamos algunas llamadas de Picasso? No. Todo lo que tenemos que hacer es crear una nueva implementación ImageServicey decirle a nuestro marco de inyección de dependencias que use esta implementación de ahora en adelante:

public class UniversalImageLoaderImageService implements ImageService {

    private final ImageLoader mImageLoader;

    public UniversalImageLoaderImageService(Context context) {

        DisplayImageOptions defaultOptions = new DisplayImageOptions.Builder()
                .cacheInMemory(true)
                .cacheOnDisk(true)
                .build();

        ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(context)
                .defaultDisplayImageOptions(defaultOptions)
                .build();

        mImageLoader = ImageLoader.getInstance();
        mImageLoader.init(config);
    }

    @Override
    public Bitmap load(String url) {
        return mImageLoader.loadImageSync(url);
    }
}

Como puede ver, la implementación puede ser muy diferente, pero no importa. No tuvimos que cambiar una sola línea de código en ningún otro lugar de nuestra aplicación. Utilizamos una biblioteca completamente diferente que podría tener características completamente diferentes o podría usarse de manera muy diferente, pero a nuestra aplicación simplemente no le importa. Igual que antes, el resto de nuestra aplicación solo ve la ImageServiceinterfaz con su load()método y, sin embargo, este método se implementa ya no importa.

Al menos para mí todo esto ya suena bastante bien, ¡pero espera! Aún hay más. Imagine que está escribiendo pruebas unitarias para una clase en la que está trabajando y esta clase usa el ImageService. Por supuesto, no puede permitir que sus pruebas unitarias realicen llamadas de red a algún recurso ubicado en algún otro servidor, pero dado que ahora está utilizando el ImageService, puede fácilmente load()devolver una estática Bitmaputilizada para las pruebas unitarias implementando un simulacro ImageService:

public class MockImageService implements ImageService {

    private final Bitmap mMockBitmap;

    public MockImageService(Bitmap mockBitmap) {
        mMockBitmap = mockBitmap;
    }

    @Override
    public Bitmap load(String url) {
        return mMockBitmap;
    }
}

Para resumir envolviendo bibliotecas de terceros, su base de código se vuelve más flexible a los cambios, en general más simple, más fácil de probar y reduce el acoplamiento de diferentes componentes en su software, todo lo que se vuelve cada vez más importante cuanto más tiempo mantenga un software.


1
Esto también se aplica a una API inestable. Nuestro código no cambia en 1000 lugares solo porque la biblioteca subyacente cambió. Muy buena respuesta.
RubberDuck

Respuesta muy concisa y clara. Hago trabajo front-end en la web. La cantidad de cambios en ese paisaje es una locura. El hecho de que las personas "piensen" que no habrá cambios no significa que no habrá cambios. Vi menciones de YAGNI. Me gustaría agregar un nuevo acrónimo, YDKYAGNI, no sabes que no lo vas a necesitar. Especialmente con implementaciones relacionadas con la web. Como regla, siempre envuelvo bibliotecas que solo exponen una API pequeña (como select2). Las bibliotecas más grandes afectan su arquitectura y su ajuste significa que espera que su arquitectura cambie, podría hacerlo, pero es menos probable que lo haga.
Adiós

Su respuesta fue muy útil y presentar el concepto con un ejemplo lo hizo aún más claro.
Anil Gorthy

24

Creo que envolver bibliotecas de terceros hoy en caso de que algo mejor venga mañana es una violación muy derrochadora de YAGNI. Si llama repetidamente a código de terceros de una manera peculiar a su aplicación, deberá (debería) refactorizar esas llamadas en una clase de ajuste para eliminar la repetición. De lo contrario, está utilizando completamente la API de la biblioteca y cualquier contenedor se parecería a la biblioteca en sí.

Ahora suponga que aparece una nueva biblioteca con un rendimiento superior o lo que sea. En el primer caso, simplemente reescribe el contenedor para la nueva API. No hay problema.

En el segundo caso, crea un contenedor que adapta la interfaz anterior para controlar la nueva biblioteca. Un poco más de trabajo, pero no hay problema, y ​​no más trabajo del que hubiera hecho si hubiera escrito el contenedor antes.


44
No creo que YAGNI se aplique necesariamente en esta situación. No se trata de incorporar funcionalidad en caso de que la necesite en el futuro. Se trata de incorporar flexibilidad en la arquitectura. Si esa flexibilidad es innecesaria, entonces, sí, se aplica YAGNI. Sin embargo, esa determinación tiende a hacerse en algún momento en el futuro cuando el cambio probablemente sea doloroso.
George Marian

77
@George Marian: el problema es el 95% del tiempo, nunca necesitará la flexibilidad para cambiar. Si necesita cambiar a una nueva biblioteca futura que tenga un rendimiento superior, entonces debería ser bastante trivial buscar / reemplazar llamadas o escribir un contenedor cuando lo necesite. Por otro lado, si su nueva biblioteca viene con diferentes funcionalidades, el contenedor ahora se convierte en un obstáculo ya que ahora tiene dos problemas: portar código antiguo para explotar las nuevas características y mantener el contenedor.
Lie Ryan

3
@lotsoffreetime: El propósito del "buen diseño" es minimizar el costo total de la aplicación durante su vida útil. Agregar capas de indirección para futuros cambios imaginados es un seguro muy costoso. Nunca he visto a nadie darse cuenta de los ahorros de ese enfoque. Simplemente crea un trabajo trivial para programadores cuyo tiempo se gastaría mucho mejor en los requisitos específicos del cliente. La mayoría de las veces, si está escribiendo un código que no es específico para su cliente, está perdiendo tiempo y dinero.
Kevin Cline

1
@ George: si estos cambios son dolorosos, creo que es un olor a proceso. En Java, crearía nuevas clases con los mismos nombres que las clases anteriores, pero en un paquete diferente, cambiaría todas las apariciones del nombre del paquete anterior y volvería a ejecutar las pruebas automatizadas.
Kevin Cline

1
@kevin Eso es más trabajo, y por lo tanto conlleva más riesgo, que simplemente actualizar el contenedor y ejecutar las pruebas.
George Marian

9

La razón básica para escribir un contenedor alrededor de una biblioteca de terceros es para que pueda intercambiar esa biblioteca de terceros sin cambiar el código que la usa. No puede evitar acoplarse a algo, por lo que el argumento dice que es mejor acoplar a una API que ha escrito.

Si vale la pena el esfuerzo es una historia diferente. Ese debate probablemente continuará por mucho tiempo.

Para proyectos pequeños, donde la probabilidad de que tal cambio sea necesario es baja, probablemente sea un esfuerzo innecesario. Para proyectos más grandes, esa flexibilidad puede muy bien superar el esfuerzo adicional para envolver la biblioteca. Sin embargo, es difícil saber si ese es el caso de antemano.

Otra forma de verlo es ese principio básico de abstraer lo que es probable que cambie. Por lo tanto, si la biblioteca de terceros está bien establecida y es poco probable que se modifique, puede estar bien no envolverla. Sin embargo, si la biblioteca de terceros es relativamente nueva, existe una mayor posibilidad de que deba ser reemplazada. Dicho esto, el desarrollo de bibliotecas establecidas ha sido abandonado muchas veces. Entonces, esta no es una pregunta fácil de responder.


En el caso de las pruebas unitarias en las que poder inyectar un simulacro de API sirve para minimizar la unidad bajo prueba, el "potencial de cambio" no es un factor. Dicho esto, esta sigue siendo mi respuesta favorita, ya que está más cerca de cómo pienso. ¿Qué diría el tío Bob? :)
lotsoffreetime

Además, los proyectos pequeños (sin equipo, especificaciones básicas, etc.) tienen sus propias reglas en las que puede violar buenas prácticas como esta y salirse con la suya, hasta cierto punto. Pero esa es una pregunta diferente ...
muchoffreetime

1

Además de lo que @Oded ya dijo, me gustaría agregar esta respuesta con el propósito especial de iniciar sesión.


Siempre tienen una interfaz para el registro, pero nunca he tenido que sustituir un log4foomarco aún.

Solo lleva media hora proporcionar la interfaz y escribir el contenedor, por lo que supongo que no perderá demasiado tiempo si resulta innecesario.

Es un caso especial de YAGNI. Aunque no lo necesito, no me lleva mucho tiempo y me siento más seguro. Si realmente llega el día de cambiar el registrador, me alegraré de haber invertido media hora porque me ahorrará más de un día intercambiar llamadas en un proyecto del mundo real. Y nunca he escrito o visto una prueba unitaria para el registro (aparte de las pruebas para la implementación del registrador en sí), así que espere defectos sin el envoltorio.


No espero cambiar log4foo, pero es ampliamente conocido y sirve como ejemplo. También es interesante cómo las dos respuestas hasta ahora son complementarias: "no siempre se ajustan"; "envolver por si acaso".
lotsoffreetime

@ Falcon: ¿envuelves todo? ORM, interfaz de registro, clases de idiomas principales? Después de todo, uno nunca puede saber cuándo se puede necesitar un mejor HashMap.
Kevin Cline

1

Estoy lidiando con este problema exacto en un proyecto en el que estoy trabajando actualmente. Pero en mi caso, la biblioteca es para gráficos y, por lo tanto, puedo restringir su uso a un pequeño número de clases que se ocupan de los gráficos, en lugar de rociarlos en todo el proyecto. Por lo tanto, es bastante fácil cambiar las API más adelante si es necesario; En el caso de un registrador, el asunto se vuelve mucho más complicado.

Por lo tanto, diría que la decisión tiene mucho que ver con lo que está haciendo exactamente la biblioteca de terceros y cuánto dolor se asociaría con cambiarla. Si cambiar todas las llamadas a la API sería fácil independientemente, entonces probablemente no valga la pena hacerlo. Sin embargo, si cambiar la biblioteca más tarde sería realmente difícil, entonces probablemente lo envolvería ahora.


Más allá de eso, otras respuestas han cubierto muy bien la pregunta principal, así que solo quiero centrarme en esa última adición, sobre la inyección de dependencia y los objetos simulados. Por supuesto, depende de cómo funciona exactamente su marco de registro, pero en la mayoría de los casos no, eso no requeriría un contenedor (aunque probablemente se beneficiará de uno). Simplemente haga que la API para su objeto simulado sea exactamente la misma que la biblioteca de terceros y luego puede intercambiar fácilmente el objeto simulado para probarlo.

El factor principal aquí es si la biblioteca de terceros incluso se implementa a través de la inyección de dependencia (o un localizador de servicios o algún patrón de acoplamiento débil). Si se accede a las funciones de la biblioteca a través de un método singleton o estático o algo así, deberá envolverlo en un objeto con el que pueda trabajar en la inyección de dependencia.


1

Estoy firmemente en el campo final y no con la posibilidad de sustituir la biblioteca de terceros con la mayor prioridad (aunque eso es una ventaja). Mi razonamiento principal que favorece el envoltorio es simple

Las bibliotecas de terceros no están diseñadas para nuestras necesidades específicas.

Y esto se manifiesta, típicamente, en la forma de un montón de duplicación de código, como los desarrolladores que escriben 8 líneas de código solo para crear QButtony diseñarlo de la forma en que debe verse para la aplicación, solo para que el diseñador quiera no solo el aspecto pero también la funcionalidad de los botones para cambiar completamente para todo el software que termina requiriendo volver y reescribir miles de líneas de código, o descubrir que la modernización de una canalización de renderizado requiere una reescritura épica porque la base de código roció ad-hoc de bajo nivel fijo canalice el código de OpenGL por todas partes en lugar de centralizar un diseño de renderizador en tiempo real y dejar el uso de OGL estrictamente para su implementación.

Estos diseños no se adaptan a nuestras necesidades de diseño específicas. Tienden a ofrecer un superconjunto masivo de lo que realmente se necesita (y lo que no es parte de un diseño es tan importante, si no más, que lo que es), y sus interfaces no están diseñadas para satisfacer específicamente nuestras necesidades en un "alto nivel" pensamiento = una solicitud "que nos priva de todo control de diseño central si los usamos directamente. Si los desarrolladores terminan escribiendo un código de nivel mucho más bajo que el requerido para expresar lo que necesitan, a veces pueden terminar envolviéndolo ellos mismos de formas ad-hoc que lo hacen para que termines con docenas de escritos apresurados y groseros. envoltorios diseñados y documentados en lugar de uno bien diseñado y bien documentado.

Por supuesto, aplicaría fuertes excepciones a las bibliotecas donde los contenedores son traducciones casi individuales de lo que las API de terceros tienen para ofrecer. En ese caso, es posible que no se busque un diseño de nivel superior que exprese más directamente los requisitos comerciales y de diseño (tal podría ser el caso de algo que se parezca más a una biblioteca de "utilidad"). Pero si hay un diseño mucho más personalizado disponible que exprese nuestras necesidades de manera mucho más directa, entonces estoy firmemente en el campo de la envoltura, al igual que estoy firmemente a favor de usar una función de nivel superior y reutilizarla sobre el código de ensamblaje en línea por todo el lugar.

Curiosamente, he chocado con los desarrolladores en formas en que parecían tan desconfiados y tan pesimistas sobre nuestra capacidad de diseñar, por ejemplo, una función para crear un botón y devolverlo que preferirían escribir 8 líneas de código de nivel inferior centradas en microscópico detalles de la creación de botones (que terminaron necesitando cambios repetidos en el futuro) sobre el diseño y uso de dicha función. Ni siquiera veo el propósito de que intentemos diseñar algo en primer lugar si no podemos confiar en nosotros mismos para diseñar este tipo de envoltorios de manera razonable.

Dicho de otra manera, veo las bibliotecas de terceros como formas de ahorrar potencialmente un tiempo enorme en la implementación, no como sustitutos del diseño de sistemas.


0

Mi idea sobre bibliotecas de terceros:

Ha habido alguna discusión recientemente en la comunidad iOS sobre pros y contras (OK, en su mayoría contras) de la utilización de dependencias de terceros. Muchos de los argumentos que vi eran bastante genéricos: agrupaban todas las bibliotecas de terceros en una sola canasta. Sin embargo, como con la mayoría de las cosas, no es tan simple. Entonces, tratemos de enfocarnos en un solo caso

¿Deberíamos evitar el uso de bibliotecas de interfaz de usuario de terceros?

Razones para considerar bibliotecas de terceros:

Parece que hay dos razones principales por las que los desarrolladores consideran usar una biblioteca de terceros:

  1. Falta de habilidad o conocimiento. Digamos que estás trabajando en una aplicación para compartir fotos. No comienzas rodando tu propia criptografía.
  2. Falta de tiempo o interés para construir algo. A menos que tenga una cantidad de tiempo ilimitada (que ningún humano tiene todavía) debe priorizar.

La mayoría de las bibliotecas de IU (¡ no todas! ) Tienden a caer en la segunda categoría. Esto no es ciencia espacial, pero lleva tiempo construirlo correctamente.

Si es una función comercial central, hágalo usted mismo, sin importar cuál sea.

Existen casi dos tipos de controles / vistas:

  1. Genérico, lo que le permite usarlos en muchos contextos diferentes, ni siquiera pensados ​​por sus creadores, por ejemplo, UICollectionViewde UIKit.
  2. Específico, diseñado para un solo caso de uso, p UIPickerView. Ej . La mayoría de las bibliotecas de terceros tienden a caer en la segunda categoría. Además, a menudo se extraen de una base de código existente para la que fueron optimizados.

Suposiciones iniciales desconocidas

Muchos desarrolladores realizan revisiones de código de su código interno, pero pueden estar dando por sentado la calidad del código fuente de terceros. Vale la pena pasar un poco de tiempo simplemente navegando por el código de una biblioteca. Es posible que te sorprendas al ver algunas banderas rojas, por ejemplo, el uso de swizzling donde no es necesario.

A menudo, aprender la idea es más beneficioso que obtener el código resultante en sí.

No puedes ocultarlo

Debido a la forma en que UIKit está diseñado, lo más probable es que no pueda ocultar la biblioteca de UI de terceros, por ejemplo, detrás de un adaptador. Una biblioteca se entrelazará con su código de IU convirtiéndose en un hecho de su proyecto.

Costo de tiempo futuro

UIKit cambia con cada lanzamiento de iOS. Las cosas se romperán. Su dependencia de terceros no estará tan libre de mantenimiento como podría esperar.

Conclusión

Desde mi experiencia personal, la mayoría de los usos del código de interfaz de usuario de terceros se reducen a intercambiar una menor flexibilidad para ganar algo de tiempo.

Usamos código listo para enviar nuestro lanzamiento actual más rápido. Sin embargo, tarde o temprano, llegamos a los límites de la biblioteca y estamos ante una decisión difícil: ¿qué hacer a continuación?


0

Usar la biblioteca directamente es más amigable para el equipo de desarrolladores. Cuando un nuevo desarrollador se une, es posible que tenga plena experiencia con todos los marcos utilizados, pero no podrá contribuir de manera productiva antes de aprender su API local. Cuando un desarrollador más joven intenta progresar en su grupo, se vería obligado a aprender su API específica que no está presente en ningún otro lugar, en lugar de adquirir una competencia genérica más útil. Si alguien conoce características o posibilidades útiles de la API original, es posible que no pueda alcanzar la capa escrita por alguien que no estaba al tanto de ellas. Si alguien obtiene una tarea de programación mientras busca un trabajo, es posible que no pueda demostrar las cosas básicas que usó muchas veces, solo porque todas estas veces estaba accediendo a la funcionalidad necesaria a través de su contenedor.

Creo que estos problemas pueden ser más importantes que la posibilidad remota de usar una biblioteca completamente diferente más adelante. El único caso en el que usaría un contenedor es cuando la migración a otra implementación está definitivamente planificada o la API envuelta no está lo suficientemente congelada y sigue cambiando.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.