Zope 3, questo sconosciuto
Note: Return to reference manual view.
1. Genesi di Zope 3
Lo Zope noto, con una storia di oltre dieci anni alle spalle e milioni di installazioni all'attivo, e' Zope 2, famoso, per alcuni "famigerato", per la sua predilezione per gli oggetti e per la sua versatilita'.
Purtroppo, una delle pecche maggiormente additate dai suoi detrattori e' la ripida curva di apprendimento che Zope richiede in cambio della potenza che mette a disposizione degli sviluppatori, difficolta' motivata tra l'altro dalla complessa gerarchia di oggetti che ne implementano i servizi e le funzionalita'.
Pur apprezzandone tutti i pregi, anche i fautori di Zope ne comprendono i limiti che hanno accompagnato le scelte tecniche operate durante la progettazione del sistema, per questo, forti di tale esperienza, dagli inizi del 2001 si e' intrapreso lo sviluppo di Zope 3: non una semplice riedizione del blasonato codice di Zope 2, bensi' una sua completa riscrittura da zero.
Genesi di Zope 3
Jim Fulton e' il principale ispiratore delle tecnologie alla base di Zope, di cui viene ritenuto il papa da tutta la comunita' sin dal 1998, anno del primo rilascio con licenza aperta.
Dopo vari anni di esperienza sul campo, durante i quali Zope e' stato apprezzato e adottato da moltissime aziende e sviluppatori, finalmente si viene a generare una comunita' stabile di "utenti", che portano casi d'uso sempre piu' variegati e loro possibili soluzioni, le quali a volte hanno bisogno di modificare il codice dell'application server stesso.
Tale esigenza di flessibilita' viene facilmente gestita da Jim e dai suoi ingegneri in Digital Creation, l'azienda che ha creato e rilasciato Zope, quando si tratta di effettuare modifiche per i propri clienti, o quando si desidera incorporare patch nel sorgente di Zope. La facilita' di gestione e' legata al fatto che i casi d'uso primari di Digital Creation tendono a prevalere su quelli proposti dalla comunita', inoltre i tecnici sono molto specializzati e a proprio agio con la gerarchia di oggetti che implementa Zope. Tuttavia ci si rende conto che il modo di fare di Zope 2 in certi casi risulta eccessivamente rigido e quindi da rivedere.
Per questo, ma non solo, Jim impone in Digital Creation la sua idea di rinnovare il codice sorgente dell'application server, con l'obiettivo di migliorarne la flessibilita', mirando anche, se possibile, a renderne piu' semplice l'utilizzo agli sviluppatori.
Da quel momento cerca consenso nella comunita' in via di formazione, e lo ottiene dopo aver esposto quelli che tuttora sono i cardini dell'architettura di Zope 3. Digital Creation si chiama fuori dall'onere di sviluppare in toto il nuovo Zope, come ancora avviene per il suo predecessore, ma lascia che Jim capitaneggi la comunita', che dal 2001 investe energie nel nuovo framework, perseguendo i validissimi risultati che stiamo per vedere insieme.
Zope 3 puo' essere considerato un framework di sviluppo orientato al web scritto in python, ottenuto dai suoi sviluppatori a partire da tutta la loro esperienza con Zope 2 e con gli altri framework, basati su Python e non, che nel frattempo si sono diffusi.
Zope 3 eredita elementi fondamentali da Zope 2, come ZODB, il database a oggetti che rende la persistenza degli oggetti un gioco da ragazzi, e ne introduce altri altrettanto fondamentali e fondanti, quali il Component Framework, l'infrastruttura di gestione orientata ai componenti.
In sintesi, Z3 nasce con tutta la robustezza derivante dalle fondamenta di Zope 2, e con tutta la flessibilita' mancante nel suo predecessore e garantita dalla nuova architettura a componenti.
2. Zope 3 In Nuce
- zope.publisher
- il pubblicatore di oggetti, il cuore di Zope, ed e' compatibile WSGI
- zope.server
- il web server nativo e abilitato WSGI integrato in Zope 3
- ZODB
- il database a oggetti in grado di gestire la persistenza degli oggetti immagazzinati in modo trasparente
- ZEO
- il sistema di load balancing che permette di ridondare i servizi di pubblicazione senza richiedere modifiche alle applicazioni
- zope.component
- l'architettura a Componenti che rivoluziona il modo di sviluppare le applicazioni proprio di Zope 2
- zope.configuration
- mette a disposizione un linguaggio di tipo XML per la registrazione e configurazione dei componenti
- zope.security
- implementa la flessibile architettura di sicurezza, rendendo facilmente estensibili le policy predefinite
- zope.testing, zope.testbrowser
- mettono a disposizione dello sviluppatore ottimi ambienti per lo sviluppo di unit test, test di integrazione e test funzionali
- zope.pagetemplate
- fornisce il linguaggio per la gestione dei template, ed e' XHTML compliant
- zope.formlib
- il motore di gestione degli schemi e della generazione automatica dei form corrispondenti
Molti altri moduli sono disponibili e possono essere utilizzati in base alle esigenze delle singole applicazioni.
3. Componenti
Una delle differenze fondamentali tra i due "gusti" di Zope e' data dal fatto che, mentre in Zope 2 molte funzionalita' sono ottenute utilizzando i meccanismi di ereditarieta' multipla sugli oggetti python, Zope 3 e' stato sviluppato nella prospettiva dei Componenti.
Ma cosa e' un componente?
Vediamo prima di capire come si arriva ad averne bisogno!
Gli oggetti mediante i quali le applicazioni Zope 2 sono costruite tendono a implementare le proprie funzionalita' ereditando da molte mix-in class, ciascuna deputata a dotare l'oggetto di una specifica caratteristica. Questo comporta una effettiva facilita' alla specializzazione e al riuso del codice, ma anche una non trascurabile complessita delle classi che definiscono le applicazioni, una dipendenza molto rigida tra i vari blocchetti che contribuiscono al risultato finale, e una derivazione non sempre chiara del pezzo di codice responsabile di una specifica implementazione.

