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 を書ける人ならば抵抗なく開発できそうに思います。ただ、ちゃんと考えて書かないとコードがグチャグチャになりそうですね。