State パターンでアニメーションの挙動を制御する

あるオブジェクトが絶えずアニメーションをするようなコードを書く時、様々な状態に応じて挙動を変えたいというのはよくあることだと思います。単純に実装すると、状態に対応するフラグを記録しておき、それに応じて条件分岐するという書き方になると思います。しかし、状態の数が増えると、フラグ管理が大変になり、コードがどんどん汚くなっていきます。そこで、State パターンのように、状態ごとに別々の挙動を行う関数を定義し、オブジェクトの状態が遷移した時に実行する関数を切り替えるようにすると、コードが状態ごとに整理され、見やすくなります。

アニメーションにおける条件分岐のつらさ

例えば、下記のように、矢印キーで動かすことができ、壁にぶつかると跳ね返るような Ball オブジェクトについて考えます。Ball は update() メソッドを持ち、requestAnimationFrame() で毎回このメソッドが呼ばれます。cx, cy が Ball の中心の座標で、vx, vyupdate() ごとに cx, cy に加算される値 (速度) です。矢印キーを押すと ax, ay (加速度) が更新され、押した方向へとボールが動きます。簡単のために、DOM への要素の追加などのコードは省いています。

const viewportHeight = 300;
const viewportWidth = 400;

class Ball {
  constructor() {
    // 中心点の座標
    this.cx = 200;
    this.cy = 150;

    // 速度
    this.vx = this.vy = 0;

    // 加速度
    this.ax = this.ay = 0;

    // ボールの半径
    this.r = 10;
  }

  update() {
    // 壁にぶつかったら跳ね返る
    if (this.cx - this.r < 0 || this.cy + this.r > viewportWidth) {
      this.vx *= -1;
    }
    if (this.cy - this.r < 0 || this.cy + this.r > viewportHeight) {
      this.vy *= -1;
    }

    this.vx += this.ax;
    this.vy += this.ay;
    this.cx += this.vx;
    this.cy += this.vy;
  }

  accelerate(code) {
    this.ax = this.ay = 0;
    
    switch (code) {
      case 37: // left
        this.ax = -0.1;
        break;
      case 38: // up
        this.ay = -0.1;
        break;
      case 39: // right
        this.ax = 0.1;
        break;
      case 40: // down
        this.ay = 0.1;
        break;
      default:
        // do nothing
    }
  }
}

let b = new Ball();
$(window)
  .on('keydown', (e) => b.accelerate(e.which))
  .on('keyup', () => b.accelerate());

animate();

function animate() {
  b.update();
  requestAnimationFrame(animate);
}

この Ball オブジェクトを拡張して、一定の速度以上の時は壁にぶつからなくするようにしたり、マウスポインターを乗せたときに動きを止め何らかのエフェクトをかけたりすると、条件分岐が増えてつらくなってきます。書いた直後は良いのですが、時間が立つにつれ、各条件分岐について完全に把握することは難しくなり、コードの変更の際、バグを混入させる確率が上がります。

class Ball {
  ...
  update() {
    if (/* マウスホバーしていたら */) {
      /* 何かのエフェクト */
      return;
    }

    if (/* 速度が一定以下なら */) {
      // 壁にぶつかったら跳ね返る
      if (this.cx - this.r < 0 || this.cy + this.r > viewportWidth) {
        this.vx *= -1;
      }
      if (this.cy - this.r < 0 || this.cy + this.r > viewportHeight) {
        this.vy *= -1;
      }
    }

    this.vx += this.ax;
    this.vy += this.ay;
    this.cx += this.vx;
    this.cy += this.vy;
  }
  ...
}

条件分岐の代わりに State パターンを使う

State パターンを使うと、上記の Ball オブジェクトの挙動をよりすっきりとしたコードで書くことができます。State パターンでは、各状態ごとにクラスを定義し、ある状態の時のオブジェクトの挙動は、対応する状態のクラスのみに記述されます。つまり、オブジェクトの挙動をそのオブジェクトのクラスに書くのではなく、状態のクラスに書くという点が特徴です。

