« Ubuntu13.04(amd64)上でのAndroid開発環境セットアップメモ | トップページ | CentOS7でgitサーバ(gitプロトコル)を立てる »

2013/06/15

金魚の糞アンチパターン(非同期処理の逐次化)

GWTではサーバとのやりとりを全ていわゆるAjax Requestで行います。従って非同期処理がコード中に多くは入り込むことになります。

ある非同期要求を行い、その応答処理を行った後で次の処理に進みたい、と言うような状況はしばしば発生します。
例えば、画面起動時に、起動処理として画面に表示するリソースを全てサーバから取得したのち、ユーザからの画面操作を受け付けるようにしたい、といった場合が該当します。

ここで、画面起動時に、2種類のデータA,Bを読み込み、読み込みが完了した後ボタンを押せるようにする、という実装を考えます。
単純に考えると以下の手順になると思います(サンプルコードはこちら)。
  1. データをもらうために、2回サーバとやりとりを行うので2種類のAsycCallback実装クラスを作ります(LoadCountryクラスLoadFruitクラス)。
  2. 2回のやりとりが終わった、というのを確実にするために、最初にデータAのリクエストを行い、その応答を処理した後デデータBのリクエストを行うようにします(LoadCountry#onSuccess)。そしてデータBの応答を処理した後、最終処理を実行します(LoadFruit#onSuccess)。
  3. 2.で実装した処理を開始します。すなわち、データAのリクエストを行います(47行目)。
上記の形で実装した場合、コード上から見える処理のかたまりは下図のようになります。


このような実装には以下の問題点があります。
  • やりたい処理のかたまりと、実装上のかたまりが一致しない。
    • 「Aの取得要求」と「取得したAの表示」は1まとまりにしたいが実装上分離してしまう。
    • 「取得したAの表示」と「Bの取得要求」処理には本来関連が無いが1メソッド内にまとまってしまう。
  • データAに関する処理とデータBに関する処理は本来前後関係や依存関係が無いにも関わらず、関係があるような表現になってしまう。
  • データA,Bに関してはそれぞれ独立して処理してよいにも関わらず、データAの処理が終わった後でないとデータBの処理に進まない。
以上のような問題があるため、前述した形の実装のことを、だらだらと続くコールバックオブジェクトを見立てて個人的に「金魚の糞アンチパターン」と呼んでいます。


ではどのように実装すべきでしょうか。本来やりたいことを図示してみます。


  • データA,Bに関する処理はそれぞれ独立している。(必須ではないが)並行して処理しても構わない。
  • ただし、起動完了処理はデータAに関する処理、データBに関する処理の双方が終了した後に実行されなければならない。

さてここで、上記のような要求はAjaxプログラミングではありがちな要求なはずなので、調べれば似たような機能のライブラリが乱立してるだろうし、それを真似て実現しようとしたのですが、調べ方が悪いのか見つからなかったので自分で実装してみました(注: 本当に調べ方が悪かっただけで、後で調べ直すといくつか見つかりました。後述)。

まず、上図にある矢印をTaskとしてクラス化しました。
次に上図の横線、これは後続タスクの実行開始タイミングを表していますが、これをBarとしてクラス化しました。
Barには前提Taskと後続Taskを登録し、前提Taskが全て終了したら後続Taskを実行するようにします。

この仕組みで実装し直したのがこちらの49行目以降になります。

要求と応答処理がひとつのTask継承クラスにまとまり、各Task間には依存関係がなくなりました。依存関係(処理フロー)はTaskとは別のクラスであるBarが管理するようになっています。


補足:
1)
上記のサンプル実装ではいくつか問題点があり、実際に適用しようとした場合には以下の点を解消する必要があるかと思います。
  • エラーハンドリング。具体的にはAsyncCallback#onFailureの扱い。これはさほど難しいことでは無いと思います。
  • Task間のデータ受け渡し型(ちなみに上の例ではTask間でデータのやりとりは発生していませんので説明には含まれていません)。今回の実装では、Barが後続Taskを開始する際に、前提TaskからもらったオブジェクトをList<?>型で渡すようになっています。このため型情報が消え、またリストに入っている順番もBarにTaskを登録した順になるので、受け取った方からすると扱いづらいです。
  • サービス呼び出し(Ajax request)の最大同時実行数制御。Webブラウザ側でも抑制するようになっているそうなので、その機構に任せてしまっても良いように思われますが、どうなんでしょうね…
  • フロー全体を管理している人がいないので、例えば進捗状況を確認したい、フローを外部から中断させたい、といった事を実現することがこのままでは不可能。
2)
ほぼ同じ問題について解説されているエントリを書かれている方がいらっしゃいました。今回のエントリ読んでもお前が問題としている点がよくわからん、という場合にはこちらを参照されると理解が深まるかもしれません。
私が金魚の糞アンチパターンと呼んでいるものを、こちらのエントリでは『「コールバックの入れ子が延々と続くので,処理を追いかけづらい」スタイルのコード』と表現されています。今後、上品に言う必要がある状況では私もこの表現を使わせて頂きます…

