Bookmark and Share
Document Actions

Adattatori
medio

Questa sezione spiega il come e il perché si usano gli adattatori, tramite un esempio reale, compresi i particolari della configurazione e una dimostrazione del meccanismo di lookup degli adattatori in azione.

Il problema principale con le classi Zope 2 è che diventano veramente complesse col tempo (anche se le tecniche di ereditarietà contribuiscono a ridurre il numero di funzionalità in un classe). Il nuovo modo di pensare di Zope 3 dice: mantieni la tua classe molto piccola ed aggiungi funzionalità alla tua classe per mezzo di adattatori. Così aggiungere funzionalità significa aggiungere un adattatore; un adattatore aggiunge una funzionalità ad un classe di base. Tutte le implementazioni specifiche di una funzionalità saranno nascoste dalle interfacce nell'architettura a componenti di Zope 3.

Perciò ricordiamoci che abbiamo 3 cose importanti da tenere presenti:

  • Le interfacce che definiscono le funzionalità

  • Gli adattatori che implementano le funzionalità

  • L' oggetto che può essere adattato

Puoi avere vari adattatori che implementano una stessa interfaccia. L'interfaccia dell'adattatore descrive soltanto le funzionalità e deve essere il più generale possibile. Diamo ora un'occhiata alle interfacce che definiscono una funzionalità. Prendiamo la funzionalità del filtro. In un oggetto folderish (una cartella) vuoi elencare soltanto del contenuto specifico (per esempio: in un PhotoAlbum vuoi elencare solo PhotoAlbum e immagini). Questo avrà un'interfaccia molto semplice:

    >>> from zope.interface import Interface
    >>> class IFilterFolder(Interface):
    ...     """
    ...         Filtra il contenuti di un oggetto folderish
    ...     """
    ...     def listObjects():
    ...         """
    ...             restituisce la lista di oggetti filtrati contenuti in una cartella
    ...         """

Generiamo un adattatore filtro fittizio che implementa questa interfaccia e si limita a restituire tutti gli oggetti nella cartella:

    >>> from zope.interface import implements	
    >>> class FolderFilter(object):
    ...     """
    ...         Filtro di esempio che non filtra nulla
    ...     """
    ...     implements(IFilterFolder)
    ...
    ...     def __init__(self, context):
    ...         """
    ...            Inizializza il nostro adattatore
    ...         """
    ...         self.context = context
    ...
    ...     def listObjects(self):
    ...         """
    ...             restituisce la lista di oggetti contenuti nella cartella
    ...         """
    ...         return self.context.objectValues()

    Come puoi vedere un adattatore eredita dalla classe Object, il tipo più semplice che potete immaginare. Si noti che un adattatore semplice prende l'oggetto da adattare (contesto) all'inizializzazione. La convenzione è di chiamarlo Context.

Generiamo un altro adattatore per un PhotoAlbum. Questo adattatore filtrerà gli oggetti Image e i PhotoAlbum contenuti:

   >>> class PhotoAlbumFilter(object):
    ...     """
    ...         Filtro che restituisce solo oggetti Image e PhotoAlbum nella cartella
    ...     """
    ...     implements(IFilterFolder)
    ...
    ...     def __init__(self, context):
    ...         """
    ...            Inizializziamo il nostro adattatore
    ...         """
    ...         self.context = context
    ...
    ...     def listObjects(self):
    ...         """
    ...             restituisce la lista di oggetti Image e PhotoAlbum nella cartella
    ...         """
    ...         return [item for item in self.context.ObjectValues(['Image','Folder'])]

