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/