Per scardinare le controindicazioni dei meccanismi di ereditarieta' senza perderne le prerogative di flessibilita' e potenza e' stata introdotta l'Architettura a Componenti, che consente di distribuire la complessita' sui vari componenti che lo sviluppatore predispone, favorendo la realizzazione di singole classi piu' semplici e piu' facili da mantenere nel tempo, e rendendo piu' lineare aggiungere e modificare funzionalita' senza manipolare classi esistenti.
Il superamento del problema della dipendenza stretta che scaturisce dai meccanismi di ereditarieta' tra classi viene ottenuto introducendo il concetto di Interfaccia.
Cercando di semplificare, dal punto di vista di chi la utilizza una classe ha un comportamento che puo' essere descritto in prima battuta elencandone gli attributi e i metodi pubblici, e per ciascuno descrivendone il significato semantico. Per questo, una classe tende a implementare una o piu' "interfacce" di funzionamento che ne definiscono il comportamento.
Tale processo descrittivo consente di "sterilizzare" il contratto di funzionamento che ci si aspetta dalla classe rispetto alla effettiva implementazione di tale comportamento.
In sostanza, una classe dichiara di implementare una specifica interfaccia, ma senza necessariamente preoccuparsi di fornire l'effettiva implementazione di tale interfaccia: questo favorisce l'introduzione di Adattatori che possono fornire implementazioni alternative dell'interfaccia specifica in base al contesto.

Per fare un esempio, possiamo immaginare di disporre di una classe che dichiara di essere in grado di spedire una notifica ad una lista di iscritti. L'implementazione fornita a corredo si preoccupa di inviare tale notifica alla lista con una mail per ogni iscritto.
Ammettiamo ora di avere uno specifico contesto in cui l'implementazione richiesta consiste nell'invio di un SMS al posto della mail. In Zope 2 si deve direttamente modificare la classe responsabile della notifica (perdendo il comportamento originale o facendo in modo di saper discriminare tra i due), oppure la strada dell'ereditarieta' ci suggerisce di costruire una classe derivata da quella di partenza e di ridefinire il metodo responsabile della notifica, con grosso dispendio di energia per continuare a far funzionare la nostra classe modificata nel suo contesto operativo.
Con l'approccio a componenti invece tutto quel che dobbiamo fare e' descrivere la nostra implementazione mediante un adattatore, che sostituira' l'implementazione originale grazie al framework a componenti adeguatamente istruito da un file di configurazione che si limita a descrivere chi deve implementare la specifica interfaccia nello specifico contesto.

