Utilizzo di librerie di terze parti: utilizzare sempre un wrapper?


78

La maggior parte dei progetti con cui sono coinvolto utilizza diversi componenti open source. Come principio generale, è sempre una buona idea evitare di associare tutti i componenti del codice alle librerie di terze parti e passare invece attraverso un wrapper incapsulante per evitare il dolore del cambiamento?

Ad esempio, la maggior parte dei nostri progetti PHP utilizza direttamente log4php come framework di registrazione, ovvero istanza tramite \ Logger :: getLogger (), utilizzano i metodi -> info () o -> warn (), ecc. In futuro, tuttavia, potrebbe apparire un ipotetico framework di registrazione che è meglio in qualche modo. Allo stato attuale, tutti i progetti che si avvicinano strettamente alle firme del metodo log4php dovrebbero cambiare, in dozzine di posti, per adattarsi alle nuove firme. Ciò avrebbe ovviamente un ampio impatto sulla base di codice e qualsiasi modifica è un potenziale problema.

Per nuove basi di codice a prova di futuro da questo tipo di scenario, considero spesso (e talvolta implemento) una classe wrapper per incapsulare la funzionalità di registrazione e rendere più semplice, sebbene non infallibile, alterare il modo in cui la registrazione funzionerà in futuro con il minimo cambiamento ; il codice chiama il wrapper, il wrapper passa la chiamata al framework di registrazione del giorno .

Tenendo presente che ci sono esempi più complicati con altre biblioteche, sono un eccesso di ingegneria o è una saggia precauzione nella maggior parte dei casi?

EDIT: Altre considerazioni: l'utilizzo dell'iniezione di dipendenza e dei doppi di test richiede praticamente di sottrarre comunque la maggior parte delle API ("Voglio controllare il mio codice esegue e aggiorna il suo stato, ma non scrivere un commento nel registro / accedere a un vero database"). Non è questo un decisore?


3
log4XYZ è un marchio così forte. La sua API cambierà non prima di quando cambierà l'API per un elenco collegato. Entrambi sono un problema risolto da tempo.
Giobbe

1
Esatto duplicato di questa domanda SO: stackoverflow.com/questions/1916030/…
Michael Borgwardt,

1
Se lo stai usando solo internamente, se lo avvolgi o no è solo un compromesso tra lavoro noto ora e possibile lavoro in seguito. Una chiamata di giudizio. Ma qualcosa che altri rispondenti sembrano aver trascurato di parlare è se si tratta di una dipendenza API o di una implementazione . In altre parole, stai perdendo lezioni da questa API di terze parti attraverso la tua API pubblica e la stai esponendo agli utenti? In questo caso non è più una semplice questione di duro lavoro passare a una libreria diversa, il problema è che ora è impossibile senza rompere la tua API. Questo è molto brutto!
Elias Vasylenko,

1
Per ulteriori riferimenti: questo modello si chiama cipolla-architettura in cui l'infrastruttura esterna (la si chiama lib esterna) è nascosta dietro un'interfaccia
k3b

Risposte:


42

Se usi solo un piccolo sottoinsieme dell'API di terze parti, ha senso scrivere un wrapper: questo aiuta a incapsulare e nascondere le informazioni, assicurandoti di non esporre un'API forse enorme al tuo codice. Può anche aiutare ad assicurarsi che qualsiasi funzionalità che non si desidera utilizzare sia "nascosta".

Un altro buon motivo per un wrapper è se ti aspetti di cambiare la libreria di terze parti. Se si tratta di un'infrastruttura che sai che non cambierà, non scrivere un wrapper.


Aspetti positivi, ma ci viene insegnato che il codice strettamente associato è negativo, per molte ragioni ben comprese (più difficile da testare, più difficile da refactorizzare, ecc.). Una formulazione alternativa della domanda è "se l'accoppiamento è errato, perché è corretto accoppiarsi a un'API?".
lotsoffreetime,

7
@lotsoffreetime Non puoi evitare l'accoppiamento con un'API. Pertanto, è meglio accoppiarsi alla propria API. In questo modo, è possibile cambiare la libreria e generalmente non è necessario modificare l'API fornita dal wrapper.
George Marian,

@ george-marian Se non riesco a evitare di utilizzare una determinata API, posso certamente ridurre al minimo i punti di contatto. La domanda è: dovrei provare a farlo sempre o è esagerare?
lotsoffreetime,