3)
本文中に、「JavaScriptでこの問題を解決するライブラリが見つからなかった」と記載していますが、実際にはやはり複数存在するようです。まだ全然中身を見ていないのでどんな機能を持っているのかは分かっていないのですが(全然関係ない可能性も)、メモとして名前だけ載せておきます。
(補足2で紹介したエントリ中にStratifiedJSの名前を見つけて、Google検索で芋づる式に発見しました…)
  • StratifiedJS
  • JSDeferred
  • jQuery.Deferred
  • CommonJSのPromises/A (これは実装ではなく仕様の名称?)
また、GWT上でjQueryの機能を提供しようとしているライブラリGQuery(gwtQuery)というものがあるようで、GQuery.Deferredで今回の問題を解決できたりするのかもしれません。


追記:
こういう形のコードのことをJavaScript界隈では"Pyramid of Doom"とか"Callback Pyramid of Doom"とか呼ぶそうですね。

« Ubuntu13.04(amd64)上でのAndroid開発環境セットアップメモ | トップページ | CentOS7でgitサーバ(gitプロトコル)を立てる »

コメント

コメントを書く

(ウェブ上には掲載しません)

トラックバック

この記事のトラックバックURL:
http://app.cocolog-nifty.com/t/trackback/18902/57600057

この記事へのトラックバック一覧です: 金魚の糞アンチパターン(非同期処理の逐次化):

« Ubuntu13.04(amd64)上でのAndroid開発環境セットアップメモ | トップページ | CentOS7でgitサーバ(gitプロトコル)を立てる »

other sites

  • follow us in feedly
  • github
  • stackoverflow

ソフトウェアエンジニアとして影響を受けた書籍

  • Christain Bauer: HIBERNATE イン アクション

    Christain Bauer: HIBERNATE イン アクション
    理論と実践が双方とも素晴らしい製品であるHibernate。本書はそのプロダクトを書名に冠していますが、Hibernateを使うつもりがなく、ORマッピングの解説書として読むにしても十分な良書です。Second EditionとしてJava Persistence With Hibernateという書籍も出版されていますが、残念ながら現在のところ 和訳はされていません。-インアクションは2.xの、Java Persistence-は3.1の頃のものなので、最新版とはちょっと違うところもあることに注意。 (★★★★★)

  • アンドリュー・S・タネンバウム: 分散システム 原理とパラダイム 第2版

    アンドリュー・S・タネンバウム: 分散システム 原理とパラダイム 第2版
    クライアント/サーバシステムを構築する上で必要となる知識が総論されてます。Web技術者も、フレームワーク部分を開発するのであれば必読。 (★★★★★)

  • Joel Spolsky∥著: ジョエル・オン・ソフトウェア

    Joel Spolsky∥著: ジョエル・オン・ソフトウェア
    前述の書籍「ソフトウエア開発プロフェッショナル」をより砕いたもの、という感じでしょうか。 前書きではプログラマでなくSE向けの本のように書かれているが、プログラマが読んでも面白い本であると思われます。 SEになった新人(あるいはそういう会社に入る/入りたての人)にとっては、これからどういったことが仕事を遂行していく上で起こりえるのか、どのように考えて行なっていけばいいのか決定する助けになると思います。 元は″Joel on Software″というブログの記事で、web上でも一部日本語で読めます。 http://japanese.joelonsoftware.com/ (★★★)

  • ドナルド・C・ゴース,ジェラルド・M・ワインバーグ: ライト、ついてますか

    ドナルド・C・ゴース,ジェラルド・M・ワインバーグ: ライト、ついてますか
    問題解決(一昔前のの流行語で言うところの『ソリューション』)能力は、システムエンジニアのスキルとして備えるべきもののうちのひとつです。しかし、これは難しい。学校で出されるテストと違い、唯一の、(問題提出者が想定している)解を求めるだけが「問題解決」では無いからです。そもそも、何が問題なのか、それは本当に問題なのか、それは本当に解決すべき問題なのか、その問題解決方法は正しいのか、などを解決しなければ、「その解は正しいのか」に辿りつくことができません。この本の最も良いところのひとつは、本があまり厚くないこと。すぐに読めるし、何回も読み返す気になるでしょう。 (★★★★★)

  • スティーブ・マコネル: ソフトウエア開発プロフェッショナル

    スティーブ・マコネル: ソフトウエア開発プロフェッショナル
    コードコンプリートで有名なスティーブマコネルの著書。新人SEに読んで欲しい。個人として業界の中でどうあるべきか、組織としてどうあるべきか、SEのプロ意識とは?SEの心構え概論、といったところでしょうか。また、業界における資格の重要性についても説かれています。この業界では資格が特に軽んじられる傾向がありますが、この傾向はどんな弊害をもたらすのか、将来的にこの業界は資格に対してどのような姿勢で臨んでいくべきなのか。日経BP社では(他の出版社もだが)最近、似たような類いのあまり面白くない書籍が乱出版されていますが、この本は別格だと思うので安心して購入して欲しいと思います。 (★★★★★)

無料ブログはココログ