Behavioral Patterns – Michele Schimd

您所在的位置:网站首页 tpsit Behavioral Patterns – Michele Schimd

Behavioral Patterns – Michele Schimd

#Behavioral Patterns – Michele Schimd| 来源: 网络整理| 查看: 265

Iterator

L’iterator pattern prevede che si realizzi un meccanismo per accedere, in modo sequenziale, agli elementi di una collezione, senza che i dettagli circ la memorizzazione di tali elementi siano noti (o debbano servire per l’accesso).

Ad oggi un meccanismo basato su iterator 猫 presente in pressoch茅 tutti i linguaggi, spesso realizzati da funzioni o classi di libreria. Prima di passare all’implementazione di un iterator, vediamo come utilizzarne uno gi脿 presente nella libreria Java.

In Java, Iterator 猫 un’interfaccia con i seguenti metodi (sono presenti anche altri metodi opzionali non discussi qui).

public interface Iterator { boolean hasNext(); E next(); }

La notazione Iterator indica che E 猫 una qualche classe da specificare nel momento di istanziazione dell’oggetto. Ad esempio Iterator 猫 un Iterator su oggetti di tipo String, si noti come il metodo next() restituisce E, nel nostro esempio il metodo restituirebbe String.

L’utilizzo di tale interfaccia 猫 semplice

public class Main { public static void main(String[] args) { ArrayList names = new ArrayList(); names.add("Alice"); names.add("Bob"); names.add("Carol"); names.add("David"); Iterator it = names.iterator(); while(it.hasNext()) { System.out.println(it.next()); } } }

Il codice sopra utilizza il metodo iterator della classe ArrayList per ottenere un’iterator sugli elementi memorizzati nella lista. In Java un oggetto pu貌 implementare la classe Iterable contiene il seguente metodo

Iterator iterator();

una volta che una classe implementa talle interfaccia, si pu貌 usare il foreach di Java, in questo modo il codice sopra pu貌 essere modificato nel seguente

public class Main { public static void main(String[] args) { ArrayList names = new ArrayList(); names.add("Alice"); names.add("Bob"); names.add("Carol"); names.add("David"); for(String s : names) { System.out.println(s); } } }

Il risultato 猫 lo stesso, ma ora non 猫 necessario utilizzare esplicitamente una variabile di tipo iterator, il costrutto for(... : ...) presente in Java si occupa di tutto.

Osserva

Nell’esempio sopra si 猫 utilizzata la classe ArrayList di Java, ma questo non rappresenta un vincolo, una qualsiasi classe che implementi l’interfaccia Iterable si utilizza allo stesso modo. In questo modo si riesce a disaccopiare i dettagli di memorizzazione degli elementi dalla lora scansione sequenziale, questo 猫 uno dei pi霉 grandi vantaggi dell’iterator pattern.

Creazione di un iterator in Java

Le interfacce Iterator e Iterable possono essere anche utilizzate per creare delle classi iterabili mediante utilizzo di un iterator custom. Il seguente codice mostra un esempio di una classe Stack che implementa l’interfaccia Iterable (definendo Object come contenuto della classe anzich茅 il generic E) e che utilizza una classe interna StackIterator la quale implementa l’interfaccia Iterator.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 import java.util.Iterator; public class Stack implements Iterable { public class StackIterator implements Iterator { private Stack stack; private int i; public StackIterator(Stack stack) { this.stack = stack; i = stack.top; } @Override public boolean hasNext() { return i>0; } @Override public Object next() { return (i>0) ? stack.content[--i] : null; } } private static int ARRAY_SIZE = 65536; private Object[] content; private int top; public Stack() { content = new Object[ARRAY_SIZE]; top = 0; } public void push(Object o) { content[top++] = o; } public Object pop() { return content[top--]; } public int size() { return top; } public boolean isEmpty() { return (size() == 0); } public Iterator iterator() { return new StackIterator(this); } public static void main(String[] args) { Stack stack = new Stack(); stack.push("Alice"); stack.push("Bob"); stack.push("Carol"); stack.push("David"); for (Object object : stack) { System.out.println(object); } } }

L’implementazione di StackIterator si limita a memorizzare un riferimento allo Stack su cui iterare ed un indice i con cui tenere traccia del punti a cui l’iterator 猫 arrivato.

