Java8 Lambda expression & Stream design pattern reconsideration --Command pattern -

Introduction

In this series, we will consider how to implement design patterns using the lambda expression / Stream API introduced in Java 8. The previous article (http://qiita.com/yonetty/items/e99b01470c3416af761c) covered the Template Method pattern.

This theme

This time we'll cover the Command pattern. In the sample program, write the command to move the piece (Peace) that moves on the two-dimensional coordinates with the Command pattern, and try to make it a lambda expression version.

The Piece class to be operated has x-coordinate, y-coordinate, and direction (Direction) as fields, and moves back and forth (moveForward (), moveBackward ()) and sets the direction (setDirection (setDirection () Change the state by Direction)) `). It is assumed that the initial state is the origin (0,0) and faces north (up).

Piece.java


public class Piece {
    private Direction direction = Direction.NORTH;
    private int x = 0;
    private int y = 0;

    public void moveForward() {
        this.x += this.direction.x();
        this.y += this.direction.y();
    }

    public void moveBackward() {
        this.x -= this.direction.x();
        this.y -= this.direction.y();
    }

    public Direction getDirection() {
        return direction;
    }

    public void setDirection(Direction direction) {
        this.direction = direction;
    }

    public int getX() {
        return x;
    }

    public int getY() {
        return y;
    }

    public static enum Direction {
        NORTH(Math.PI / 2), EAST(0), SOUTH(Math.PI / -2), WEST(Math.PI);

        final private double radian;
        final private int x;
        final private int y;

        Direction(double radian) {
            this.radian = radian;
            this.x = (int) Math.cos(radian);
            this.y = (int) Math.sin(radian);
        }

        public static Direction valueOf(double radian) {
            return Stream.of(Direction.values())
                    .filter(d -> d.x == (int) Math.cos(radian) && d.y == (int) Math.sin(radian))
                    .findAny()
                    .orElseThrow(IllegalArgumentException::new);
        }

        public int x() {
            return this.x;
        }

        public int y() {
            return this.y;
        }

        public double radian() {
            return this.radian;
        }
    }
}

Conventional mounting method

First, define the interface for Command. Have a ʻexecute (Piece)method that receives aPiece` object and processes it.

PieceCommand.java


public interface PieceCommand {

    void execute(Piece piece);
}

As a concrete Command implementation, prepare a command to move back and forth by the specified number of squares (Forward, Backward) and change the direction to the left and right (TurnRight, TurnLeft). In addition, the direction expansion to the left and right is a rotation of ± 90 degrees (π / 2 radians), and a new direction is obtained from the current direction.

Forward.java



public class Forward implements PieceCommand {
    private final int step;

    public Forward(int step) {
        this.step = step;
    }

    @Override
    public void execute(Piece piece) {
        for (int i = 0; i < step; i++) {
            piece.moveForward();
        }
    }
}

TurnLeft.java


public class TurnLeft implements PieceCommand {

    @Override
    public void execute(Piece piece) {
        double radian = piece.getDirection().radian() + Math.PI / 2;
        piece.setDirection(Piece.Direction.valueOf(radian));
    }

}

Here is a code example that uses these instructions to move Piece.

Usage


        PieceCommand cmd1 = new Forward(5);
        cmd1.execute(piece);

        assertEquals(Piece.Direction.NORTH, piece.getDirection());
        assertEquals(0, piece.getX());
        assertEquals(5, piece.getY());

        PieceCommand cmd2 = new TurnRight();
        cmd2.execute(piece);

        assertEquals(Piece.Direction.EAST, piece.getDirection());
        assertEquals(0, piece.getX());
        assertEquals(5, piece.getY());

        PieceCommand cmd3 = new Backward(3);
        cmd3.execute(piece);

        assertEquals(Piece.Direction.EAST, piece.getDirection());
        assertEquals(-3, piece.getX());
        assertEquals(5, piece.getY());

        PieceCommand cmd4 = new TurnLeft();
        cmd4.execute(piece);

        assertEquals(Piece.Direction.NORTH, piece.getDirection());
        assertEquals(-3, piece.getX());
        assertEquals(5, piece.getY());

Implementation using lambda expression

Now let's implement the Command pattern using a lambda expression. First, each instruction receives a Piece object and performs forward / backward movement and direction change operations, so let's realize it with the functional interfaceConsumer <T>provided by the Java 8 standard. The PieceCommand in the previous section will be replaced with theConsumer <Piece>type. And we will prepare a Factory to get the function that represents each instruction.

The implementation of Forward.java in the previous section dared to use the old-fashioned for syntax, but this has also been corrected to the writing method using Stream API.

PieceCommandFactory.java


public class PieceCommandFactory {

    static Consumer<Piece> ofForward(final int step) {
        return p -> {
            IntStream.range(0, step)
                    .forEach(i -> p.moveForward());
        };
    }

    static Consumer<Piece> ofBackward(final int step) {
        return p -> {
            IntStream.range(0, step)
                    .forEach(i -> p.moveBackward());
        };
    }

    static Consumer<Piece> ofTurnRight() {
        return p -> {
            double radian = p.getDirection().radian() - Math.PI / 2;
            p.setDirection(Piece.Direction.valueOf(radian));
        };
    }

    static Consumer<Piece> ofTurnLeft() {
        return p -> {
            double radian = p.getDirection().radian() + Math.PI / 2;
            p.setDirection(Piece.Direction.valueOf(radian));
        };
    }

}

Here is a code example using the lambda expression version:

Usage


        Consumer<Piece> cmd1 = PieceCommandFactory.ofForward(5);
        cmd1.accept(piece);

        assertEquals(Piece.Direction.NORTH, piece.getDirection());
        assertEquals(0, piece.getX());
        assertEquals(5, piece.getY());

        Consumer<Piece> cmd2 = PieceCommandFactory.ofTurnRight();
        cmd2.accept(piece);

        assertEquals(Piece.Direction.EAST, piece.getDirection());
        assertEquals(0, piece.getX());
        assertEquals(5, piece.getY());

        Consumer<Piece> cmd3 = PieceCommandFactory.ofBackward(3);
        cmd3.accept(piece);

        assertEquals(Piece.Direction.EAST, piece.getDirection());
        assertEquals(-3, piece.getX());
        assertEquals(5, piece.getY());

        Consumer<Piece> cmd4 = PieceCommandFactory.ofTurnLeft();
        cmd4.accept(piece);

        assertEquals(Piece.Direction.NORTH, piece.getDirection());
        assertEquals(-3, piece.getX());
        assertEquals(5, piece.getY());

Function composition

The advantage of the lambda expression version is that you can synthesize functions. Specifically, the default method ʻandThen (Consumer <? Super T>)defined inConsumer ` is used.

UsegeOfAndThen


        Consumer<Piece> composite = PieceCommandFactory.ofForward(5)
                .andThen(PieceCommandFactory.ofTurnRight())
                .andThen(PieceCommandFactory.ofBackward(3))
                .andThen(PieceCommandFactory.ofTurnLeft());
        composite.accept(piece);

Since the synthesized result is the same Consumer <Piece> type function, it can be treated as a command (instruction) that operates Piece, similar to the one defined as a static method of PieceCommandFactory. You can define and use new functions by compositing as follows.

Composite


        //Repeat "right for right" and "turn right"
        Consumer<Piece> reverse = PieceCommandFactory.ofTurnRight()
                .andThen(PieceCommandFactory.ofTurnRight());

By the way, how is function composition implemented? Looking at the source of Consumer <T>, the implementation is as follows.

Consumer.java


    default Consumer<T> andThen(Consumer<? super T> after) {
        Objects.requireNonNull(after);
        return (T t) -> { accept(t); after.accept(t); };
    }

It's pretty simple. It is one line except for the precondition check. After calling the Consumer <T> instance's own ʻaccept to which ʻandThen belongs, theConsumer <T>passed as an argument is called the ʻaccept. I think it's a good idea to simulate in your head how a lambda expression is constructed and executed when multiple ʻand Thens are concatenated.

Reduce Instead of concatenating ʻandThenin sequence, you can implement a utility method that receives a variable array and returns the synthesizedConsumer ` as follows.

PieceCommandFactory#chain


    @SafeVarargs
    static Consumer<Piece> chain(Consumer<Piece>... commands) {
        return Stream.of(commands)
                .reduce((c1, c2) -> c1.andThen(c2))
                .orElse(p -> {
                });
    }

By synthesizing the two functions sequentially with ʻand Then, they can be combined into one function as a whole. Finally, ʻor Else specifies a lambda expression that does nothing, so even if the argument commands is null, it works safely. It is okay if the length is 1.

SafeChain


        Consumer<Piece> composite = PieceCommandFactory.chain(PieceCommandFactory.ofForward(5));
        composite.accept(piece);

        assertEquals(Piece.Direction.NORTH, piece.getDirection());
        assertEquals(0, piece.getX());
        assertEquals(5, piece.getY());

        Consumer<Piece> empty = PieceCommandFactory.chain();
        empty.accept(piece);

        assertEquals(Piece.Direction.NORTH, piece.getDirection());
        assertEquals(0, piece.getX());
        assertEquals(5, piece.getY());

Summary

The property of the Command pattern to proceed with processing by sequentially applying multiple Commands seems to be compatible with the concept of ** function composition ** in functional programming. Next, I'm going to take up the Chain of Responsibility (chain of responsibility) pattern.

Recommended Posts

Java8 Lambda expression & Stream design pattern reconsideration --Command pattern -
Java8 Lambda expression & Stream design pattern reconsideration --Template Method pattern -
Java8 Lambda Expression & Stream Design Pattern Rethinking --Null Object Pattern -
Java8 Lambda Expression & Stream Design Pattern Rethinking --Chain of Responsibility Pattern -
Java Lambda Command Pattern
Java8 stream, lambda expression summary
Rethinking design patterns with Java8 lambda expressions & Stream --Builder pattern -
Java design pattern
Design pattern ~ Command ~
[Java] Lambda expression
Java lambda expression
java neutral lambda expression 1
Java lambda expression variations
Java 8 lambda expression Feature
java lambda expression memo
Java lambda expression [memo]
Studying Java 8 (lambda expression)
Review java8 ~ Lambda expression ~
Java lambda expression again
Java design pattern summary
Getting Started with Legacy Java Engineers (Stream + Lambda Expression)
[Java] Functional interface / lambda expression
About Lambda, Stream, LocalDate of Java8
Java basic learning content 9 (lambda expression)
What is a lambda expression (Java)
Java beginner design pattern (Factory Method pattern)
Now let's recap the Java lambda expression
Nowadays Java lambda expressions and Stream API
I've reviewed Java's lambda expression, stream API, six months after I started Java.
How to use Java API with lambda expression
Java8 to start now ~ forEach and lambda expression ~
Design pattern ~ Builder ~
[Java] Strategy pattern
Design pattern ~ Visitor ~
[JAVA] Stream type
java callback pattern
Design pattern ~ Proxy ~
Try Java 8 Stream
Design pattern ~ State ~
Design pattern ~ Strategy ~
Design pattern ~ Singleton ~
Design pattern (2): Builder
Design pattern (1): AbstractFactory
[Java] Singleton pattern
Java Stream API
Design pattern ~ Iterator ~
Java Design Patterns
Design pattern ~ Facade ~
Design pattern ~ Bridge ~
Design pattern ~ Mediator ~
Design pattern ~ Decorator ~
Design pattern ~ Interpreter ~
Hello Java Lambda
Design pattern ~ Observer ~
Studying Java 8 (Stream)
Design pattern ~ Prototype ~
[Java] Adapter pattern
Design pattern ~ Memento ~
Design pattern ~ Adapter ~
Java Stream termination
[Java] Stream processing