hifiveではいわゆるMVCの他にロジックと呼ぶ機能があります。ロジックとは、
- 複雑な計算
- サーバ通信
- ローカルDB操作
など画面操作を含まない通信、計算処理を記述するものと定めています。
処理を外出しすることで、コントローラはビューとロジックの取り回しをするだけで良くなります。それによって可読性、保守性、再利用性、テストが容易になるといったメリットが生まれます。
図として描くと次のようになります。
ロジックは純粋に計算処理、DB周りの処理に特化することで複雑な処理を外出しできるようになります。
デモ
まずはデモで動きを見てみましょう。
このような画面が最初に表示されています。
検索ボタンを押すと、外部からデータを取得してそれを一覧として表示します。
コントローラー
では最初にコントローラのコードを見てみます。
var itemSearchController = {
__name: 'ItemSearchController',
__templates: 'template.ejs',
/**
* 商品検索ロジック
*/
itemSearchLogic: itemSearchLogic,
/**
* 検索ボタン押下アクション
*/
'#searchBtn click': function(context) {
var $resultDiv = this.$find('#resultList');
var that = this;
$resultDiv.empty();
// 画面で選択されたカテゴリ
var category = $('#select-category option:selected').val();
this.itemSearchLogic.getItemList(category)
.done(function(resultData) {
that.view.append($resultDiv, 'template1', {
listData: resultData
});
})
.fail(function(errMsg) {
alert('取得に失敗しました' + errMsg);
});
}
};
コントローラが行っている処理としては、ボタンが押されたイベントを取得し、 itemSearchLogic.getItemList を実行しています。ロジックはデフォルトで Deferred を持っていますので、done/fail/thenなどのキーチェーンメソッドが使えます。これにより、いわゆるコールバック地獄になるのを防止し、見通しの良いコードが書けるようになります。
ロジック
次のロジックのコードを見てみましょう。
var itemSearchLogic = {
__name: 'ItemSearchLogic',
/**
* 商品リスト(商品名と税込価格)を取得する。
*
* @param categoryId {Number} カテゴリID
* @returns 商品リスト
*/
getItemList: function(categoryId) {
var dfd = this.deferred();
var result = null;
this._getItemData(categoryId)
.done(function(data) {
result = $.map(data, function(obj) {
dfd.notify(data.length);
obj.price = Math.floor(obj.price * 1.05);
return obj;
});
dfd.resolve(result);
})
.fail(function(error) {
dfd.reject(error.message);
});
return dfd.promise();
},
/**
* カテゴリIDから商品(商品名と税抜価格)リストをサーバから取得する。
*
* @param categoryId {Number} カテゴリID
* @returns 商品リスト
*/
_getItemData: function(categoryId) {
return $.ajax({
type: 'GET',
dataType: 'json',
url: './itemList' + categoryId
});
}
};
ロジックの処理では最初に deferred を定義しています。後は核処理の状態によって resolve(成功)やfail(失敗)などのステータスを変更しています。非同期処理になりますので、あらかじめ dfd.promise() を返却しています。
ロジックで行っていることは、コントローラーが表示処理を行いやすいようにデータを取得、加工する処理になります。このメリットとしては、
- ロジックのメソッドがテストしやすい
- メソッド単位で再利用がしやすい
- コントローラのコードが短い
などがあります。ビューが絡むとテストがとても面倒になりますが、ロジックには表示処理がないのでユニットテストが書きやすく、品質も保証しやすくなります。また、似たような処理があった場合にコントローラ内部に処理があるとビューと結びついてしまって再利用がしづらいですが、ロジックは切り離されているのがメリットです。
表示処理を行うコントローラの中にデータの加工処理が入ると、コードがとても複雑になってしまいます。見通しの良いコードを保つためにもロジックを活用しましょう。
コントローラだけで行う場合
例えば今回のコードをコントローラだけで実装すると、このようになるかと思います。
'#searchBtn click': function(context) {
var $resultDiv = this.$find('#resultList');
var that = this;
$resultDiv.empty();
// 画面で選択されたカテゴリ
var category = $('#select-category option:selected').val();
$.ajax({
type: 'GET',
dataType: 'json',
url: './itemList' + category
})
.done(function(resultData) {
resultData = $.map(resultData, function(obj) {
dfd.notify(resultData.length);
obj.price = Math.floor(obj.price * 1.05);
return obj;
});
that.view.append($resultDiv, 'template1', {
listData: resultData
});
})
.fail(function(errMsg) {
alert('取得に失敗しました' + errMsg);
});
単純な処理なのでまだ見通しは悪くありませんが、商品リストを取得する処理を他でも再利用したいとなったらどうでしょう。その場合、同じテンプレートを使うとは限りません。Deferredは非同期処理なのでうまく書き出すのも難しいかもしれません。そういった後々の行程を考えるとあらかじめコントローラとロジックに分離しておくメリットが分かってもらえるのではないでしょうか。
今回のデモはGitHub上にアップしてあります(logic.html)。ぜひ動作を確認してください。