上記の Ball オブジェクトの挙動を State パターンで書くと以下のようになります。まず、各状態ごとの挙動を定義した関数を作ります。次に Ball オブジェクトに新たに behavior というプロパティを追加します。behavior は関数が入るプロパティであり、update() メソッドの中で毎回呼ばれます。そして、状態が切り替わるごとに、behavior に状態に対応した関数を代入するようにします。今回の例では、update の内部以外に状態によって挙動が変わる部分がないため、関数自体を切り替えています。完全なコードは CodePen に置いておきました。Simple Ball Animation with State Pattern

class Ball {
  constructor() {
    this.behavior = moveBehavior;
  }

  update() {
    this.behavior(this);
  }
}

function moveBehavior(ball) {
  /* 矢印キーで動かす処理 */

  if (/* 速度が一定以上 */) {
    ball.behavior = leaveBehavior; // 状態を変える
  }
}

function effectBehavior(ball) {
  /* マウスホバーでエフェクトをかける処理 */
}

function leaveBehavior(ball) {
  /* 壁にぶつからずに動く処理 */
}

まとめ

オブジェクトの update 時に実行される関数を状態ごとに変えることによって、アニメーションの挙動の記述の条件分岐を減らし、コードをすっきりとさせました。単純なアニメーションなら同一のオブジェクトに直接挙動を書けば良いですが、ユーザーの入力が増えたりして、取りうる状態が増えそうであれば、State パターンで書き直したほうが良いように思います。

Vue.js を使った中規模 Web アプリ向けのディレクトリ構造を考えた

最近 Vue.js を使って Web アプリを書いていて、どんなディレクトリ構造だと良いんだろうなーということを考えた結果を書きとめようと思います。Angular Best Practice for App Structureっぽい感じです。

ディレクトリ構造

├── app
│   ├── components
│   │   ├── component1
│   │   │   ├── component1.html
│   │   │   ├── component1.js
│   │   │   └── component1.css
│   │   ├── component2
│   │   │   ├── component2.html
│   │   │   ├── component2.js
│   │   │   └── component2.css
│   │   ├── component3
│   │   │   ...
│   │   ...
│   ├── filters
│   │   └── filter1.js
│   ├── directives
│   │   └── directive1.js
│   ├── plugins
│   │   └── plugin1.js
│   ├── images
│   │   ...
│   ├── index.html
│   ├── main.js
│   └── main.css
├── gulpfile.js
├── package.json
├── webpack.conf.js
...

コンポーネントごとにディレクトリを分け、コンポーネントを構成する html, css, js ファイルを同一のディレクトリ内に入れるのが良いと思います。html や css ファイルも一緒にすることで、関連するファイルを探すのが楽になります。コンポーネントを取り除くときや、リファクタリングする時も、一つにまとまってるので楽です。

vueifyvue-loader によってコンポーネントを一つの .vue ファイルで構成するというやり方もありますが、個人的には分割するほうが良いのではないかなーと思います。まず、.vue ファイル一つだとコードが長くなると見通すのがつらいです。vueify や vue-loader の例を見るとすっきりとして見やすいなーと思いますが、実際のコンポーネントはもっと量が多く、スクロールで行ったり来たりするのが結構つらいです。また、エディタが対応してないのがつらいというのもあります。html として扱えばシンタックスハイライトはしてくれますが、各種 Lint 系ツールが動かなかったり、動いても不具合があったりしてつらかったです。

app/ 直下の main.js には各種プラグインの読み込みや、設定、ルーティングなどを書いてます。main.js をエントリポイントにして、ここから各種コンポーネントなどを読み込んでいく形になります。また、main.css にはコンポーネントには入らない、リセット系のスタイルなどを定義するようにします。

また、filter や directive、plugin などは components と同じ階層にディレクトリを作って、それぞれ分けています。この辺りはまだ全然実装してないので、コンポーネントと合わせれば良いのではないかなーという程度の考えです。

具体的にどう実装するのか

ディレクトリ構造を決めた後は、実際にその構造で動くように実装をしました。具体的には、webpack を用いてコンポーネント間の依存関係を解決し、各コンポーネントの css, js も読みこむようにしました。ここでは、webpack の具体的な設定を説明するのではなく、最終的にどのように書けるようにしたかを書きます。

