Sono sempre stato contrario ai progetti lasciati deliberatamente “troppo aperti“, che nascono cioè per uno scopo preciso – quello che il cliente si aspetta e per cui paga – ma contengono già riferimenti espliciti ad (improbabili) aggiustamenti futuri.
L’esempio più semplice sono le strutture C, che compaiono qui e là, con campi marcati deliberatamente “reserved for future use“. L’idea di fondo, tuttavia, è ubiqua, direi quasi quotidiana per tutti: ogni volta che qualcuno individua un possibile reimpiego di un programma, ecco che nel codice ne compare per magia altro, che al momento non serve.
Esiste ma non viene (ancora) usato: rispetto allo scopo originale, è puro dead code a livello semantico. Viene messo come segnaposto, come warning esplicito, per ricordarsi di quell’uso futuro e ancora non meglio precisato.
| Talvolta ci si imbatte in campi di strutture marcati come “reserved for future use” ed impiegati solamente come riempitivi non utilizzati (filler/padding), per arrivare ad occupare una data dimensione di spazio oppure per rispettare qualche vincolo di allineamento.
In sostanza si tratta di spazio al momento sprecato, per cui marcarlo come “non usato” o come “riservato per usi futuri” sembrerebbe voler dire la stesa cosa. In realtà c’è una differenza abissale perchè la seconda variante suona come un preciso messaggio di avvertimento agli incauti programmatori utilizzatori: “Al momento non lo usiamo, ma questo non significa che puoi e devi usarlo tu. Gira al largo, bellimbusto!” In questo post mi riferisco invece a codice che contiene deliberatamente altro codice, al momento inutile, lasciato come un post-it sul frigorifero, in modo da annunciare a chi passa improbabili estensioni future. Lo stesso vale per quello che ruota intorno al codice, ad esempio la documentazione. |
Non è uno sbaglio di per sè, ma aggiungere fin da subito del codice per qualcosa che verrà è una scommessa col futuro che difficilmente può essere vinta.
Per esperienza, pur mettendoci tutto l’impegno e la passione del caso, devo ammettere che questo tipo di strategia non mi ha mai dato soddisfazione: 99 casi su 100, è più facile rifare qualcosa da zero, magari riusando del codice o dei piccolo moduli autocontenuti da qualcosa di esistente, che intestardirsi ad estendere qualcosa di grosso e particolareggiato. Già è spesso difficile arrivare alla fine di un progetto, con continui cambiamenti nei requisiti e nelle funzionalità offerte, figuriamoci predisporre un prodotto per un futuro e completo “retargeting“.
Non sto parlando certo di videogiochi, di “modding” e men che meno “total conversion”, che alla fine producono ancora videogiochi, quindi mantengono lo stesso target: parlo di prodotti customizzati per un dato cliente ed estesi, reinventati per altri tipi di clienti.
| Pre-estendere qualcosa senza sapere come o perchè è come sparare a caso. Inoltre significa accettare l’idea di avere un piede in due scarpe ed una non si sa bene dov’è finita. |
Un approccio migliore: l’architettura a plugin
Credo che l’approccio migliore sia progettare la struttura portante del codice per essere estesa, non inserire già dei riferimenti ai futuri usi.
Cioè, se state scrivendo un programma che supporta diversi formati di file, è meglio pensare a come inserirne altri, in un secondo momento, non cominciare ad aggiungere già del codice per supportarne un altro (presunto) paio.
Progettare un sistema modulare, magari con un’architettura a plugin, è un ottimo esempio di strategia per aggiungere funzionalità a del codice pre-esistente, senza doverlo per forza riaprire se si tratta di un sistema a caricamento dinamico, a runtime. Da Wikipedia:
“Il plugin in campo informatico, è un programma non autonomo che interagisce con un altro programma per ampliarne le funzioni. Ad esempio, un plugin per un software di grafica permette l’utilizzo di nuove funzioni non presenti nel software principale.
La capacità di un software di supportare i plugin è generalmente un’ottima caratteristica, perché rende possibile l’ampliamento e la personalizzazione delle sue funzioni da parte di terzi, in maniera relativamente semplice e veloce.
Ciò favorisce da un lato la minore obsolescenza del software e dall’altro la maggior diffusione, tanto più sono numerosi e funzionali i plugin scritti per uno specifico programma o secondo uno standard specifico: ne è un esempio il successo commerciale dei software di produzione audio di Steinberg, legato alla larga diffusione dei plugin musicali in standard VST.
Per facilitare il compito agli sviluppatori di terze parti che intendono realizzare dei plugin, l’azienda produttrice del software o ideatrice dello standard distribuisce – spesso gratuitamente – dei sistemi detti Kit di Sviluppo Software (Software Development Kit o SDK), che racchiudono funzionalità, esempi e documentazione per lo sviluppatore.”
| Durante il periodo della “rivoluzione MP3″ iniziata con Napster, il lettore software per antonomasia era WinAMP, tuttora esistente.
Nel 1998 fu riscritto per supportare un’architettura a plugin che permettesse a terzi di estenderlo a piacere. Fu un imponente successo: ovunque comparvero plugin per aggiungere questo e quello a quel programma, aumentandone usi e quindi diffusione. Ad agevolare il tutto, un buon kit si sviluppo, una buona documentazione e perfino un libro-manuale, “MP3 Power! With Winamp“. Il bello è che WinAMP non è mai stato un progetto OpenSource ma freeware e poi shareware, quindi codice chiuso. Se ben pensato, un’architettura a plugin permette anche a programmi proprietari di essere estesi da terzi senza dover rivelare nulla dell’interno (black box). Nuovamente: le interfacce sono e fanno la differenza. |
Perchè impiegarli?
L’approccio a plugin generalmente è un buon modo per costringersi a strutturare bene le cose, anche se un domani non li si userà davvero per estenderli.
Tuttavia modellare le cose a plugin non è sempre indicato – non tutti i programmi si prestano o necessitano davvero di questo tipo di soluzione – e può facilmente rivelarsi un overkill/overhead notevole, almeno nei primi momenti di vita di un software.
Molti prodotti famosi – lo stesso Winamp è un esempio – nascono come monolitici o modulari (librerie statiche e dinamiche) ma comunque intrinsecamente statici e solo successivamente, in una major release successiva (v2.0+), riscritti per includere il supporto ai plugin.
Ho detto riscritti e non semplicemente rivisti, perchè l’architettura a plugin è profondamente invasiva e permea/tocca tutto il software, che deve essere adeguato per supportarla. Il tutto, se fatto tardi, diventa facilmente costoso in termini di tempo e risorse da impiegare.
| Se dovete scrivere un programma ed immaginate fin da subito una o più espansioni successive, valutate bene se è il caso includere immediatamente un’architettura a plugin. Il tutto per tenervi aperta una porta verso sviluppi futuri, senza però sacrificare le funzionalità attuali e senza aggiungere dell’inutile codice segnaposto. |
Diventa quindi e prima di tutto un discorso di interfacce, di moduli pensati “decoupled“, ossia indipendenti, rimpiazzabili ed estendibili a piacere. Le interfacce sono tutto: se sono ben pensate, definite, modellate, sapranno servirvi in modi ed in tempi che non sospettate. Inoltre, come ben indicato da Wikipedia, si può arrivare addirittura a veri e propri standard.
| Un esempio di “standard” per plugin è rappresentato da NPAPI, un sistema di creazione di plugin multipiattaforma per il “vecchio” browser Netscape.
Da Wikipedia:
Non è un caso che questo “standard“, semplice e ben definito, sia sopravvissuto a tutte le traversie della società che l’ha prodotto, Netscape, e sia tuttora usato con successo anche da altre aziende e a dispetto della sua età. |
| Per quanto una strutturazione a plugin sia ottima per permettere estensioni di terzi, non fate l’errore di credere che siano adatti ed applicabili solo a quel caso: potete benissimo sfruttarli anche e solo per un uso interno!
Vi basta non rilasciare la documentazione delle interfacce all’esterno e solo voi godrete della possibilità di usarli. |
Che ne pensate?