In questo modo abbiamo due adattatori per un'interfaccia. Dovremmo ora aggiungere un po' di collante per fare in modo che FolderFilter adatti un oggetto Folder, e che PhotoAlbumFilter adatti un oggetto PhotoAlbum. Faremo questo nuovamente con un file ZCML: definiremo un adattatore che implementa un'interfaccia (e quindi una funzionalità) per un certo tipo di oggetto che implementa un'altra interfaccia (quanto appena detto diventerà più chiaro con l'esempio,… ci auguriamo :)):

<configure
      xmlns="http://namespaces.zope.org/zope"
      xmlns:five="http://namespaces.zope.org/five"
      >

    <adapter
      for="Products.ATContentTypes.interface.IATFolder"
      provides="Products.ATContentTypes.interface.IFilterFolder"
      factory="Products.ATContentTypes.adapters.folder.FolderFilter"
      />

    <adapter
      for="Products.ATContentTypes.interface.IPhotoAlbum"
      provides="Products.ATContentTypes.interface.IFilterFolder"
      factory="Products.ATContentTypes.adapters.image.PhotoAlbumFilter"
      />

  </configure>

Così ora è facile da capire, definiamo due adattatori che adatteranno due tipi di contenuto differenti.

Primo IATFolder

Vogliamo un adattatore che adatti ogni oggetto che implementa l'interfaccia Products.ATContentTypes.interface.IATFolder.

Un oggetto ATFolder la implementa e può essere adattato dal nostro adattatore:

   for="Products.ATContentTypes.interface.IATFolder"

Qui diciamo che la funzionalità implementata dall'adattatore è descritta nell'interfaccia Products.ATContentTypes.interface.IFilterFolder:

   provides="Products.ATContentTypes.interface.IFilterFolder"

Qui diciamo che l'adattatore che implementa IFilterFolder è in “Products.ATContentTypes.adapters.folder.FolderFilter”:

   factory="Products.ATContentTypes.adapters.folder.FolderFilter"

Così, ricapitolando, questa direttiva significa che registreremo il nostro adattatore FolderFilter nel registro degli adattatori per qualsiasi oggetto che implementa IATFolder.

Secondo IPhotoAlbum

Vogliamo un adattatore che adatterà ogni oggetto che implementa l'interfaccia Products.ATContentTypes.interface.IPhotoAlbum. Adattiamo così fondamentalmente un “PhotoAlbum”:

 for="Products.ATContentTypes.interface.IPhotoAlbum"

Qui diciamo che la funzionalità implementata dall'adattatore è descritta in questa interfaccia Products.ATContentTypes.interface.IFilterFolder:

 provides="Products.ATContentTypes.interface.IFilterFolder"

Diciamo ora che l'adattatore che implementa IFilterFolder è in Products.ATContentTypes.adapters.image.PhotoAlbumFilter:

  factory="Products.ATContentTypes.adapters.image.Phot

Sintetizzata in questo modo, questa direttiva registrerà il nostro adattatore PhotoAlbumFilter nel registro degli adattatori per qualsiasi oggetto che implementi IPhotoAlbum.

Ricordati il modello generico per la direttiva dell'adattatore:

<adapter
     for="A"
     provides="B"
     implements="C"
     />

  *for* (per) qualsiasi oggetto che implementi l'interfaccia A
    noi *provide* (forniamo) una nuova funzionaalità definita dall'interfaccia B
      *implements* (implementazione) della funzionalità è definita dalla sua *factory* nella classe C.

Ci auguriamo che questo ora sia più chiaro!

Ed ora vediamo la magia del lookup dell'adattatore in azione. Come abbiamo detto prima, l'implementazione specifica di una funzionalità viene nascosta nell'architettura a componenti di Zope 3 dalle interfacce. Così, se desidero la lista filtrata degli oggetti di un qualsiasi contenuto folderish senza dovermi preoccupare se si tratta di un PhotoAlbum, una semplice cartella, una cartella Audio, mi limito ad adattare l'oggetto all'interfaccia. Invochiamola:

Se abbiamo una cartella “folder1”:

    >>> self.folder.invokeFactory('Folder', 'folder1')
    'folder1'
    >>> folder1 = self.folder.folder1

Adattiamo la cartella all'interfaccia di IfilterFolder:

    >>> from Products.ATContentTypes.interface import IFilterFolder
    >>> adapter = IFilterFolder(folder1)

E adesso otteniamo il FolderFilter:

   >>> adapter.__class__
    <class 'Products.ATContentTypes.adapters.folder.FolderFilter'>

Questo adattatore usa la nostra cartella folder1 come contesto:

    >>> adapter.context
    <ATFolder at ...>

Ora se abbiamo un PhotoAlbum folder1:

    >>> from Products.ATContentTypes.interface import IPhotoAlbum
    >>> from zope.interface import directlyProvides
    >>> directlyProvides(folder1, IPhotoAlbum)

In questo modo la cartella diventa in effetti un PhotoAlbum. Adattiamo il PhotoAlbum invocando l'interfaccia di IfilterFolder:

  >>> adapter = IFilterFolder(folder1)

Ed otteniamo il PhotoAlbumFilter

Qui potresti chiedere: “Perchè otteniamo un PhotoAlbumFilter e non un FolderFilter, dato che folder1 implementa sia IATFolder che IPhotoAlbum?”. La risposta è semplice: il lookup dell'adattatore trova la prima interfaccia che si associa ad un adattatore.

Quindi stai attento alla priorità nella dichiarazione delle interfacce e quando abbiamo eseguito directlyProvides (folder1, IPhotoAlbum), abbiamo dato priorità assoluta all'interfaccia IphotoAlbum rispetto a tutte le altre. Così il lookup dell'adattatore in primo luogo prende IPhotoAlbum, cerca se c' è un adattatore che si associa a questa interfaccia (cercando per l'attributo “for” nelle direttive zcml), se c' è, lo usa, se non c'è adattatore per questa interfaccia prende la successiva  (che dovrebbe essere IATFolder) ed effettua lo stesso lookup per l'adattatore che si abbina a questa:

    >>> adapter.__class__
    <class 'Products.ATContentTypes.adapters.image.PhotoAlbumFilter'>

Non preoccuparti per l'implementazione dell'adattatore per il tuo oggetto, il lookup  lo farà per te.

Ora puoi definire altri adattatori filtro per il tuo tipo di contenuto semplicemente implementando l'interfaccia IFilterFolder e registrando il tuo nuovo adattatore, come abbiamo fatto nel precedente esempio di ZCML. La successiva sezione entrerà nel dettaglio.

 

 
by Maurizio Delmonte last modified 2008-12-12 15:56
Contributors: Jean François Roche, Russ Ferriday, Maurizio Delmonte (traduzione)