2
@lotsoffreetime Questa è una domanda difficile a cui rispondere. Ho ampliato la mia risposta a tal fine. (Fondamentalmente, dipende da molti se.)
George Marian,

2
@lotsoffreetime: se hai un sacco di tempo libero, puoi farlo anche tu. Ma sconsiglio di scrivere un wrapper API, tranne in queste condizioni: 1) l'API originale è di livello molto basso, quindi scrivi un'API di livello superiore per soddisfare le tue esigenze specifiche di progetto, oppure 2) hai un piano nel nel prossimo futuro per cambiare libreria, stai usando la libreria corrente solo come trampolino di lancio mentre cerchi una migliore.
Lie Ryan,

28

Senza sapere quali nuove straordinarie funzionalità avrà questo presunto futuro logger migliorato, come scriveresti il ​​wrapper? La scelta più logica è quella di far istanziare il tuo wrapper una sorta di classe logger e avere metodi come ->info()o ->warn(). In altre parole, sostanzialmente identico alla tua attuale API.

Piuttosto che un codice a prova di futuro che potrei non aver mai bisogno di cambiare, o che potrebbe richiedere comunque una riscrittura inevitabile, preferisco il codice "a prova di passato". Cioè, nelle rare occasioni in cui cambio significativamente un componente, è quando scrivo un wrapper per renderlo compatibile con il codice passato. Tuttavia, qualsiasi nuovo codice utilizza la nuova API e refactoring il vecchio codice per usarlo ogni volta che sto apportando una modifica nello stesso file o quando la pianificazione lo consente. Dopo alcuni mesi, posso rimuovere l'involucro e il cambiamento è stato graduale e robusto.

In altre parole, i wrapper hanno davvero senso solo quando conosci già tutte le API che devi avvolgere. Buoni esempi sono se l'applicazione deve attualmente supportare molti driver di database, sistemi operativi o versioni di PHP diversi.


"... i wrapper hanno davvero senso solo quando conosci già tutte le API che devi avvolgere." Questo sarebbe vero se stavo abbinando l'API nel wrapper; forse dovrei usare il termine "incapsulamento" più fortemente di wrapper. Vorrei astrarre queste chiamate API per "registrare questo testo in qualche modo" piuttosto che "chiama foo :: log () con questo parametro".
lotsoffreetime,

"Senza sapere quali nuove straordinarie funzionalità avrà questo presunto futuro logger migliorato, come scriverebbe il wrapper?" @ kevin-cline di seguito menziona un logger futuro con prestazioni migliori, piuttosto che una funzionalità più recente. In questo caso, nessuna nuova API da avvolgere, solo un diverso metodo di fabbrica.
lotsoffreetime,

27

Avvolgendo una libreria di terze parti aggiungi un ulteriore livello di astrazione su di essa. Questo ha alcuni vantaggi:

  • La tua base di codice diventa più flessibile alle modifiche

    Se hai mai bisogno di sostituire la libreria con un'altra, devi solo cambiare l'implementazione nel tuo wrapper, in un unico posto . Puoi cambiare l'implementazione del wrapper e non devi cambiare nulla di tutto, in altre parole hai un sistema liberamente accoppiato. Altrimenti dovresti esaminare l'intera base di codice e apportare modifiche ovunque, il che ovviamente non è quello che desideri.

  • È possibile definire l'API del wrapper indipendentemente dall'API della libreria

    Librerie diverse possono avere API notevolmente diverse e allo stesso tempo nessuna di esse può essere esattamente ciò di cui hai bisogno. Che cosa succede se una libreria necessita di un token per essere passato insieme ad ogni chiamata? Puoi trasferire il token nella tua app ovunque sia necessario utilizzare la libreria o puoi proteggerlo da qualche parte più centralmente, ma in ogni caso ti serve il token. La tua classe wrapper semplifica di nuovo tutto questo, perché puoi semplicemente mantenere il token all'interno della tua classe wrapper, non esponendolo mai a nessun componente all'interno della tua app e togliendo completamente la necessità. Un enorme vantaggio se hai mai usato una libreria che non enfatizza una buona progettazione delle API.

  • Il test unitario è molto più semplice

    I test unitari dovrebbero testare solo una cosa. Se vuoi testare un'unità di una classe devi prendere in giro le sue dipendenze. Questo diventa ancora più importante se quella classe effettua chiamate di rete o accede ad altre risorse al di fuori del tuo software. Avvolgendo la libreria di terze parti è facile prendere in giro quelle chiamate e restituire i dati di test o qualsiasi altra cosa richiesta da unit test. Se non hai un tale livello di astrazione, diventa molto più difficile farlo - e il più delle volte si traduce in un sacco di codice brutto.

  • Si crea un sistema liberamente accoppiato

    Le modifiche al wrapper non hanno alcun effetto su altre parti del software, almeno finché non si modifica il comportamento del wrapper. Introducendo uno strato di astrazione come questo wrapper puoi semplificare le chiamate alla libreria e rimuovere quasi completamente la dipendenza dell'app dalla libreria. Il tuo software utilizzerà semplicemente il wrapper e non farà alcuna differenza nel modo in cui il wrapper viene implementato o come fa ciò che fa.


