Interfacce
medio
Questa sezione rende esplicita la differenza fra la interfacce di Zope 2 e Zope 3, e mostra la differenza fra le interfacce implementate nel file configure.zcml ed all'interno della classe. È il nostro primo sguardo al file di configurazione ZCML, quindi approfondiamo un po' l'argomento.
Interfacce
Le interfacce sono realmente utili per descrivere che cosa fa una classe. Le interfacce di Zope 2 e di Zope 3 entrambe documentano il “Cosa” senza parlare del “Come”, ma Zope 3 si spinge più avanti, usando le interfacce come componenti per adattare una classe, specializzando così il suo comportamento.
Zope2
Nel mondo di Zope 2, le interfacce erano intese solo a documentare la classe e dare una descrizione di metodi e attributi, in modo da poter dare un rapido sguardo ai diversi metodi di un classe e alla loro documentazione senza preoccuparsi dell'implementazione.
Le interfacce Zope 2 ereditano da questi pacchetti:
>>> from Interface import Interface
>>> from Interface import Attribute Perciò un'interfaccia è solo un classe con dei metodi, senza codice ed attributi, che eredita da Interface.
Per convenzione, un'interfaccia comincia sempre con la lettera I. Ecco un semplice esempio:
>>> class ITreeWalker (Interface):
... """
... Visita un albero
... """
...
... my_attribute = Attribute("""Un finto attributo solo come esempio""")
...
... def walk(context, path=''):
... """
... visita l'intero albero.
... """ Da notare che non utilizziamo il self nel metodo walk in un'interfaccia, poichè ci limitiamo a descrivere i metodi della classe. Nota inoltre come viene definito un attributo in un'interfaccia.
Diciamo che un classe implementa un'interfaccia non appena dispone degli stessi metodi ed attributi dell'interfaccia. Una classe può avere più metodi della relativa interfaccia ma non di meno! Non c'è problema se una classe implementa più di un'interfaccia.
Generiamo la classe che implementa l'interfaccia precedente in stile Zope 2:
>>> class SimpleTreeWalker:
... """
... Una classe molto sepplice che visiterà una gerarchia
... """
...
... __implements__ = (ITreeWalker)
...
... my_attribute = None
...
... def walk(self, context, path=''):
... """
... visita l'intero albero
... """
... pass Guarda la variabile __implements__ , in Zope 2 quello è il modo di dire che questa classe implementa le relative interfacce.
Da notare che Zope/Python non protesteranno se non implementerete correttamente tali interfacce (usando i metodi necessari, con i giusti parametri, etc.)
Perciò generare un test su questo è molto utile:
>>> ITreeWalker.isImplementedByInstancesOf(SimpleTreeWalker)
1
>>> from Interface.Verify import verifyClass
>>> verifyClass(ITreeWalker, SimpleTreeWalker)
1 Possiamo anche testare un'istanza della classe SimpleTreeWalker:
>>> simpleTreeWalkerObject = SimpleTreeWalker()
>>> ITreeWalker.isImplementedBy(simpleTreeWalkerObject)
1
>>> from Interface.Verify import verifyObject
>>> verifyObject(ITreeWalker, simpleTreeWalkerObject)
1 Così siamo felici di avere un'interfaccia e un classe che la implementa correttamente.
Zope3
Creare un'interfaccia in Zope 3 non è differente da Zope 2, cambiano solo le classi da cui si eredita.
Le interfacce di Zope 3 ereditano le informazioni da un pacchetto diverso:
>>> from zope.interface import Interface
>>> from zope.interface import Attribute Ora fissate bene in mente che tutte le cose che vengono da zope.XXX sono pronte per Zope 3.
Quindi possiamo usare la stessa interfaccia di prima:
>>> class ITreeWalker (Interface):
... """
... Visita un albero
... """
...
... my_attribute = Attribute("""Un semplice attributo di esempio""")
...
... def walk(context, path=''):
... """
... visita l'intero albero.
... """ Nello stile di Zope 3 abbiamo due diversi modi di definire che una classe implementa un'interfaccia:
Il primo modo può essere fatto dalla classe stessa in stile Z2.
Il secondo modo sarà molto differente. Non lo facciamo dalla classe come abbiamo fatto in Z2, ma lo facciamo dal file ZCML.
Il secondo metodo potrebbe essere più difficile del primo, benchè sia un buon modo di indicare il collegamento fra il cosa e il come fuori dal come stesso.
Dalla classe
Questo è il modo semplice per dichiarare che un classe implementa un'interfaccia Zope 3:
>>> from zope.interface import implements
>>> class SimpleTreeWalker:
... """
... Una semplice classe che visita una gerarchia
... """
...
... implements(ITreeWalker)
...
... my_attribute = None
...
... def walk(self, context, path=''):
... """
... visita l'intero albero
... """
... pass E questo è tutto, non dovrai più andare al tuo configure.zcml. La tua classe implementa l'interfaccia Zope 3.
Dal file ZCML
Digressione su ZCML
Dobbiamo parlare dello ZCML di Zope 3 . Zope 3 viene rilasciato con un nuovo meccanismo di configurazione e collegamento di componenti, usando XML. Questa è considerata come buona cosa da qualcuno e come nuova dolorosa cosa da altri, ad ogni modo non siamo qui per protestare, limitiamoci a spiegare.
Questo è un semplice esempio di configurazione mediante file ZCML:
<configure xmlns="http://namespaces.zope.org/zope"
xmlns:five="http://namespaces.zope.org/five">
</configure> xmlns definisce i diversi namespaces. (Definizione di W3C: “Un namespace XML è una collezione di nomi, identificati da un riferimento URI…, usati nei documenti XML come tipi di elemento e nomi di attributo”). Qui dichiariamo il namespace Zope 3 per la configurazione zcml di base ed il namespace Five per potere inserire il nostro codice Zope 2 nell'architettura a componenti di Zope 3.
Questo zcml risiede in configure.zcml ed è analizzato all'avvio di Zope da Five (in Zope 2).
Ora che possiamo giocare con ZCML torniamo alla nostra implementazione di interfaccia. Generiamo la classe che implementa l'interfaccia:
>>> class SimpleTreeWalker:
... """
... Una semplice classe che visiterà una gerarchia
... """
...
... my_attribute = None
...
... def walk(self, context, path=''):
... """
... visita l'intero albero
... """
... pass Da notare che questa classe è come la classe Z2 salvo che noi non parliamo più di __implements__! Il nuovo modo di dire che questa classe implementa l'interfaccia ITreeWalker è:
<configure xmlns="http://namespaces.zope.org/zope"
xmlns:five="http://namespaces.zope.org/five">
<five:implements
class="Products.ATContentTypes.adapters.treeWalker.TreeWalker"
interface="Products.ATContentTypes.interface.treeWalker.ITreeWalker"
/>
</configure> Adesso TreeWalker implementa l'interfaccia ItreeWalker. Nuovamente Zope/Python non protesteranno se non implementi correttamente la tua interfaccia, perciò un test è il benvenuto:
Testando dalla classe di SimpleTreeWalker:
Il test di implementazione dell'interfaccia Zope2 era:
ItreeWalker.isImplementedByInstanceOf(SimpleTreeWalker)
Il test di implementazione dell'interfaccia Zope3 è:
ITreeWalker.implementedBy(SimpleTreeWalker)
Testando dall'oggetto di SimpleTreeWalker (simpleTreeWalkerObject):
Il test di implementazione dell'interfaccia Zope2 era:
ItreeWalker.isImplementedBy()
Il test di implementazione dell'interfaccia Zope3 è:
ItreeWalker.providedBy(simpleTreeWalkerObject)
Uso:
>>> ITreeWalker.implementedBy(SimpleTreeWalker)
True
>>> from zope.interface.verify import verifyClass
>>> verifyClass(ITreeWalker, SimpleTreeWalker)
True Effettuiamo il test da un'istanza della classe SimpleTreeWalker:
>> simpleTreeWalkerObject = SimpleTreeWalker()
>> ITreeWalker.providedBy(simpleTreeWalkerObject)
True
>> from zope.interface.verify import verifyObject
>> verifyObject(ITreeWalker, simpleTreeWalkerObject)
True Siamo di nuovo felici, la nostra classe implementa un'interfaccia Zope 3!
La direttiva include di ZCML
Se devi definire molte direttive zcml all'interno del tuo configure.zcml, una buona pratica è di scindere il file usando la direttiva <include file="myfile.zcml"/>.
Esempio:
<configure xmlns="http://namespaces.zope.org/zope"
xmlns:five="http://namespaces.zope.org/five">
<include file="implements.zcml"/>
</configure> Posizione dei file delle Interfacce Zope2 e Zope3
Nel prodotto ATCT, le interfacce Zope 2 sono definite nel file interfaces.py. Tutte le interfacce Zope 3 sono nella cartella interface. Non c'è ancora una convenzione definitiva su dove mettere le interfacce (anche in Zope 3).
Zope 3 ti forzerà a scrivere le interfacce (che sono una buona pratica) poichè esse non avranno solo funzione di documentazione, sono anche il punto di partenza per ottenere l'adattatore.