// component1.js の例
require('./component1.css');

module.exports = {
  template: require('./component1.html'),

  ...

  components: {
    component2: require('../component2/component2')
  },
  filters: {
    filter1: require('../../filters/filter1')
  },
  ...
};

それぞれの js ファイルはコンポーネントのオプションオブジェクトを export するように書きます。Vue.component() メソッドは使用しません。filter や directive なども同様です。export させることで、コンポーネント間の依存関係を webpack に解決させてます。また、そういった書き方をしているので、各コンポーネントのオプションオブジェクトの中で、依存するコンポーネントなどを指定する必要があります。コンポーネントごとに依存関係を指定するのは面倒ですが、ソースコード内に依存関係が明示されるのは良いのではないかと考えています。

html, css ファイルは、そのコンポーネントの js ファイルの中で読み込ませます。webpack の html-loader を使えば html を文字列として取得し、template に渡すことができます。また、style-loader を使えば、読み込んだ css を style 要素としてページに追加することができます。html や css は普通に書けば良いです。

もうちょっと何とかしたい点

以下の点をもうちょっと何とかしたいです。

  • あるコンポーネントから別のコンポーネントや、フィルター、ディレクティブを参照する時の表現が冗長
  • CSS Module に対応したい
  • いろんな部分で使い回すようなモジュールはどういう扱いにするのか?

例えば上の例だと、component2 を参照する時は component2 を二回書かないとダメですし、filter1 に関しては ../../ がつらい感じです。前者についてはファイル名をすべて index.(ext) にすると、一回の記述ですみます。しかし、すべて index.(ext) にしてしまうと、エディタで開いた時がつらいです。後者については、webpack の resolve.root の設定に app/ ディレクトリを入れると省略はできますが、ライブラリ系以外で resolve.root 使うのはどうなんだろうなーという感じです。

また、css に関して、コンポーネントごとに分けてはいますが、普通に衝突します。css-loader は CSS Module にも対応しているようなので、試してみたいです。

いろんな部分で使い回すようなモジュールに関しては、とりあえず Vue.js のプラグインとして書いてみていますが、普通に Vue.js とは独立した書き方で書いて、それを import させても良さそうな気がします。

最後に

Web ページのディレクトリ構造といえばファイルの種類ごとに分けるのが一般的だったと思うのですが、各種フレームワークとか webpack とかを使ってコンポーネント化を意識すると、ファイルの種類ごとに分けると依存関係がわからなくなってつらくなることが多いように感じます。初めは少し抵抗を覚えますが、コンポーネントごとに分けるのも良いのではないかと考えます。

今回のディレクトリ構造を中規模向けとしたのは、おそらく大規模になってくるとコンポーネントの数が多すぎて、うまく目的のものを見つけられないということが起こると考えたからです。大規模な Web アプリの場合は、ページごとにさらにディレクトリを分けるなどする必要があるんじゃないかなと思います。

2014年に読んだ技術書の中で良いと思ったもの三冊

もう年明けてしまいましたが、僕が2014年に読んだ技術書の中で良いと思ったものを三冊紹介します。ちなみに、筆者は普段 Web 系のコードを主に書いているので、読んでる本もそういったものに偏り気味です。

プログラミング HTML5 Canvas – ウェブ・モバイル・ゲーム開発者のためのアプリケーション開発ガイド

Programming HTML5 Canvas

その名の通り、HTML5 の Canvas 要素を使ったプログラミングについて、基礎から応用まで書かれている本です。簡単なテキスト編集エリアを Canvas 要素で作ったりしていて、おもしろく、勉強になります。Canvas 要素の API の使い方だけでなく、こういったものを作るにはどういう風にプログラムを書くと良いのか、という部分にまで踏み込んで解説しており、Canvas 要素を使ったプログラミング以外にも役立ちそうな知識が得られると思います。

Head Firstデザインパターン – 頭とからだで覚えるデザインパターンの基本

Head First デザインパターン