Esempio pratico

Diciamo la verità. Le persone possono discutere dei vantaggi e degli svantaggi di qualcosa del genere per ore, motivo per cui preferisco piuttosto mostrarvi un esempio.

Supponiamo che tu abbia una sorta di app Android e devi scaricare immagini. Ci sono un sacco di librerie là fuori che rendono il caricamento e la memorizzazione nella cache delle immagini un gioco da ragazzi, ad esempio Picasso o Universal Image Loader .

Ora possiamo definire un'interfaccia che useremo per avvolgere qualunque libreria finiremo usando:

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

Questa è l'interfaccia che ora possiamo usare in tutta l'app ogni volta che dobbiamo caricare un'immagine. Siamo in grado di creare un'implementazione di questa interfaccia e utilizzare l'iniezione di dipendenza per iniettare un'istanza di tale implementazione ovunque utilizziamo ImageService.

Diciamo che inizialmente decidiamo di usare Picasso. Ora possiamo scrivere un'implementazione per la ImageServicequale utilizza 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();
    }
}

Abbastanza semplice se me lo chiedi. Avvolgere le biblioteche non deve essere complicato per essere utile. L'interfaccia e l'implementazione hanno meno di 25 righe di codice combinate, quindi non è stato quasi uno sforzo creare questo, ma già otteniamo qualcosa facendo questo. Vedi il Contextcampo nell'implementazione? Il framework di iniezione delle dipendenze di tua scelta si occuperà già di iniettare quella dipendenza prima di usare la nostra ImageService, la tua app ora non deve preoccuparsi di come le immagini vengono scaricate e di quali dipendenze possa avere la libreria. Tutto ciò che la tua app vede è un ImageServicee quando ha bisogno di un'immagine con cui chiama load()con un url - semplice e diretto.

Tuttavia, il vero vantaggio arriva quando iniziamo a cambiare le cose. Immagina che ora dobbiamo sostituire Picasso con Universal Image Loader perché Picasso non supporta alcune funzionalità di cui abbiamo assolutamente bisogno in questo momento. Dobbiamo ora esaminare la nostra base di codice e sostituire noiosamente tutte le chiamate a Picasso e quindi gestire dozzine di errori di compilazione perché abbiamo dimenticato alcune chiamate di Picasso? No. Tutto ciò che dobbiamo fare è creare una nuova implementazione ImageServicee dire al nostro framework di iniezione delle dipendenze di utilizzare questa implementazione da ora in poi:

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);
    }
}

Come puoi vedere l'implementazione potrebbe essere molto diversa, ma non importa. Non abbiamo dovuto modificare una singola riga di codice in nessun altro punto della nostra app. Usiamo una libreria completamente diversa che potrebbe avere funzionalità completamente diverse o potrebbe essere utilizzata in modo molto diverso, ma alla nostra app non importa. Come prima il resto della nostra app vede l' ImageServiceinterfaccia con il suo load()metodo e tuttavia questo metodo è implementato non ha più importanza.

Almeno per me questo già suona già abbastanza carino, ma aspetta! C'è ancora di più. Immagina di scrivere unit test per una classe su cui stai lavorando e questa classe usa il ImageService. Ovviamente non puoi permettere alle tue unit test di effettuare chiamate di rete verso alcune risorse situate su qualche altro server ma dato che ora stai usando ImageServicepuoi facilmente load()restituire uno statico Bitmapusato per le unit test implementando un derisione ImageService:

