Riesaminiamo il meccanismo che permette di aggiungere componenti grafici (ad es., pulsanti) ad un pannello Swing/AWT.
Consideriamo l’esempio seguente, che posiziona tre pulsanti in una finestra.
Il risultato è mostrato in figura.
// un contenitore
Container c1 = new Container();
c1.setLayout(new FlowLayout());
// aggiungo due pulsanti al contenitore
c1.add(new Jbutton("qui"));
c1.add(new Jbutton("qua"));
// la finestra
JFrame f = new JFrame();
Container c2 = f.getContentPane();
c2.setLayout(new FlowLayout());
c2.add(c1); // c1 si comporta come un componente!
c2.add(new JButton("quo"));
Cominciamo col creare un contenitore c1, nel quale inseriamo i due pulsanti “qui” e “qua”, il FlowLayout ci assicura che, finché c’è abbastanza spazio, i due pulsanti saranno posizionati uno di fianco all’altro.
Poi, creiamo una finestra e otteniamo un riferimento c2 al suo pannello del contenuto (contentPane), come c1, anche il pannello del contenuto è di tipo Container.
Per ottenere la disposizione voluta, aggiungiamo il contenitore c1 al contenitore c2, per poi aggiungere a quest’ultimo anche il pulsante “quo”
Questo meccanismo corrisponde al pattern COMPOSITE.
Contesto:
Soluzione:
Il diagramma a destra illustra le principali classi coinvolte nel pattern COMPOSITE, e le relazioni tra di loro.
Primitive è l’interfaccia che rappresenta un oggetto primitivo.
Sia i veri oggetti primitivi (qui chiamati Leaf, “foglia”) sia gli oggetti compositi implementano l’interfaccia Primitive, non è detto che Primitive sia sempre un’interfaccia, come nel caso di Component, illustrato nella prossima slide.
La relazione di aggregazione tra Composite e Primitive indica che un oggetto di tipo Composite contiene un insieme di riferimenti ad oggetti primitivi.
Il diagramma a destra illustra come il pattern COMPOSITE è stato applicato nel caso dei contenitori e dei componenti grafici Swing/AWT.
Il ruolo dell’interfaccia Primitive è ricoperto dalla classe Component:
getPreferredSize è uno dei tanti metodi di quella classe, restituisce le dimensioni “ideali” di questo componente.
Come già detto, sia JButton che Container estendono Component.
Il metodo add di Container permette di aggiungere un altro Component all’insieme.
In accordo col pattern, quando un contenitore deve rispondere ad una chiamata a getPreferredSize, esso chiamerà il metodo omonimo su tutti i componenti contenuti, e poi sommerà le dimensioni ottenute, sulla base del suo layout, per restituire le sue dimensioni ideali.
Per introdurre un nuovo pattern, esaminiamo il meccanismo per aggiungere delle barre di scorrimento ad un componente Swing/AWT;
ad esempio, ad un campo di testo di più righi
/* un’area di testo con 10 righe e 25 colonne */
Component area = new JTextArea(10, 25);
/* aggiungiamo le barre di scorrimento */
Component scrollArea = new JScrollPane(area);
Notiamo che si deve creare un nuovo oggetto, di tipo effettivo JScrollPane, a partire dall’oggetto JTextArea esistente.
Inoltre, l’oggetto così creato è a sua volta sottotipo di Component.
Questo meccanismo viene codificato dal pattern DECORATOR, presentato nella prossima slide.
Contesto:
Soluzione:
Il diagramma a destra illustra le principali classi coinvolte nel pattern DECORATOR, e le relazioni tra di loro.
L’interfaccia Component rappresenta un componente generico.
Sia i veri componenti base (ConcreteComponent) che le loro decorazioni (Decorator) implementano l’interfaccia, in modo che il client li possa usare nello stesso modo.
La relazione di aggregazione indica che un oggetto decoratore contiene un riferimento al componente che sta decorando, presumibilmente, tale riferimento viene inizializzato tramite un costruttore o metodo di Decorator.
Il diagramma a destra illustra come il pattern DECORATOR è stato applicato nella libreria Swing/AWT.
Sia il campo di testo (JTextArea) che le barre di scorrimento (JScrollPane) estendono la classe Component.
Il costruttore di JScrollPane prende come argomento il componente a cui applicare la decorazione.
Quando un client chiama paint (il metodo che chiede ad un componente di disegnare il suo contenuto) di un oggetto JScrollPane, quest’ultimo chiama il metodo omonimo dell’oggetto decorato e poi aggiunge il disegno delle barre di scorrimento.
Confrontando le descrizioni e i diagrammi dei pattern COMPOSITE e DECORATOR, notiamo diverse somiglianze.
Entrambi prevedono una distinzione tra oggetti di base (chiamati primitivi in un caso e componenti nell’altro) e oggetti compositi.
Entrambi prevedono che gli oggetti delle due categorie implementino un’interfaccia comune.
Tuttavia, i contesti di applicazione sono molto diversi.
COMPOSITE si applica quando bisogna aggregare più oggetti di base in un unico oggetto, che può a sua volta essere aggregato con altri; si ottengono così gerarchie di oggetti disposti in un albero.
DECORATOR si applica quando bisogna aggiungere funzionalità, illimitate a priori, ad una o più classi.
4. Risoluzione dell'overloading e dell'overriding
5. Controllo di uguaglianza tra oggetti
6. Classi interne, locali ed anonime
7. Iteratori, teoria e pratica
8. Clonazione di oggetti. Confronto tra oggetti.
9. Elementi di programmazione di interfacce grafiche
10. Il paradigma Model-View-Controller. Il pattern Strategy
11. I pattern Composite e Decorator
12. I pattern Template Method e Factory Method
13. Classi e metodi parametrici
14. La libreria Java Collection Framework: le interfacce Iterable, ...
15. La libreria Java Collection Framework: la classe HashSet e le l...
16. Parametri di tipo con limiti
17. L'implementazione della programmazione generica: la cancellazio...
18. La riflessione
19. Introduzione al multi-threading
22. Classi enumerate