Mi piacerebbe saperne di più sul lato tecnico e pratico.
Ci sono cambiamenti [u]rilevanti[/u] in termini di performance rispetto un programma monolitico o modulare e uno a plugins? Come si può realizzare un software che fa uso di plugins (interfacce e sdk)?
@Stefano: un plugin alla fine della fiera è una libreria dinamica/condivisa, caricata e scaricata dinamicamente, che offre qualcosa al “caricante”. Per farlo usa delle interfacce/API comuni o qualcosa che enumeri comunque il contenuto, così che il “caricante” possa accederlo e sfruttarlo in modo conveniente. Una volta che la libreria è caricata e si ha accesso al contenuto, tutto risiede nello spazio del processo caricante (o per meglio dire “è come se”) per cui le prestazioni ammesso che ne risentano, ne risentono in modo impercettibile.
Scrivere un sistema a plugin è prima di tutto capire come operare su una data piattaforma: come costruire le librerie, come caricarle e scaricarle a runtime… Poi sta a te creare delle interfacce, ossia un elenco di cose che il caricatore si aspetta di trovare. Nota che non è obbligatorio usare linguaggi OOP come C++, C# o Java ma si può usare perfino il C.
Non è facile spiegare come fare in poche righe – soprattutto dentro un commento -. Se mi riesce e ho tempo, tornerò sull’argomento…
Ciao & grazie di essere passato! ^^