public class MockImageService implements ImageService {

    private final Bitmap mMockBitmap;

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

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

Per riassumere avvolgendo le librerie di terze parti, la tua base di codice diventa più flessibile alle modifiche, nel complesso più semplice, più facile da testare e riduci l'accoppiamento di diversi componenti nel tuo software - tutte cose che diventano sempre più importanti man mano che mantieni un software.


1
Questo vale anche per un'API instabile. Il nostro codice non cambia in 1000 posizioni solo perché è cambiata la libreria sottostante. Risposta molto bella.
RubberDuck,

Risposta molto concisa e chiara. Faccio lavori front-end sul web. La quantità di cambiamenti in quel paesaggio è folle. Il fatto che le persone "pensino" che non ci saranno cambiamenti non significa che non ci saranno cambiamenti. Ho visto menzioni di YAGNI. Vorrei aggiungere un nuovo acronimo, YDKYAGNI, non sai che non ne avrai bisogno. Soprattutto con le implementazioni relative al web. Di norma avvolgo sempre le librerie che espongono solo una piccola API (come select2). Librerie più grandi influiscono sulla tua architettura e il loro avvolgimento significa che ti aspetti che la tua architettura cambi, potrebbe, ma è meno probabile che lo faccia.
Arrivederci il

La tua risposta è stata super utile e presentare il concetto con un esempio ha reso il concetto ancora più chiaro.
Anil Gorthy,

24

Penso che avvolgere le librerie di terze parti oggi nel caso in cui qualcosa di meglio accada domani sia una violazione molto dispendiosa di YAGNI. Se si chiama ripetutamente il codice di terze parti in un modo peculiare per la propria applicazione, si dovrà (dovrebbe) rielaborare tali chiamate in una classe di wrapping per eliminare la ripetizione. Altrimenti stai utilizzando completamente l'API della libreria e qualsiasi wrapper sembrerebbe proprio come la libreria stessa.

Supponiamo ora che una nuova libreria appaia con prestazioni superiori o altro. Nel primo caso, devi solo riscrivere il wrapper per la nuova API. Nessun problema.

Nel secondo caso, si crea un wrapper adattando la vecchia interfaccia per guidare la nuova libreria. Un po 'più di lavoro, ma nessun problema, e niente più lavoro di quello che avresti fatto se avessi scritto il wrapper in precedenza.


4
Non penso che YAGNI si applichi necessariamente in questa situazione. Non si tratta di integrare funzionalità nel caso in cui sia necessario in futuro. Si tratta di costruire flessibilità nell'architettura. Se tale flessibilità non è necessaria, allora sì, si applica YAGNI. Tuttavia, tale determinazione tende a essere presa in futuro quando si apporta la modifica sarà probabilmente dolorosa.
George Marian,

7
@ George Marian: il problema è il 95% delle volte, non avrai mai bisogno della flessibilità per cambiare. Se devi passare a una nuova libreria futura con prestazioni superiori, dovrebbe essere abbastanza banale cercare / sostituire le chiamate o scrivere un wrapper quando ne hai bisogno. D'altra parte, se la tua nuova libreria presenta funzionalità diverse, il wrapper ora diventa un ostacolo poiché ora hai due problemi: porting del vecchio codice per sfruttare le nuove funzionalità e mantenere il wrapper.
Lie Ryan,

3
@lotsoffreetime: lo scopo del "buon design" è di minimizzare il costo totale dell'applicazione per tutta la sua durata. L'aggiunta di livelli di riferimento indiretto per i cambiamenti futuri immaginati è un'assicurazione molto costosa. Non ho mai visto nessuno realizzare alcun risparmio da questo approccio. Crea semplicemente un lavoro banale per i programmatori il cui tempo sarebbe molto meglio speso per esigenze specifiche del cliente. Il più delle volte, se stai scrivendo un codice non specifico per il tuo cliente, stai sprecando tempo e denaro.
Kevin Cline,

1
@Gorge: se questi cambiamenti sono dolorosi, penso che sia un odore di processo. In Java, vorrei creare nuove classi con gli stessi nomi delle vecchie classi, ma in un pacchetto diverso, modificare tutte le occorrenze del vecchio nome del pacchetto ed eseguire nuovamente i test automatizzati.
Kevin Cline,

1
@kevin Questo è più lavoro e quindi comporta più rischi rispetto al semplice aggiornamento del wrapper e all'esecuzione dei test.
George Marian,

9

Il motivo di base per scrivere un wrapper in una libreria di terze parti è che è possibile scambiare quella libreria di terze parti senza modificare il codice che la utilizza. Non puoi evitare l'accoppiamento a qualcosa, quindi l'argomento dice che è meglio accoppiarsi a un'API che hai scritto.

Se questo vale la pena, è una storia diversa. Tale dibattito probabilmente continuerà per molto tempo.

Per i piccoli progetti, dove la probabilità che un tale cambiamento sia necessario è bassa, è probabilmente uno sforzo inutile. Per i progetti più grandi, tale flessibilità può benissimo superare lo sforzo supplementare per avvolgere la biblioteca. Tuttavia, è difficile sapere se questo è il caso in anticipo.

Un altro modo di osservarlo è quel principio base di astrazione di ciò che è probabile che cambi. Quindi, se la libreria di terze parti è ben consolidata e è improbabile che venga cambiata, potrebbe andar bene non avvolgerla. Tuttavia, se la libreria di terze parti è relativamente nuova, è più probabile che debba essere sostituita. Detto questo, lo sviluppo di biblioteche affermate è stato abbandonato molte volte. Quindi, questa non è una domanda facile a cui rispondere.


Nel caso di unit test in cui la possibilità di iniettare una simulazione dell'API serve a minimizzare l'unità sotto test, il "cambiamento potenziale" non è un fattore. Detto questo, questa è ancora la mia risposta preferita in quanto è più vicina a come penso. Cosa direbbe lo zio Bob? :)
lotsoffreetime