オブジェクト指向プログラミングのデザインパターンについて説明が書かれている本。デザインパターンだけではなく、オブジェクト指向プログラミングの設計原則も述べられていて、設計をする際に役立ちました。写真や図をふんだんに使って説明していて、かなりわかりやすいと思います。あと、本の内容と関連したジョークがたくさん書かれてておもしろい。名著らしいので、読んだことがある方も多いかも。

Web API The Good Parts

Web API The Good Parts

Web API の設計において、API の利用者がわかりやすく、メンテナンスをしやすく、かつ、セキュリティを考慮した設計をどのようにすれば良いのかということが書かれている本。様々な Web サービスの API を例として上げつつ、こういった設計にすれば良いというように説明しているので、説得力があり、わかりやすいです。API の設計に関しては、デザインパターンと同じように、すべての状況で正解となるものはないと思っているので、事例がたくさん挙げられているのは良いと思いました。この本は結構いろんな所で紹介されているのを見ますね。

おわりに

この三冊は読み終わった後も、設計や実装をする時に、内容を思い出したり、読み返したりしていて、役に立っていると感じています。特に「Head First デザインパターン」は、オブジェクト指向プログラミングすべてに適用できる考え方だと思うので、三冊の中では一番多くの人の役に立つ本じゃないかなと思います。他の二冊も、Canvas や Web API 設計に関わる人ならば、読んでおいて損はない本だと思います。

Objective-C でデリゲートメソッド内でコールバックできるようにする

Objective-C では非同期処理の完了後の処理をデリゲートメソッドで行うものがあります。デリゲートメソッドで処理するのは、単純なシステムなら問題ありませんが、システムが複雑になってくると、1つのデリゲートメソッド内に多くの条件分岐ができて、コードが読みにくくなる場合があります。こういった時にはデリゲートメソッドの代わりにコールバック関数を用いることで、コードがすっきりします。既に存在するライブラリが、非同期処理完了後の処理をデリゲートメソッドで実行するようにしている場合は、デリゲートメソッド内でコールバック関数を呼ぶようなラッパークラスを書く必要があります。コールバック関数については以前の投稿を読むと分かるかもしれません (Objective-C でコールバックを持つメソッドを実装する方法について)。

非同期処理完了後の処理をデリゲートメソッドで行うと以下のようになります。例として、WebSocket ライブラリの square/SocketRocket を用いています。今回は説明を簡単にするために、クライアントが送信したメッセージを受け取ったら、即座に同一のメッセージを返すサーバーを仮定します。

// メッセージ送信
- (void)sendMessage:(NSString *)message {
    [_socket send:message];
}