Importante

Il codice sopra 猫 stato creato a scopo didattico, normalmente non 猫 possibile iterare su uno stack senza svuotarlo, in questo caso per scopi puramente didattici 猫 stato creato un iterator che passa in rassegna il contenuto dello stack senza eseguire nessuna operazione push.

Attenzione

La classe StackIterator mostrata sopra accede direttamente all’array content che 猫 un campo privato della classe Stack. Questo modo di accedere agli elementi, pur essendo efficace (e molto usato) presenta un problema, cosa succede se durante l’utilizzo di uno StackIterator l’oggetto Stack a cui questo si riferisce viene modificato (ad esempio mediante un pop)? In questo caso non 猫 pi霉 possibile garantire il corretto comportamento dell’iterator, ci sono due modi per affrontare questo aspetto:

ignorare questo problema nel qual caso l’utilizzo di un iterator dopo aver modificato la struttura a cui fa riferimento porta ad un comportamento imprevedibile;invalidare l’iterator esplicitamente in modo da “impedire” (segnalando, ad esempio con un eccezione, che l’iterator 猫 in uno stato INVALID).

Spesso si sceglie di ignorare il problema in quanto l’implementazione del meccanismo di “invalidazione” di un iterator risulta troppo complesso. In generale 猫 buona norma non modificare una struttura mentre si itera su quella oppure smettere di iterare quando si esegue una modifica.

Observer

Il pattern observer definisce una dipendenza uno-a-molti tra un oggetto che cambia proprio stato ed altri oggetti che reagiscono automaticamente a tale cambiamento.

Il pattern prevede che vi siano due parti di codice (ad esempio due classi):

un subject il quale 猫 il “soggetto” del cambiamento di stato eduno o pi霉 observer i quali “osservano” il cambiamento di stato del subject reagendo a tali cambiamenti, se necessario.

Una classica applicazione del pattern observer 猫 nelle interfacce grafiche (GUI) dove gli elementi visualizzati a schermo dipendono dallo stato interno (ad esempio di una classe) al cambiare del quale, anche gli elementi devono cambiare e devono, quindi, essere ridisegnati (re-rendered).

Realizzazione in Java dell’observer pattern

Il linguaggio Java offriva, fino alla versione 8, la classe Observable e l’interfaccia Observer per l’implementazione, rispettivamente, del subject e dell’observer, dalla versione 9 queste sono state deprecate perch茅 considerate poco flessibili.

Vediamo una semplice implementazione Java del pattern observer utilizzando una classe per il subject ed una classe per l’observer, ognuna delle classi implementer脿 un’interfaccia apposita. Per rendere la descrizione pi霉 concreta, affronteremo il problema utilizzando un esempio.

Consideriamo la lista delle notifiche di un sistema, ad esempio di uno smartphone, ogni qualvolta un’applicazione inserisce una nuova notifica, il sistema deve reagire inserendo un nuovo elemento grafico all’interfaccia di visualizzazione delle notifiche. In questo caso il subject 猫 una classe NotificationList che mantiene uno stato interno (ad esempio una lista di notifiche) e l’observer e la classe NotificationManager del sistema operativo.

Le due interfacce per subject e observer sono le seguenti.

public interface ISubject { public void addObserver(IObserver observer); public void removeObserver(IObserver observer); } public interface IObserver { public void updateFrom(ISubject subject); }

L’interfaccia ISubject permette di aggiungere e rimuovere observer, l’interfaccia IObserver contiene un singolo metodo che un ISubject chiama ogni volta che deve notificare un cambiamento nel proprio stato interno.

La classe NotificationList che implementa l’interfaccia ISubject ha la seguente implementazione

import java.util.ArrayList; public class NotificationList implements ISubject { private ArrayList observers; private Object state; @Override public void addObserver(IObserver observer) { observers.add(observer); } @Override public void removeObserver(IObserver observer) { observers.remove(observer); } private void notifyObservers() { for(IObserver observer : observers) { observer.updateFrom(this); } } public void setState(Object newState) { state = newState; notifyObservers(); } public Object getState() { return state; }

La classe NotificationManager che implementa l’interfaccia IObserver ha la seguente implementazione.

public class NotificationManager implements IObserver { @Override public void updateFrom(ISubject subject) { // Code to change based on the updated state } }


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3