Inoltre, i piccoli progetti (nessuna squadra, specifiche di base, ecc.) Hanno le proprie regole in cui è possibile violare le buone pratiche come questa e cavarsela in una certa misura. Ma questa è una domanda diversa ...
lotsoffreetime,

1

Oltre a quanto già detto da @Oded , vorrei solo aggiungere questa risposta allo scopo speciale di registrazione.


Ho sempre un'interfaccia per la registrazione ma non ho mai dovuto sostituire un log4fooframework ancora.

Ci vuole solo mezz'ora per fornire l'interfaccia e scrivere il wrapper, quindi immagino che non perderai troppo tempo se risulta inutile.

È un caso speciale di YAGNI. Anche se non ne ho bisogno, non ci vuole molto tempo e mi sento più sicuro. Se il giorno dello scambio del logger arriva davvero, sarò felice di aver investito mezz'ora perché mi risparmierà più di un giorno a scambiare chiamate in un progetto del mondo reale. E non ho mai scritto o visto un unit test per il logging (a parte i test per l'implementazione del logger stesso), quindi aspettatevi difetti senza il wrapper.


Non mi aspetto di cambiare log4foo, ma è ampiamente noto e serve da esempio. È anche interessante come le due risposte finora siano complementari: "non sempre avvolgere"; "avvolgi per ogni evenienza".
lotsoffreetime,

@Falcon: avvolgi tutto? ORM, interfaccia di registro, lezioni di lingua di base? Dopotutto, non si può mai dire quando potrebbe essere necessaria una migliore HashMap.
Kevin Cline,

1

Sto affrontando questo problema esatto su un progetto a cui sto attualmente lavorando. Ma nel mio caso la libreria è per la grafica e quindi sono in grado di limitarne l'uso a un piccolo numero di classi che si occupano di grafica, invece di spargerla per tutto il progetto. Quindi è abbastanza facile cambiare API in seguito, se necessario; nel caso di un logger la questione diventa molto più complicata.

Quindi direi che la decisione ha molto a che fare con ciò che la biblioteca di terze parti sta facendo esattamente e quanto dolore sarebbe associato al suo cambiamento. Se cambiare tutte le chiamate API sarebbe facile a prescindere, probabilmente non vale la pena farlo. Se tuttavia cambiare la libreria in un secondo momento fosse davvero difficile, probabilmente l'avrei avvolto ora.


