SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

DeveloperZine(デベロッパージン)- エンジニアの意思決定を支える技術情報メディア ProductZine

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

デザインパターンを置き換えよう! Javaラムダ式によるシンプルコーディング

ラムダ式でCommandパターンで書かれたコードをシンプルにする ~ ラムダ式使いへの第一歩

デザインパターンを置き換えよう! Javaラムダ式によるシンプルコーディング 第1回


亀シミュレータ(Commandパターンバージョン)

 それではここから、「平面上を歩きまわる亀のシミュレータ」を題材として、Commandパターンを用いて作られたプログラムが、ラムダ式を使ってどのように書き直せるかを見ていきます。このシミュレータは、ユーザが矢印文字「←↓↑→」からなる文字列を入力することによって、XY座標上にいる「亀」の位置を動かすというものです。例えば、開始時点で(X=0, Y=0)にいる亀に「↓↓→」という入力を与えると、亀は(X=1, Y=2)に移動します。今回のプログラムでは簡単のため、ユーザの入力文字は定数で与えることにします。また、プログラムの最後に移動を終えた亀の位置を標準出力に書き出します。

 まずは、Commandパターンを使って実装した場合のプログラムを見てみましょう。このプログラムのクラス図は図3のとおりです(一部のクラスは省略されています)。

図3 亀シミュレータのクラス図(Commandパターン)
図3 亀シミュレータのクラス図(Commandパターン)

 

 それぞれのクラス、インタフェースの役割は次のとおりです。

  • Motion:亀の動作を表すコマンドオブジェクトのインタフェース。Commandの役割を担う
  • LeftMotion:矢印文字「←」に対応し、亀を左方向に動かすコマンドオブジェクトのクラス。ConcreteCommandの役割を担う
  • DownMotion:矢印文字「↓」に対応し、亀を下方向に動かすコマンドオブジェクトのクラス。ConcreteCommandの役割を担う
  • UpMotion:矢印文字「↑」に対応し、亀を上方向に動かすコマンドオブジェクトのクラス。ConcreteCommandの役割を担う
  • RightMotion:矢印文字「→」に対応し、亀を右方向に動かすコマンドオブジェクトのクラス。ConcreteCommandの役割を担う
  • Turtle:操作対象の亀。Receiverの役割を担う
  • TurtleMover:矢印文字にしたがって亀の動きを起動するクラス。Invokerの役割を担う
  • CommandTurtleSimulator:矢印文字に対応する亀の動きを TurtleMover クラスのインスタンスに登録するクラス。Clientの役割を担う。

 また、Commandパターンを用いたプログラムのシーケンス図は図4のとおりです(一部のクラスは省略されています)。

図4 亀シミュレータのシーケンス図(Commandパターン)
図4 亀シミュレータのシーケンス図(Commandパターン)

 

 CommandTurtleSimulator クラスは、矢印文字と亀の動きとの対応を、registerMotion メソッドを用いて TurtleMover クラスのインスタンスに登録します。その後、TurtleMover クラスの moveAround メソッドを用いて亀の動きを実行します。

 Commandパターンを用いたプログラムをリスト6に掲げます。

リスト6:Commandパターンを用いたプログラム
import java.util.*;

public class CommandTurtleSimulator {

    public static void main(String[] args) {
        // (1) ユーザの入力
        String userInput = "↓←←↑↑→→→→↓↓";

        // (2) 矢印文字に対応する動作を登録
        TurtleMover turtleMover = new TurtleMover();
        turtleMover.registerMotion('←', new LeftMotion());
        turtleMover.registerMotion('↓', new DownMotion());
        turtleMover.registerMotion('↑', new UpMotion());
        turtleMover.registerMotion('→', new RightMotion());

        // (3) 原点上の亀
        Turtle turtle = new Turtle(0, 0);

        // (4) 入力に従って亀を動かす
        turtleMover.moveAround(turtle, userInput);

        // (5) 移動先を表示
        System.out.printf("X=%d Y=%d%n", turtle.getX(), turtle.getY());
    }

    /** 平面上の亀. */
    static class Turtle {
        private int x, y;
        Turtle(int x, int y) {
            this.x = x;
            this.y = y;
        }
        int getX() {
            return this.x;
        }
        void setX(int x) {
            this.x = x;
        }
        int getY() {
            return this.y;
        }
        void setY(int y) {
            this.y = y;
        }
    }

    /** 亀の動きを起動するクラス. */
    static class TurtleMover {
        /** 文字と動作の対応. */
        private final Map<Character, Motion> motionMap = new HashMap<>();

        /** 文字に動作を割り当てる. */
        void registerMotion(char ch, Motion motion) {
            this.motionMap.put(ch, motion);
        }

        /** 入力文字列に従って亀を動かす. */
        void moveAround(Turtle turtle, String input) {
            for (char ch : input.toCharArray()) {
                Motion motion = this.motionMap.get(ch);
                if (motion != null) {
                    motion.move(turtle);
                }
            }
        }
    }

    /** 亀の動作. */
    interface Motion {
        public void move(Turtle turtle);
    }

    /** 亀を左に動かすコマンド. */
    static class LeftMotion implements Motion {
        @Override public void move(Turtle turtle) {
            turtle.setX(turtle.getX() - 1);
        }
    }

    /** 亀を下に動かすコマンド. */
    static class DownMotion implements Motion {
        @Override public void move(Turtle turtle) {
            turtle.setY(turtle.getY() + 1);
        }
    }

    /** 亀を上に動かすコマンド. */
    static class UpMotion implements Motion {
        @Override public void move(Turtle turtle) {
            turtle.setY(turtle.getY() - 1);
        }
    }

    /** 亀を右に動かすコマンド. */
    static class RightMotion implements Motion {
        @Override public void move(Turtle turtle) {
            turtle.setX(turtle.getX() + 1);
        }
    }
}

 実行結果は次のとおりです。

X=2 Y=1

 以上のように、TurtleMover クラスの内部に亀の動作を記述することはなく、具体的な動作は登録されたコマンドオブジェクトに託しています。

次のページ
亀シミュレータ(ラムダ式バージョン)

この記事は参考になりましたか?

デザインパターンを置き換えよう! Javaラムダ式によるシンプルコーディング連載記事一覧

もっと読む

この記事の著者

宮川 拓(ミヤカワ タク)

日本Javaユーザーグループ幹事。東京のシステムインテグレータに勤務。Java VM上で動作する言語である「Kink」を開発中。相撲とアメリカ文学とスコティッシュポップを愛する。 ・ブログ: http://d.hatena.ne.jp/miyakawa_taku/ ・Twitter: @miyakawa_taku

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/8300 2015/01/07 17:21

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー