Zitat

Lambda Expressions – Template Method Pattern reloaded

Beim Template Method Pattern wird in einer abstrakten Klasse das Skelett eines Algorithmus definiert. Die konkrete Ausformung der einzelnen Schritte wird an Unterklassen delegiert. Dadurch besteht die Möglichkeit, einzelne Schritte des Algorithmus zu verändern oder zu überschreiben, ohne dass die zu Grunde liegende Struktur des Algorithmus modifiziert werden muss. Die Template Method ruft abstrakte Methoden auf, die erst in den Unterklassen definiert werden. Diese Methoden werden auch als Hook Methoden bezeichnet.

Durch die Verwendung von Lambda Expressions kann dieses Pattern kompfortabler implementiert werden. Anstatt die Hook Logik in Unterklassen zu implementieren, kann dies nun in Lambda Expressions erfolgen. Es ist nicht mehr nötig mehrere Klassen zu implementieren.

Die Klasse AbstractGame definiert in der Methode playOneGame den grundlegenden Spielmechanismus (ein Spieler nach dem anderen usw) und delegiert die Definition der Spielregeln an die abstrakten Methoden. Diese werden erst von den erbenden konkreten Klassen implementiert.

/**
 * An abstract class that is common to several games in
 * which players play against the others, but only one is
 * playing at a given time.
 */

abstract class AbstractGame {

    protected int playersCount;
    abstract void initializeGame();
    abstract void makePlay(int player);
    abstract boolean endOfGame();
    abstract void printWinner();

    /* A template method : */
    public final void playOneGame(int playersCount) {
        this.playersCount = playersCount;
        initializeGame();
        int j = 0;
        while (!endOfGame()) {
            makePlay(j);
            j = (j + 1) % playersCount;
        }
        printWinner();
    }
}

Die Klasse Monopoly implementiert die Regeln des Monopoly Spiels.

//Now we can extend this class in order
//to implement actual games:

class Monopoly extends Game {

    /* Implementation of necessary concrete methods */
    void initializeGame() {
        // Initialize players
        // Initialize money
    }
    void makePlay(int player) {
        // Process one turn of player
    }
    boolean endOfGame() {
        // Return true if game is over
        // according to Monopoly rules
    }
    void printWinner() {
        // Display who won
    }
    /* Specific declarations for the Monopoly game. */

    // ...
}

Die Klasse Schach implementiert die Schach Regeln.

class Chess extends Game {

    /* Implementation of necessary concrete methods */
    void initializeGame() {
        // Initialize players
        // Put the pieces on the board
    }
    void makePlay(int player) {
        // Process a turn for the player
    }
    boolean endOfGame() {
        // Return true if in Checkmate or
        // Stalemate has been reached
    }
    void printWinner() {
        // Display the winning player
    }
    /* Specific declarations for the chess game. */

    // ...
}

Jetzt das Ganze mit Lambdas:
Die Klasse AbstractGame delegiert die Definition der konkreten Spielregeln nicht an die abstrakten Methoden, sondern an Lambda-Expressions. Dadurch darf (muss) die Klasse nicht mehr abstract sein. Die Lambda Expression werden dem Konstruktor übergeben.

public class Game {

	interface FunctionNoParameter {
		void call();
	}

	private FunctionNoParameter initializeGame;
	private Consumer<Integer> makePlay;
	private BooleanSupplier endOfGame;
	private FunctionNoParameter printWinner;
	private int playersCount;

	public Game(FunctionNoParameter initializeGame,
Consumer<Integer> makePlay,
BooleanSupplier endOfGame,
FunctionNoParameter printWinner) {
		this.initializeGame = initializeGame;
		this.makePlay = makePlay;
		this.endOfGame = endOfGame;
		this.printWinner = printWinner;
	}

	/* A template method : */
	public void playOneGame(int playersCount) {
		this.playersCount = playersCount;
		initializeGame.call();
		int j = 0;
		while (!endOfGame.getAsBoolean()) {
			makePlay.accept(j);
			j = (j + 1) % playersCount;
		}
		printWinner.call();
	}
}

Beim Instanzieren des Chess und Monopoly Objekts werden die Spielregeln als Lambda Expressions übergeben.

public class TemplateWithLambdas {
	public void main(String[] arg) {
		final Game chess = new Game(() -> {
					// Initialize players
					// Put the pieces on the board
				},
				integer -> {/* Process a turn for the player */} ,
				() -> {
					// Return true if in Checkmate or
					// Stalemate has been reached
					return true;
				},
				() -> {/* Display the winning player*/});
		final Game monopoly = new Game(() -> {
					// Initialize players
					// Initialize money
				},
				integer -> {/* Process a turn for the player */} ,
				() -> {
					// Return true if game is over
					// according to Monopoly rules
					return true;
				},
				() -> {/* Display who won */});
	}
}

Das Beispiel hakt etwas, denn eine Lambda Expression kann nur auf final Variablen des übergeordneten Scope zugreifen. Somit kann die Lambda Expression initializeGame nicht den Zustand des Game-Objekts verändern. Vermutlich müsste initializeGame der Zustand des Spiels zurück geben und die Methode playOneGame müsste den Zustand des Spiels verwalten.

Advertisements

Kommentar verfassen

Trage deine Daten unten ein oder klicke ein Icon um dich einzuloggen:

WordPress.com-Logo

Du kommentierst mit Deinem WordPress.com-Konto. Abmelden / Ändern )

Twitter-Bild

Du kommentierst mit Deinem Twitter-Konto. Abmelden / Ändern )

Facebook-Foto

Du kommentierst mit Deinem Facebook-Konto. Abmelden / Ändern )

Google+ Foto

Du kommentierst mit Deinem Google+-Konto. Abmelden / Ändern )

Verbinde mit %s