Oltre a ciò, altre risposte hanno coperto molto bene la domanda principale, quindi voglio solo concentrarmi sull'ultima aggiunta, sull'iniezione di dipendenza e sugli oggetti finti. Dipende ovviamente da come funziona esattamente il tuo framework di registrazione, ma nella maggior parte dei casi non è necessario un wrapper (anche se probabilmente ne trarrà beneficio). Rendi l'API per il tuo oggetto simulato esattamente uguale alla libreria di terze parti e quindi puoi facilmente scambiare l'oggetto simulato per il test.

Il fattore principale qui è se la libreria di terze parti viene implementata o meno attraverso l'iniezione delle dipendenze (o un localizzatore di servizi o alcuni di questi modelli liberamente accoppiati). Se si accede alle funzioni della libreria tramite un metodo singleton o statico o qualcosa del genere, è necessario racchiuderlo in un oggetto con cui è possibile lavorare nell'iniezione delle dipendenze.


1

Sono fortemente nel campo di confezionamento e non con la possibilità di sostituire la biblioteca di terze parti con la massima priorità (anche se questo è un bonus). La mia logica principale che favorisce il confezionamento è semplice

Le librerie di terze parti non sono progettate per le nostre esigenze specifiche.

E questo si manifesta, in genere, sotto forma di un carico di duplicazione del codice, come gli sviluppatori che scrivono 8 righe di codice solo per crearne uno QButtone disegnarlo nel modo in cui dovrebbe cercare l'applicazione, solo per il progettista che desidera non solo l'aspetto ma anche la funzionalità dei pulsanti per cambiare completamente per l'intero software che finisce per richiedere di riscrivere e riscrivere migliaia di righe di codice, o scoprire che la modernizzazione di una pipeline di rendering richiede una riscrittura epica perché la base di codice è cosparsa di basso livello ad hoc fisso- pipeline di codice OpenGL dappertutto invece di centralizzare un progetto di rendering in tempo reale e lasciare rigorosamente l'uso di OGL per la sua implementazione.

Questi design non sono adattati alle nostre esigenze di progettazione specifiche. Tendono a offrire un enorme superset di ciò che è effettivamente necessario (e ciò che non fa parte di un progetto è importante, se non di più, di quello che è) e le loro interfacce non sono progettate per soddisfare specificamente le nostre esigenze in un livello "alto" pensiero = una richiesta "tipo di modo che ci priva di ogni controllo centrale del progetto se li utilizziamo direttamente. Se gli sviluppatori finiscono per scrivere un codice di livello molto più basso di quello che dovrebbe essere necessario per esprimere ciò di cui hanno bisogno, a volte possono finire per racchiuderlo da soli in modi ad hoc che lo rendono così si finisce con dozzine di scritti frettolosamente e rozzamente- involucri progettati e documentati anziché uno ben progettato e ben documentato.

Ovviamente applicherei forti eccezioni alle biblioteche in cui i wrapper sono traduzioni quasi individuali di ciò che le API di terzi hanno da offrire. In tal caso, potrebbe non essere possibile ricercare progetti di livello superiore che esprimano più direttamente i requisiti aziendali e di progettazione (ciò potrebbe essere il caso di qualcosa che assomiglia di più a una libreria di "utilità"). Ma se è disponibile un design molto più su misura che esprime in modo molto più diretto le nostre esigenze, allora sono fortemente nel campo del wrapping, così come sono fortemente favorevole all'utilizzo di una funzione di livello superiore e al riutilizzo su un codice assembly integrato dappertutto.

Stranamente mi sono scontrato con gli sviluppatori in modi in cui sembravano così diffidenti e così pessimisti sulla nostra capacità di progettare, diciamo, una funzione per creare un pulsante e restituirlo che avrebbero preferito scrivere 8 righe di codice di livello inferiore focalizzate sul microscopico dettagli sulla creazione di pulsanti (che alla fine hanno dovuto cambiare più volte in futuro) sulla progettazione e l'utilizzo di detta funzione. Non vedo nemmeno lo scopo di noi cercare di progettare qualcosa in primo luogo se non possiamo fidarci di noi stessi nel progettare questo tipo di involucri in modo ragionevole.

In altre parole, vedo le librerie di terze parti come modi per risparmiare potenzialmente tempo enorme nell'implementazione, non come sostituti per la progettazione di sistemi.


0

La mia idea su librerie di terze parti:

