前置き
コーディングの際、個人的には普段から私が実践している方法なのですが
意外と実践していない人を多く見かけるので今回は記事にしてみました。
仮に、商品一覧を表示する画面のサーバーサイドを実装するとします。
仕様書は以下のような、よくある内容のものです。
チェック処理
種別 | 項目 | 内容 |
---|---|---|
必須 | ユーザーID | |
必須 | 商品ID | |
桁 | ユーザーID | 1~3桁 |
桁 | 商品ID | 1~3桁 |
取得条件
値 | 条件 | テーブル | カラム |
---|---|---|---|
リクエスト.ユーザーID | = | ユーザーマスタ | ユーザーID |
リクエスト.商品ID | = | 商品マスタ | 商品ID |
さて、このような仕様をソースコードに起こしていくとします。
千差万別だとは思いますが、
みなさんはどういう手順を踏んで実装に移して行きますか?
木から森
まずはよくみかける手順です
①とりあえず処理を羅列
function getProductMaster(userId: number, productId: number) {
// DBから取得
return productMaster;
}
function main(userId: number, productId: number) {
if (!userId) { throw new Error(); }
if (!productId) { throw new Error(); }
if (userId < 0 || userId > 999) { throw new Error(); }
if (productId < 0 || productId > 999) { throw new Error(); }
let productMaster = this.getProductMaster(userId, productId);
return productMaster;
}
②ソースが長くなってきたところでメソッド化
function isValid(userId: number, productId: number) : boolean {
if (!userId) { return false; }
if (!productId) { return false; }
if (userId < 0 || userId > 999) { return false; }
if (productId < 0 || productId > 999) { return false; }
return true;
}
function getProductMaster(userId: number, productId: number) {
// DBから取得
return productMaster;
}
function main(userId: number, productId: number) {
if (!this.isValid(userId, productId)) { throw new Error(); }
let productMaster = this.getProductMaster(userId, productId);
return productMaster;
}
これでも悪いというわけではないのですが
今回の場合は、簡単な仕様なので処理の流れの全体像をすぐに把握することができます。
しかし、複雑な仕様をソースに起こしていく場合、
ただ上から順に処理を羅列していくと
ソース量が膨大になってきた時に
ソースコードの全体像が見えなくなり
ソースコードの森の中で迷子になってしまうやもしれません。
これは木から森という、
細かい粒度から大きい粒度の大枠が出来上がっていくために
だんだんと全体像が見えなくなっていき、森の中で迷子になってしまうのです。
そこで今度は処理の順(シーケンシャル)にソースを起こしていくのではなく、
まずは処理全体のアウトラインをつかみ、
先に大枠をソースに起こしておいて
森から木へと、外側から内側方向へとソースを起こしていくようにします。
森から木
①アウトラインだけの中身が空のメソッドを用意して、
処理の全体像を把握しやすくする
function isValid(userId: number, productId: number) : boolean {
// todo: チェック処理
return true;
}
function getProductMaster(userId: number, productId: number) {
// DBから取得
return productMaster;
}
function main(userId: number, productId: number) {
if (!this.isValid(userId, productId)) { throw new Error(); }
let productMaster = this.getProductMaster(userId, productId);
return productMaster;
}
②空のメソッドの中身を埋めていく
function isValid(userId: number, productId: number) : boolean {
if (!userId) { return false; }
if (!productId) { return false; }
if (userId < 0 || userId > 999) { return false; }
if (productId < 0 || productId > 999) { return false; }
return true;
}
function getProductMaster(userId: number, productId: number) {
// DBから取得
return productMaster;
}
function main(userId: number, productId: number) {
if (!this.isValid(userId, productId)) { throw new Error(); }
let productMaster = this.getProductMaster(userId, productId);
return productMaster;
}
こういった書き方をすると、だんだん処理が増えていくうちに
すごい膨大な1メソッドになっちゃった!
ということが自然と防げるために可読性の良いソースになっていきます。
もちろんそれでも単一責任法則を考慮し、
メソッドの債務が重責になってきた際には
後からメソッドの切り出しをしていくことは必要です。