// メッセージ受信 (SocketRocket のデリゲートメソッド)
- (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(id)message {
    // メッセージ受信後の処理を書く
    NSLog(@"%@", message);
}

メッセージ受信後に1つの処理のみを行うのであれば上記で充分ですが、場合によって異なる種類の処理を行いたい場合は工夫をする必要があります。例えば、サーバーから受け取るメッセージの種類によって処理を分けたい場合は、サーバーから受け取ったメッセージ内に、type のようなパラメータを加えて、デリゲートメソッド内で条件分岐をするということが考えられます。また、上記の sendMessage メソッドを呼び出すクラスが複数あり、クラス毎にメッセージ受信後の処理が違うというような場合は、デリゲートメソッドのみでは対応できません。こういった場合は、NSNotificationCenter を使った通知で条件分岐をするとうまくいきます。また、sendMessage メソッドの引数でコールバック関数のポインタを渡すようにし、デリゲートメソッド内でそのコールバック関数を呼ぶことでも対応できます。今回は、デリゲートメソッド内でこのコールバック関数を呼ぶ方法を説明します。

デリゲートメソッド内でコールバック関数を呼ぶには、インスタンス変数にコールバック関数を保存する必要があります。また、非同期処理を行うメソッドは並列で実行される可能性があるため、コールバック関数は複数保存可能で、かつ、デリゲートメソッド内で対応するコールバック関数を判別可能であるべきです。これらは以下の様なコードで実現できます。ここでも、サーバーはクライアントから受け取ったメッセージを即座にそのまま返すと仮定します。また、NSDictionary と JSON の変換部分は擬似コードです。インスタンス変数の初期化部分も適当なので、自分で実装する時は書き換えることをおすすめします。

// インスタンス変数
@property NSMutableDictionary *callbacks;
@property int *callbackID;
// 初期化
- (void)initialize {
    _callbacks = [NSMutableDictionary dictionary];
    _callbackID = 0;
}

// メッセージ送信
- (void)sendMessage:(NSString *)message completion:(void (^)(NSString *))completion {
    // メッセージに _callbackID を含める
    NSDictionary *dic = @{@"message": message,
                         @"id": [NSNumber numberWithInt: _callbackID]};

    // _callbackID をキーとして、コールバック関数をインスタンス変数に保存
    [_callbacks setObject:completion forKey:[NSNumber numberWithInt:_callbackID]];
    _callbackID++;
    
    [_socket send:[dic json]]; // NSDictionary を JSON に変換して送信 (擬似コード)
}

// メッセージ受信 (SocketRocket のデリゲートメソッド)
- (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(id)message {
    NSDictionary *dic = [message dictionary]; // JSON を NSDictionary に変換 (擬似コード)
    
    // ID に対応するコールバック関数を取得
    void (^callback)(NSString *) = _callbacks[dic[@"id"]];

    // コールバック関数を呼ぶ
    callback(dic[@"message"]);
}

上記のコードのように、コールバック関数毎に一意な ID を割り当てることができ、デリゲートメソッド内でこの ID を取得することができれば、デリゲートメソッド内でコールバック関数を呼ぶことができます。今回の例では、インスタンス変数に NSMutableDictionary 型の変数 (callbacks) と int 型の変数 (callbackID) を用意してこれを行いました。新しいコールバック関数が与えられる度に、現在の callbackID の値をキーとして、コールバック関数を callbacks 内に保存しています (14行目)。また、サーバーへ送信するメッセージ内に callbackID の値を含めています (10-11行目)。サーバーはメッセージをそのまま返すので、メッセージに含めた callbackID の値をデリゲートメソッド内で取得することができます (25行目)。取得した callbackID を用いてコールバック関数を取得し、実行すれば完了です (25-28行目)。

今回はサーバーがメッセージをそのまま返すものだったのでこのままでは役に立ちませんが、あるパラメータに与えられた値のみはそのまま返すようなサーバーにすることで実用的になります。また、最終的にデリゲートメソッド内でコールバック関数を取得できれば良いので、考えれば色々なパターンで実装できそうです。

box-sizing: border-box; を指定している時の jQuery UI Resizable の調整方法

CSS の幅や高さ指定を直感的なものにしてくれる border-box というものがありますが、これを使うと jQuery UI の Resizable の挙動がおかしくなります。この問題は短くて単純なコードで解決することができます。

通常、CSS の width や height は HTML 要素のコンテンツのサイズを指定します。つまり、border や padding の値は width や height には含まれません。例えば、width が 100px で、左右の padding が 20 px の HTML 要素を表示してみると、見た目上の幅は 140px となるわけです。これは CSS を書いてると結構気持ち悪く感じることがあると思います(他の要素との組み合わせを考える時にめんどくさかったり)。

上記の問題(?)はCSS で、box-sizing: border-box; を指定することにより解決することができます。つまり、width や height の値を padding や border も含んだものとして指定できるようになります。先の例の width が 100px で、左右の padding が 20px の HTML 要素を考えると、見た目の幅が 100px となり、要素内のコンテンツの幅が 60px になります。

しかし、box-sizing を border-box にすると、jQuery UI の Resizable の挙動が気持ち悪くなります。具体的には、HTML 要素のサイズを変更するためにドラッグを開始すると、padding や border の値だけ大きさがずれます。このせいで、ドラッグ中のマウスポインタの座標と、HTML 要素の端が一致しないのですごく気持ち悪いです。

Resizable の border-box 問題に関しては、jQuery 側ではまだ対応されていないみたいなので、自分で対応してみました。以下がそのコードです。jQuery UI のコードを読み解くのは嫌だったので、普通な感じの解法です。$el が border-box と resizable の両方を適用している HTML 要素の jQuery オブジェクトです。

$el.on("resize", function(ev, ui) {
    // box-sizing: border-box; に対応
    ui.element.height(ui.size.height);
    ui.element.width(ui.size.width);
});

HTML 要素がリサイズされた時に発生するイベント “resize” に対してイベントハンドラを割り当て、この中で width や height の値を修正しています。”resize” イベントのイベントハンドラは第2引数に、ui オブジェクトが与えられます。ui.element はリサイズ対象の HTML 要素の jQuery オブジェクトで、ui.size はリサイズ後の HTML 要素のサイズを示しています。ui.size のそれぞれの値を ui.element の width、height 関数に与えるだけで border-box に対応することができます。これは、ui.size の値が padding や border の値を考慮していない値になっており、さらに、jQuery の width、height 関数は同様に padding や border の値を含まないのサイズを指定するようになっているためです(border-box っぽく指定したい時は css 関数で指定できます)。

border-box と Resizable を併用することで発生するサイズの問題は “resize” イベントのイベントハンドラで HTML 要素の正しいサイズを設定してやると解決できました。偶然だと思いますが、結構短いコードで解決できたのが面白いです。初めに真面目に padding と border を取得していたのは何だったのかと。

Objective-C でコールバックを持つメソッドを実装する方法について

非同期に結果が返ってくる処理を書く場合、Objective-C では @Protocol を定義して、デリゲートメソッド内で結果をもらうのが良いのかもしれませんが、JavaScript を書いてるとコールバックで結果を取得したいと考えてしまいます。この記事では、Objective-C でコールバック付きのメソッドを作る方法を解説します。(コールバックって呼んでいいのかわかりませんが)

メソッドの書き方

サーバーからデータを取得し、そのデータを UIImage として返すメソッドを例とします。以下がメソッドの簡単な書き方です。

// メソッド定義
- (void)requestImageWithURL:(NSURL *)url completion:(void (^)(UIImage *image, NSError *error))completionHandler;

 

// メソッド実装
- (void)requestImageWithURL:(NSURL *)url completion:(void (^)(UIImage *, NSError *))completionHandler
{
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    [NSURLConnection sendAsynchronousRequest:request queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
        if (connectionError) {
            completionHandler(nil, connectionError);
            return;
        }
        UIImage *image = [UIImage imageWithData:data];
        completionHandler(image, nil);
    }];
}

メソッドの定義

定義部分のポイントは completionHandler の書き方です。completionHandler の型に関数のようなものが書いてあります。これは関数のポインタを表しています。void が戻り値を表しており、^ がポインタであることを表しており、UIImage *image, NSError *error が引数を表しています。

このようにメソッドの引数に関数のポインタを渡すことにより、定義したメソッド内で、この関数を実行することができるようになります。この関数をメソッド内の非同期処理が終了したタイミングで呼び出せば、コールバック関数のように使うことができます。

メソッドの実装

非同期処理が終了したタイミングで仮引数の  completionHandler を実行しています。コールバック関数でメソッドの値を返す時は、コールバック関数の引数に返したい値を与えます。これにより、呼び出し元の関数の、実引数として与えられた関数内でメソッドが返す値にアクセスすることができます。

注意しなければならないのは completionHandler はObjective-C のメソッド呼び出しではなく、C言語の関数呼び出しのように書かなければならないということです。

おわりに

コールバックの概念は理解しづらいですが、理解できるといろいろと書けるようになるので、書き方は覚えておいて損はないと思います。関数のポインタはまだまだ色々と使い道がありそうです。

Node.js でデスクトップアプリを作れる node-webkit

node-webkit を使うことで、HTML や JavaScript でデスクトップアプリを作ることができるようです。既存の Web アプリをデスクトップアプリとして作ることも簡単にできそうです。

作り方は、フロントエンドの書き方 + Node.js といった風で、Node.js を書ける人ならば抵抗なく開発できそうに思います。ただ、ちゃんと考えて書かないとコードがグチャグチャになりそうですね。