2012-02-13
AndroidアプリにStrutsのようなコントローラを導入し,画面制御させるサンプルコード (の試作品。バリデーションやビジネスロジックの骨組み)
Androidアプリの設計に,Strutsのようなアーキテクチャを取り入れよう。という記事。
ただし,Javaにつきものの「XML地獄」は,徹底的に避けるものとする。
いかにシンプルにAndroidアプリの画面制御を管理するか?
Androidアプリは,複数の「Activity」(=画面)を持つ。
それらActivityどうしの間を行き来するためには,Intentを発行する。
通常,Intentの発行処理は,遷移元のActivityの中に書いてしまう。
Activityの中に,遷移元と遷移先の情報が埋め込まれるのだ。
そうすると,下記のような「密な結合」が生じてしまう。
なんとも網目状で,カオスな結合状態だ。
これは,オブジェクト指向的にはダメである。
「疎結合」なクラス設計になっていない。
むしろ,Activityにはそういった情報を埋め込まずに,
画面遷移に関る処理を集約して,1つの「コントローラ」クラスに任せる。
すると,下記のような「スター状」の結合になる。
ビューと制御の分離である。
コントローラクラスは,制御について責任を負う。バリデーションなども引き受ける。
Activityは,個別の画面におけるUI描画・イベントのハンドリングなどについて責任を負う。
どちらがよい設計か?
小規模アプリなら,前者で事足りる。
しかし規模が複雑になってくると,画面制御の洗い出しに苦労するものだ。
そして,思わぬところに画面遷移の実装漏れ・不整合が見つかったりして,テスト工程の負荷が予想外に増える。
ちょうど,Strutsフレームワークで,struts-config.xmlが
画面遷移やフォームのバリデーションに関する設定を引き受けていたのを思い出そう。
同じように,Androidアプリの「コントローラ層」の情報の集約を行なうことで
アプリ開発の保守性・生産性・拡張性・テスタビリティ等を向上させたい。
そのようなサンプルコードが完成し,Android実機上で実際に動作した。
試作品のレベルだが,下記で紹介する。
(1)コントローラ層を簡潔に,シンプルにコーディング
DSLを提供するライブラリを無視すると,ユーザが手を触れるのは,わずかな量のコードだ。
以下の4つだけ。
- アクティビティ本体
- コントローラ
- バリデータ
- ビジネスロジック
2012-02-10
Androidアプリの画面レイアウトを,まるでjQueryのようなコードで動的構築できるライブラリ (の試作品。UIコーディングのためのDSL)
まるでjQueryのような簡潔なコードによって,
AndroidアプリのUI・レイアウトをコーディングできるようなJavaライブラリ。
その試作に成功した。
アクティビティ中で,下記のようなコードが書ける。
// 動的に定義したViewは,これらのフィールドに代入される。 MButton button1; MLinearLayout layout1; MTextView tv1; MEditText et1; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // レイアウトを動的に描画する。 new UIBuilder(context) .add( // Buttonを定義 button1 = new MButton(context) .text("ボタン1") .click(new OnClickListener(){ // クリックイベント @Override public void onClick(View v) { // イベント発生時,他のViewの値を参照可能 Toast.makeText( context, "ボタン1が押されました。入力値:" + et1.getText(), Toast.LENGTH_LONG ).show(); } }) ) .add( // LinearLayoutを定義 layout1 = new MLinearLayout(context) .orientationHorizontal() // 水平に並べる .widthFillParent() .add( // TextViewを定義 tv1 = new MTextView(context) .text("ラベル" ) .widthWrapContent() , // EditTextを定義 et1 = new MEditText(context) .widthPx(200) ) ) .display(); // 表示する }
上記のコードを実行すると,下記の画面キャプチャのようなレイアウトとなる。
ガチガチの静的型付け言語である Java 上であるにも関らず,
バリバリの動的言語である JavaScript や jQuery っぽいコーディングができる。
少し夢みたいな話だ。
しかし,これは現に実現した。
このライブラリの基本的なアイデア,およびその内部のコードの一部抜粋を,下記に記載する。
着想・企画
Androidアプリを開発する際,レイアウトXMLをチマチマ編集するのは,極めて面倒だ。
2012-02-09
制御しやすい「デバッグ用ロガー」を自作して,サクサク開発 (Javaで,メソッド名を含めログ出力する方法のサンプル)
開発中,デバッグ用のログ出力機能は,できるだけ役立って欲しい。
プログラマにとって役立つような情報を,最大限のボリュームまで引き出してログ出力したい。
そのために,Javaで簡易ロガーを自作する際,
下記の2つの要素を駆使してみるのはどうか?
- スタックトレースAPI
- 独自アノテーション
スタックトレースを使えば,ログ出力しようとしているオブジェクト自身の情報が分かる。*1
つまり,「呼び出し元」のクラス名やメソッド名を,ログに記載できる。
(これは,Log4jをラップする独自ロガークラスなんかを作った時,よく初回で苦戦する罠だろう。)
また,独自アノテーションを定義すれば,
特定のクラスや特定のメソッドに対して,自由に「メタ属性」を付与できる。
その属性を見ることによって,クラスやメソッドの振る舞いを容易に制御できる。
- 「このクラスはもう十分,単体テスト済みなので,このクラスだけはデバッグログを出さないようにしたい。」
とか。
そうすれば,結合レベルの試験段階で,作業負荷を軽減できる見込みがある。
上記の指針に基づいて,ごくシンプルなクラス設計を行ない,
下記にサンプルコードと実行結果を示す。
※また末尾には,おまけとして,ラテン語の小ネタを掲載する。
サンプルコード
主要な役者となるクラスは,3つ。
- メイン処理
- 独自ロガー
- 独自アノテーション
*1:JavaScriptでいうと,arguments.callee.callerみたいなものだ。残念ながらJSでは,Functionオブジェクトの名前までは取得できないし,関数のレシーバを参照するすべも無いが…。
2012-02-05
Javaの非同期処理を,シングルスレッドのようにシンプルにコーディングするための設計パターン (並列処理を逐次処理にする)
java, 設計, マルチスレッド, javascript |
マルチスレッドの処理を,シングルスレッドであるかのようにコーディングしたい場合がある。
1番目の非同期タスクの処理結果を,2番目の非同期タスクが利用する場合など。
つまり,並列化されたタスクを,取扱い上は「逐次化」したいのだ。
まずは手っ取り早く,やりたい事をUMLで表現しよう。
「非同期タスクの連鎖」を実装する際,
しばしば下記のような「コールバックの入れ子」が生まれる。
その結果,メインの処理フローが見失われてしまう。
「非同期タスクが連鎖している」という状況が,ソースコードから一目で伝わらないのだ。
この状況を改善するために,下記のようなシーケンス図に作り変えたい。
※ダイアグラム中で,矢印の先っぽが「同期呼び出し」と「非同期呼び出し」の違いを表している点に注意。
こうすれば,「非同期タスクが連鎖している」という状況が,ソースコードからも,クラス設計からも,一目で伝わるようになる。
メインの処理フローが,明示的にわかるようになるのだ。
そうすれば設計しやすくなり,実装しやすくなり,テストしやすくなり,保守しやすくなる。
加えて,後者の方がよりオブジェクト指向な設計だ。
- 個々のタスク自身は,自分自身のタスクに専念すればよい。(Mind your own Business!)
- どのタスクがどの順番で呼ばれるか?どう連携させるか?といった「制御フロー」に関する情報は,制御用の親クラスに任せる。(いわば,タスクマネージャ)
残念ながら,前者の「コールバックの入れ子が延々と続くので,処理を追いかけづらい」スタイルのコードのほうを,よく見かけるのでは。
この点は,JavaやJavaScriptなど,非同期タスクをサポートしているプログラミング言語であれば何でも問題になり得る*1。
Javaの場合,言語レベルでsynchronizedブロックをサポートしているため,
- スレッド間で「同期」を取れば簡単では?
と思うかもしれない。
しかし,synchronizedは乱用できるものではない。安易に使うとデッドロックを生むので,慎重な考察が必要になる。
ここでは,Javaで「synchronized」キーワードを使わずに,
複数の非同期タスクを「逐次的」にコーディングする方法を示す。
まず,「逐次的」とはどういうものか説明する。
そして,上述の「タスクマネージャ型」のシーケンス図に倣い,Javaでのマルチスレッド・プログラミングのコードをシンプルにするような設計技法を1つ示す。
- (1)処理の「逐次化」とは?
- (2)JavaScriptでの「逐次化」の具体例
- (3)Javaでの「並列化」がスパゲッティコードを生みかねない例
- (4)Javaで,複数の非同期タスクを「逐次化」する設計パターン例
(1)処理の「逐次化」とは?
*1:JavaScriptを例に挙げたが,Javaと違ってJavaScriptがシングルスレッドである,という事実はお忘れなく。 http://d.hatena.ne.jp/language_and_engineering/20090614/p1