Software-Entwurfsmuster

Compound-Iterator für drei Iteratoren

Der Compound-Iterator für drei Iteratoren durchläuft quasi das kartesische Produkt dreier iterierbarer Folgen, also die Folge aller Tripel aus den Elementen dieser Folgen. Normalerweise benötigt man drei ineinander geschachtelte For-Schleifen, um die Elemente dieses kartesischen Produkts zu durchlaufen.

Ein Compound-Iterator hat dagegen den Vorteil, dass er, einmal geschrieben, nur eine For-Schleife benötigt und dass er überall einsetzbar ist, wo ein Iterator stehen kann, zum Beispiel als Basisiterator eines Filteriterators.

Problem

Es sollen alle Positionen eines 8×8-Spielbretts durchlaufen werden und dabei jeweils ein Spielzug mit einem der Spielsteine 0, ..., 3 ausprobiert werden. Ein Iterator hierfür verwendet einen Basisiterator für die Zeilen und einen Subiterator für die Spalten des Spielbretts sowie einen Subsubiterator für die vier Spielsteine, und er setzt dann aus dem jeweiligen Zeilenindex z, dem Spaltenindex s und dem Spielstein f einen Spielzug zusammen.

Die Klasse Move kapselt ein solches Tripel von Integer-Werten.

 

public class Move
{
    // Attribute
    public int z, s, f;

    // Konstruktor
    public Move(int z, int s, int f)
    {
        this.z=z;
        this.s=s;
        this.f=f;
    }
    
    // Methoden

    //  ...

    public String toString()
    {
        return "("+z+","+s+","+f+")";
    }

}

Compound-Iterator: Idee

Gesucht ist nun ein Iterator, der alle Positionen des Spielbretts durchläuft und dabei die vier Spielsteine ausprobiert. Ein solcher Iterator lässt sich als Compound-Iterator3 realisieren. Ein Compound-Iterator3 benutzt einen Basisiterator (im Beispiel der Iterator für die Zeilen), einen Subiterator (im Beispiel der Iterator für die Spalten) und einen Subsubiterator (im Beispiel der Iterator für die Spielsteine). Ferner wird eine Funktion composeItems benötigt, um aus den drei Cursor-Objekten der drei Iteratoren ein Objekt zu machen, das mit next() zurückgegeben wird (im Beispiel ein Move-Objekt bestehend aus dem Zeilenindex, dem Spaltenindex und dem Spielstein).

Basisiterator, Subiterator und Subsubiterator

Als Basisiterator, als Subiterator und als Subsubiterator wird jeweils ein Generator verwendet, der nacheinander die Zahlen von 0 bis n-1 liefert.

 

public class IntIterator implements Iterator<Integer>
{
    private int i, n;

    public IntIterator(int n)
    {
        this.n=n;
        i=0;
    }

    @Override
    public boolean hasNext()
    {
        return i<n;
    }

    @Override
    public Integer next()
    {
        return i++;
    }

}

 

Behavior AllMoves

Der Basisiterator, der Subiterator, der Subsubiterator und die Funktion composeItems werden in einem sogenannten Behavior festgelegt, mit dem der Compound-Iterator arbeitet. Das folgende Behavior AllMoves ergibt einen Compound-Iterator3, der alle Positionen eines rows×cols-Spielbretts durchläuft und dabei alle figs möglichen Spielsteine ausprobiert. Im Konstruktor werden die Werte für rows, für cols und für figs übergeben. Es folgen dann die vier Methoden, die in der abstrakten Basisklasse CompoundIteratorBehavior3 festgelegt sind.

In der Methode baseIterator wird der Basisiterator spezifiziert, der die Zahlen von 0 bis rows-1 durchläuft, in der Methode subIterator der Subiterator, der die Zahlen von 0 bis cols-1 durchläuft, und in der Methode subsubIterator der Subsubiterator, der alle Spielsteine von 0 bis figs-1 durchläuft. Im Allgemeinen ist es möglich, dass der Subiterator vom Cursor-Objekt x0 des Basisiterators abhängig ist und der Subsubiterator von den Cursor-Objekten x0 und x1 des Basisiterators und des Subiterators, deswegen die Parameter, dies wird aber hier nicht benutzt. Die Methode composeItems setzt aus dem Cursor-Objekt x0 des Basisiterators, dem Cursor-Objekt x1 des Subiterators und dem Cursor-Objekt x2 des Subsubiterators einen Spielzug (x0,x1,x2) zusammen.

Die erwähnte Factory-Methode, die den fertigen Iterator zurückgibt, befindet sich in der Basisklasse CompoundIteratorBehavior3.

 

public class AllMoves extends CompoundIteratorBehavior3<Integer, Integer, Integer, Move>
{
    private int rows, cols, figs;

    public AllMoves(int rows, int cols, int figs)
    {
        this.rows=rows;
        this.cols=cols;
        this.figs=figs;
    }

    @Override
    // the base iterator
    public Iterator<Integer> baseIterator()
    {
        return new IntIterator(rows);
    }

    @Override
    // the subiterator
    public Iterator<Integer> subIterator(Integer x0)
    {
        return new IntIterator(cols);
    }

    @Override
    // the subsubiterator
    public Iterator<Integer> subsubIterator(Integer x0, Integer x1)
    {
        return new IntIterator(figs);
    }

    @Override
    // specifies the object composed of the three cursor objects x0, x1 and x2
    public Move composeItems(Integer x0, Integer x1, Integer x2)
    {
        return new Move(x0, x1, x2);
    }

}

Anwendung

Die folgende Main-Funktion testet den Compound-Iterator3 mit dem Behavior AllMoves. Es werden alle Positionen eines 2×3-Spielbretts durchlaufen und jeweils 2 Spielsteine ausprobiert.

public class TestAllMoves
{
    public static void main(String[] args)
    {
        // Compound-Iterator3 mit dem AllMoves-Behavior
        Iterator<Move> it=new AllMoves(2, 3, 2).iterator();
        while (it.hasNext())
            System.out.println(it.next());
        System.out.println();

        // oder einfacher:
        for (Move move : new AllMoves(2, 3, 2))
            System.out.println(move);
        System.out.println();
    }
}

Zusammenfassung

Die konkret benötigte Funktionalität eines Compound-Iterators für drei Iteratoren wird in einem Behavior (hier AllMoves) festgelegt. Dies geschieht durch Angabe eines Basisiterators, eines Subiterators, eines Subsubiterators und der Funktion composeItems.

Zugrunde gelegt werden dabei getestete Implementierungen der Klassen Compound-Iterator3 und Compound-IteratorBehavior3.

Für das angegebene sehr einfache Beispiel lohnt sich der Aufwand, einen Compound-Iterator zu benutzen, vielleicht nicht. Dadurch aber, dass der Compound-Iterator selbst das Iterator-Interface implementiert, kann er überall eingesetzt werden, wo ein Iterator stehen kann, also z.B. als Basisiterator in einem Filteriterator oder als Subiterator in einem anderen Compound-Iterator.

 

Weiter mit:   [Implementierung CompoundIterator3]  oder  [up]

 


H.W. Lang   mail@hwlang.de   Impressum   Datenschutz
Created: 22.02.2021   Updated: 18.02.2023
Diese Webseiten sind während meiner Lehrtätigkeit an der Hochschule Flensburg entstanden