Recentemente nella community di iOS è stata discussa la questione dei pro e dei contro (OK, per lo più contro) dell'uso di dipendenze di terze parti. Molti argomenti che ho visto erano piuttosto generici, raggruppando tutte le librerie di terze parti in un paniere. Come per la maggior parte delle cose, però, non è così semplice. Quindi, proviamo a concentrarci su un singolo caso

Dovremmo evitare l'uso di librerie di UI di terze parti?

Motivi per considerare le librerie di terze parti:

Sembrano esserci due ragioni principali per cui gli sviluppatori considerano l'utilizzo di una libreria di terze parti:

  1. Mancanza di abilità o conoscenza. Supponiamo che tu stia lavorando su un'app di condivisione foto. Non inizi a lanciare la tua criptovaluta.
  2. Mancanza di tempo o interesse per costruire qualcosa. A meno che tu non abbia un periodo di tempo illimitato (che nessun essere umano ha ancora) devi dare la priorità.

La maggior parte delle librerie dell'interfaccia utente ( non tutte! ) Tendono a rientrare nella seconda categoria. Questa roba non è scienza missilistica, ma ci vuole tempo per costruirla nel modo giusto.

Se si tratta di una funzione aziendale principale, fallo da solo, non importa quale sia.

Esistono praticamente due tipi di controlli / visualizzazioni:

  1. Generico, che ti consente di usarli in molti contesti diversi nemmeno pensati dai loro creatori, ad esempio UICollectionViewda UIKit.
  2. Specifico, progettato per un singolo caso d'uso, ad es UIPickerView. La maggior parte delle biblioteche di terze parti tende a rientrare nella seconda categoria. Inoltre, vengono spesso estratti da una base di codice esistente per la quale sono stati ottimizzati.

Ipotesi iniziali sconosciute

Molti sviluppatori eseguono revisioni del codice del proprio codice interno ma potrebbero dare per scontata la qualità del codice sorgente di terze parti. Vale la pena dedicare un po 'di tempo a sfogliare il codice di una libreria. Potresti finire per essere sorpreso di vedere alcune bandiere rosse, ad esempio lo swizzling usato dove non è necessario.

Spesso apprendere l'idea è più vantaggioso che ottenere il codice risultante stesso.

Non puoi nasconderlo

A causa del modo in cui UIKit è progettato, molto probabilmente non sarai in grado di nascondere la libreria dell'interfaccia utente di terze parti, ad esempio dietro un adattatore. Una libreria si intreccia con il tuo codice UI diventando di fatto parte del tuo progetto.

Costo del tempo futuro

UIKit cambia con ogni versione di iOS. Le cose si romperanno. La tua dipendenza da terze parti non sarà così esente da manutenzione come puoi aspettarti.

Conclusione :

Dalla mia esperienza personale, la maggior parte degli usi del codice dell'interfaccia utente di terze parti si riduce a scambiare una minore flessibilità per guadagnare tempo.

Usiamo il codice pronto per spedire la nostra versione attuale più velocemente. Prima o poi, però, raggiungiamo i limiti della biblioteca e ci troviamo di fronte a una decisione difficile: cosa fare dopo?


0

L'uso diretto della libreria è più intuitivo per il team di sviluppatori. Quando un nuovo sviluppatore si unisce, può avere piena esperienza con tutti i framework utilizzati, ma non sarà in grado di contribuire in modo produttivo prima di apprendere l'API cresciuta in casa. Quando uno sviluppatore più giovane tenta di progredire nel tuo gruppo, sarebbe costretto ad apprendere la tua specifica API non presente altrove, invece di acquisire competenze generiche più utili. Se qualcuno conosce utili funzionalità o possibilità dell'API originale, potrebbe non essere in grado di raggiungere il livello scritto da qualcuno che non ne era a conoscenza. Se qualcuno otterrà un'attività di programmazione durante la ricerca di un lavoro, potrebbe non essere in grado di dimostrare le cose di base che ha usato molte volte, solo perché tutte queste volte stava accedendo alle funzionalità necessarie tramite il wrapper.

Penso che questi problemi possano essere più importanti della possibilità piuttosto remota di utilizzare una libreria completamente diversa in seguito. L'unico caso in cui utilizzerei un wrapper è quando la migrazione verso un'altra implementazione è sicuramente pianificata o l'API wrapping non è abbastanza congelata e continua a cambiare.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.