INTRO L'idea di scrivere un articolo sulla programmazione modulare e' nata dal desiderio di condividere una metodologia di programmazione. Poiche' spesso nei codici dei principali programmi sviluppati da italiani l'elemento metodologico non viene valorizzato e spesso nemmeto tenuto in considerazione. - Ovviamente non parlo di tutti i programmi italiani perche' non ho conoscenza di tutte creazioni made in italy, ma sarei felice che qualcuno si sentisse offeso da questa mia provocazione :) - Come il kernel, dagli anni 80 ad oggi, e' passato da un approccio monolitico ad uno modulare cosi e' stato per buona parte degli applicativi di ultima generazione. Il passaggio e' stato tanto parallelo che nello studiare il codice di alcuni software, potremmo quasi definirli entro un' epoca precisa dell'eta' dell'informatica. Quando scrivere applicazioni modulari? La risposta e': sempre. Sempre da quando non siamo piu' vittime della necessita' di soddisfare solo un bacino di utenti ristretto, ma possiamo pensare che il nostro applicativo e' destinato, potenzialmente, ad ognuno degli abitanti del pianeta. Questo salto dimensionale ci pone difronte ad una realta' nella quale ogni utente potenzialmente ha la possibilita' di modificare il nostro software (sia esso compilato o meno) per soddisfare specifiche necessita'. quindi dovremmo cominciare pensare i nostri applicativi come un continuo working progress. Cosa si intende con software modulari? Esistono varie tipologie di approcci alla scomposizione di funzioni e funzionalita' nella scrittura di codice: * Esistono applicativi che gestiscono plugin runtime. * altri che funzionano grazie ad una interazione costante di micro programmi che nell'insieme compiono delle funzionalita' * altri ancora supportano delle estensioni Ognuno di queste modalita' reca con se una struttura, un organigramma, una serie di layer e quindi degli algoritmi che vale la pena di andare ad analizzare. Per cominciare differenziamo il termine modulo: * modulo: Un'aggiunta funzionale ad un programma capace di supportarne la sintassi. Un esempio puo' essere un modulo ouput per xmms o un elemento nuovo nella taskbar di xfce4. Il modulo in questi due esempi e' un codice compilato che in se stesso non compie nessuna funzione, ma che ottiene valore e significato una volta che e' sfuttato correttamente dal programma per il quale e' stato scritto. Esistono esempi di moduli scritti per determinati programmi e poi usati da altri. Un esempio e' mplayer che sfrutta i moduli di xmms per far suonare tipologie di file da esso non riconosciute. Mplayer pero' e' solito fare cio' con ogni genere di programma, libreria, dll, codec attraverso accrocchi quasi mistici, e per lo piu' funzionanti. * estensione: Aggiunta di funzioni esterne a programmi gia' autosufficienti. Le estensioni di firefox sono da considerarsi, appunto, estensioni perche' non completano il funzionamento di questo browser, ma vi aggiungono elementi non essenziali. La differenza tra modulo ed estensione e' cosi' labile che ogni tanto non si e' in grado di dare con certezza una definizione. Pensiamo ad esempio a tutti i formati, le funzioni e gli effetti di gimp. Questi sono da considerarsi estensioni o moduli? Sicuramente gimp funziona senza questi codici, ma sarebbe lo stesso il gimp che utiliziamo senza questi elementi? * libreria esterna: Un'altro elemento essenziale alla base della programmazione sono le librerie. Con libreria intendo una porzione di funzioni appositamente realizzate per qualunque software ne faccia richiesta e sia in grado di supportarle. Gli esempi sono infiniti e vanno da librerie grafiche -gimp, xchat, gedit utilizzano tutti le librerie gtk per disegnare sul vostro schermo le loro rispettive interfaccie-, a librerie di calcolo, accesso a filesystem, criptazione, rete, sicurezza e infiniti altri settori. Programmare un software in modo che alcune funzioni siano esterne e inglobate in una libreria significa rendere quel codice impiegabile da altri coders. In questo modo si aumenta esponenzialmente la complessita' degli applicativi, la loro bellezza e soprattutto la loro funzionalita'. "Librerie esterne" e' una locuzione errata, ma rende bene l'idea del punto di vista che vorrei esporre: esterne per chi sta scrivendo il programma, esterne per il programma stesso e quindi il codice venga riutilizzato da altri. Si possono quindi definire come librerie condivise. * libreria interna: Occorre pensare alle librerie interne similmente al ragionamento sopradescritto. Un programma ha bisogno di layer, a meno che naturalmete non sia composto da meno di 1000 righe di codice. Layer: ovvero una stratificazione complessa e gerarchica che regola quali porzioni di codice possano richiamare altre funzioni, quando e perche'. Questo avviene per ragioni di chiarezza mentale, maggiori elementi di debug, e soprattutto perche' cosi' ogni parte e' facilmente modificabile senza intaccarne l'insieme. Esattamente come le librerie esterne queste ultime mettono a disposizione funzioni. Queste funzioni accettano solo specifici dati e al contempo ne restituiscono altri in un preciso formato. Nulla si puo' fare senza rispettare queste regole, lo stesso vale all'interno dei layer di un programma. Indubbiamente esistono linguaggi maggiormente inclini a quest'approccio, ma come sempre in tutti i campi e' il metodo, non lo strumento che fa si che le cose vadano a buon fine :) * protocollo: Il trait' union degli elementi descritti e' costituito dal protocollo ovvero dallo standard sintattico di comunicazione tra i pezzi di separati (o meno) di un software. Protocollo e' un termine ultra generico che comprende dallo standard di rete (come avviene la comunicazione tra client e server del tale servizio) a come devono comunicare i programmi grafici col server X perche' tutto funzioni. L'idea di fondo, oltre alla parola magica "documentazione" e' quella di rendere modulare ogni punto di contatto tra un programma e' l'altro. Ove ci sia una comunicazione tra programmi, li' e' da realizzarsi una libreria esterna; ove si ritrovino delle similitudini tra porzioni di codice, li' e' da rendere modulare un software. Dopo questa manfrina teorica, passiamo alla sostanza con degli esempi reali che possano essere utili ai neo programmatori o a chi vuolesse iniziare un nuovo progetto "modularmente". Ultimamente sto (ri)collaborando con lesion per la scrittura del suo programma "Paranoy" ovvero un client di posta incentrato sulla sicurezza dei dati, delle comunicazioni e dei protocolli. Paranoy e' un programma che predilige gli standard criptati per il download della posta e cripta password e messaggi sia nei salvataggi su disco, sia nei passaggi in memoria. Le mie collaborazioni al progetto sono state di duplice tipologia: _malloc e paranoy_body. Il primo e' una libreria che randomizza dati in memoria, il che rende particolarmente complessa la lettura i dati che ol programma memorizza durante l'esecuzione. Paranoy_body e' un viewer di email grafico che visualizza gli allegati immagine, il body in html e in testuale. Di interesse circa il tema che ho esposto e' l'organigramma usato per scrivere quest'ultimo elemento. A differenza di _malloc che e' una libreria esterna scaricabile ed utilizzabile a parte, paranoy_body nasce come libreria interna e quindi appositamente scritta per il programma di lesion. Quello che lesion vede rispetto a quel codice e' soltando una serie di chiamate relativamente esemplificative come: /* Create and destroy */ GtkWidget * paranoy_body_new (void); void paranoy_body_destroy (GtkWidget *body); /* Realize */ void paranoy_body_realize (GtkWidget *body); gboolean paranoy_body_is_realized (GtkWidget *body); /* Text func */ void paranoy_body_set_text (GtkWidget *body, gchar *text); void paranoy_body_unset_text (GtkWidget *body); void paranoy_body_force_show_text (GtkWidget *body, gboolean show); gboolean paranoy_body_is_showed_text (GtkWidget *body); .... Senza sapere nulla di cio' che avviene all'interno del codice , ma limitandosi a fornire i dati da eleborare potremmo utilizzare comodamente le chiamate messe a disposizione dalla libreria. Non si tratta di una manovra in direzione del close software, tutt'altro: free software perche' esiste un rapporto di fiducia tra coders nell'utilizzare reciprocamente il codice. Immaginiamoci ora di cambiare l'interfaccia di paranoy_body. Nulla e' piu' semplice se si rispetta il protocollo. Se creo un oggetto, o modifico quello esistente, nulla cambiera' per paranoy se le chiamate che ho messo a disposizione agli altri sviluppatori del progetto rimangono uguali. Lo stesso vale per la liberia _malloc che e' stata pensata come libreria esterna poiche' compie funzioni molto piu' generiche e riutilizzabili rispetto ad un view di email. Nella speranza che questo esempio sia stato abbastanza chiaro, vi rimando all'homepage del progetto http://http://paranox.sourceforge.net/. Per quanto riguarda i moduli, l'esempio che vado a proporvi e' proprio xmms. Il suo codice e' particolarmente chiaro e penso possa essere terreno di studio per molti neo (ma non solo) programmatori. xmms ha una struttura a moduli per tutto cio' che riguarda l'input, l'output e la visualizzazione. Esistono quindi moduli per la decodifica di mp3, ogg, wav e altri formati, esattamente come esistono moduli per l'audio secondo oss, alsa, e i principali demoni del suono. Il programma gestisce l'interfaccia grafica, la playlist, e la gestione dei plugin che forniscono le principali funzionalita' ovvero: suonare brani o streaming audio. I moduli a differenza delle librerie non sono autosufficienti, usano funzioni messe a loro disposizione, per esempio da xmms. Attraverso una struttura simile a questa: typedef struct { void *handle; /* Filled in by xmms */ char *filename; /* Filled in by xmms */ char *description; /* The description that is shown in the preferences box */ void (*init) (void); /* Called when the plugin is loaded */ void (*about) (void); /* Show the about box */ void (*configure) (void); int (*is_our_file) (char *filename); /* Return 1 if the plugin can handle the file */ GList *(*scan_dir) (char *dirname); /* Look in Input/cdaudio/cdaudio.c to see how */ ... } InputPlugin; I moduli di input possono attivare funzioni che Xmms attivera' ventualmente al momento opportuno. La stessa modalita' e' usata da molti altri programmi, come ad esempio Apache. I moduli vengono visualizzati come file .so, esattamente come delle librerie esterne, Xmms li carica attravero la libdl capace di effettuare il linking di librerie runtime. Per analizzare le estensioni prendo come esempio apt, ma si potrebbe anche utilizzare Xfce4 o Gimp perche' hanno una dinamica praticamente identica. Invece di pensare i plugin come qualcosa da inglobare per aggiungere funzionalita' essenziali, apt ha inteligentemente sfruttato la separazione della parte di accesso ai pacchetti debian, in programmi distiniti e richiamati automaticamente in base alla necessita'. Mi spiego, apt dispone di piccoli programmi indipendenti: # ls /usr/lib/apt/methods/ bzip2 cdrom copy file ftp gzip http rsh ssh Questi software compiono ognuno un accesso ai pacchetti debian attraverso protocolli differenti. Http scarica un pacchetto richiesto da un repository web, ssh lo fa usando il protocollo ssh e cosi' per ognuno di questi software. Apt a seconda della propria configurazione li usera' per accedere al dato richiesto. Esiste uno standard di comunicazione tra apt e le estensioni presenti nell'albero dei suoi stessi sorgenti: /apt-0.5.28.1/doc/method.sgml in modo tale da poter aggiungere una eventuale estensione "napster" che accede alle rete opennap per scaricare i pacchetti richiesti. Tuttavia apt non compie soltanto l'installazione dei pacchetti, ma e' una serie di tool che copre quasi tutto l'aspetto di aggiornamento di debian, per questa ragione lo scaricamento del pacchetto e' solo una comodita' di download. Abbiamo quindi elencato le principali modalita' per rendere un'applicazione modulare e abbiamo anche analizzato i vantaggi rispetto ad un'applicazione monolitica. Purtroppo non posso offrivi la documentazione per capire bene come scegliere l'uno o l'altro metodo perche' non esistono testi ben fatti su quest'argomento. Leggere codice ben scritto e' spesso piu' utile che studiare su manuali ridondanti e magari troppo teorici. Ricapitolo qui i programmi che vi ho suggerito con alcune righe di descrizione: * xmms Approccio modulare. E' scritto in un buon C, chiaro e semplice. Il codice e' molto ben commentato. * apt C++, approccio ad estensioni non sotto forma di librerie, ma a programmi esterni. * libgtk C ben scritto. Libreria esterna molto versatile. E' a sua volta capace di accettare estensioni sotto librerie interne o esterne. Per qualsiasi chiarimento, commento o altro scrivetemi al mio indirizzo email. Sul mio sito internet potrete trovare alcuni miei codici come _malloc e la suite soma, mio ultimo progetto. Andrea Marchesini aka bakunin mailto://bakunin@autistici.org http://autistici.org/bakunin/