In sintesi, ammettiamo di avere una richiesta di attivazione di un metodo di interfaccia su un oggetto che dichiara tale interfaccia, il Component Framework individua il codice che implementa il metodo richiesto secondo le configurazioni che accoppiano l'interfaccia alle implementazioni disponibili, quindi lo esegue.
Cio' implica che modificare un certo comportamento di uno specifico oggetto e' semplice quanto produrre un file di configurazione.
L'architettura a componenti di Zope 3 fornisce quindi un meccanismo che rende relativamente semplice assemblare oggetti tra loro: gli oggetti sono connessi in base alle interfacce che dichiarano, questo porta al concetto di componente secondo Zope 3.
Un Componente Z3 e' un oggetto con delle interfacce pubbliche dichiarate, implementate mediante una o piu' classi python.
Inoltre i componenti sono oggetti disaccoppiati che facilmente possono essere connessi tra loro.
Una nota conclusiva e' d'obbligo! L'architettura a componenti e' un servizio che si affianca al normale meccanismo di ereditarieta' che chiaramente resta funzionante, in quanto proprio di Python, e in molti casi ancora indispensabile.
Biblio
Una trattazione teorica piuttosto chiara e generica dello sviluppo basato su componenti si trova nel numero 4 del 2003 della rivista Upgrade
4. Interfacce
Piu' in dettaglio, un'interfaccia in Zope 3 e' a sua volta un "oggetto vuoto" che descrive e documenta il comportamento "esterno" dei componenti, gli oggetti che dichiarano di implementarlo, mediante l'uso di documentazione informale (doc string python), definizione di attributi, metodi e invarianti, cioe' condizioni che devono essere valide per le classi python che implementano l'interfaccia specifica.
L'uso delle interfacce offre diverse prerogative:
- specificando il comportamento degli oggetti rappresentano un contratto semantico a cui un componente si deve attenere
- tramite le descrizioni e i commenti informali offrono la necessaria documentazione sul comportamento atteso dei componenti
- classificano gli oggetti in base al comportamento dichiarato, e non in base alla propria classe di appartenenza: oggetti con la stessa interfaccia possono essere usati allo stesso modo
- permettono di stabilire se i vari oggetti hanno interfacce compatibili, ad esempio richiedendo che un oggetto che vuole usare un determinato servizio implementi una specifica interfaccia
Facendo riferimento all'esempio del componente per la notifica alla lista di iscritti, l'interfaccia per la funzione di notifica potrebbe essere definita in questo modo:
from zope.interface import Interface
class INotificabile(Interface):
def notifica():
"""Invia la notifica agli iscritti
"""
Disponendo delle due implementazioni alternative per inviare la notifica via mail o via SMS, chiunque richiami il metodo "Notifica" sull'oggetto vedra' la notifica funzionare correttamente, anche se un adattatore ne ha modificato l'implementazione originale.
L'architettura a componenti utilizza le interfacce principalmente per classificare gli oggetti e per connetterli fra loro: Zope 3 non classifica mai i componenti in base alla loro classe, in quanto si vuole rendere semplice l'aggiunta di implementazioni alternative.
Per quanto visto, l'approccio basato su componenti offre dei vantaggi significativi rispetto a quello basato su ereditarieta', tuttavia non si pone come unica strada.
L'ereditarieta' continua a funzionare, e Zope 3 non impedisce di usarla nei modi che gli sviluppatori ritengono opportuni!
5. Tipologie di componenti principali
Componenti Contenuto
Zope e' essenzialmente un pubblicatore di oggetti "web", tra i piu' importanti troviamo per questo i componenti contenuto, specializzati nel gestire documenti, immagini, file, etc.
Normalmente l'interfaccia di tali componenti ne specifica lo schema di attributi desiderato, e non contiene metodi che specializzano il componente in funzionalita' specifiche dell'applicazione, ne tanto meno interfacce utente quali pagine html.
Componenti Factory
I componenti contenuti vanno generati nella nostra applicazione, e per fare questo in Zope 3 ci si avvale di componenti factory, specializzati nel generare istanze di componenti contenuto.
Componenti Vista
Oltre alla creazione di componenti contenuto, Zope 3 deve preoccuparsi di fornire accesso a tali contenuti, e lo fa mediante componenti di presentazione (viste).
Le viste offrono l'interfaccia utente verso altri componenti: in ambito Zope un esempio di componenti di presentazione e' dato dagli Zope Page Template (ZPT), i quali dovrebbero contenere solo logiche di presentazione e non di applicazione.
Componenti Adattatori
Nel momento in cui si ha bisogno di aggiungere delle logiche applicative agli ZPT, il modo migliore per farlo consiste nel definire dei componenti adattatori, che implementano le logiche specifiche senza andare ad intaccare i componenti contenuto o presentazione, che devono restare quanto piu' possibile "neutri" per poter essere facilmente riusabili in contesti diversi da quello originale.
Oltre a questi quattro tipi principali, esistono altri due tipi di componenti molto usati: utility e servizi.
Utility
Le utility sono componenti pensate per erogare una specifica funzionalita', senza agire su altri componenti. Sono l'equivalente delle funzioni python, se raffrontate ai metodi delle classi python: le utility sono componenti che non hanno bisogno di altri oggetti per poter svolgere la loro funzione, gli adattatori invece, come i metodi python, hanno bisogno di un altro componente su cui lavorare.
Un traduttore di HTML in testo semplice potrebbe essere implementato con una utility.
Servizi
I componenti servizio sono infine i componenti che permettono all'infrastruttura di Zope di funzionare, ad esempio sono servizi quelli che permettono di gestire i componenti stessi, o di accedere allo ZODB, o di gestire la sicurezza. Rappresentano il kernel di Zope 3.