ギルドワークスの増田です。
前回if文の条件式の書き方あれこれに書いた内容の続編です。
if文の条件式で論理演算式をべた書きしていた部分を、メソッドに抽出し、さらに、そのメソッドを、演算対象のデータを持つServiceDateクラスに移動しました。
設計改善前(論理演算式をべた書き)
if(date.isBefore(SUMMER_START)||date.isAfter(SUMMER_END))
...
設計改善後
if(date.isNotSummer())
...
//ServiceDateクラスにロジックを移動
class ServiceDate
{
private final LocalDate date;
boolean isNotSummer()
{
return date.isBefore(SUMMER_START)||date.isAfter(SUMMER_END);
}
}
- if文の条件式で、やりたいことが明確になりました(メソッド名で目的を表現)
- 季節判断のロジックをServiceDateクラスに一元化できました(あちこちに同じ演算式が重複しない)
季節の判断ロジックの変更が必要になった時に、プログラムのあちこちを調べたり変更する必要はありません。ServiceDateクラスのisNotSummer()メソッドの内容を変更しても、変更の影響は、このクラスに限定できます。
この設計改善により、ソフトウェアの変更コストが下がったわけです。
isNotSummer()の"Not"ってどうなの?
前回の記事を読んだ感想として、isNotSummer()というメソッド名はわかりにくい、というフィードバックをいくつもいただきました。設計として問題があるだろうと。
その通りですね。 "Not"がメソッド名に入っているのは、不自然でわかりにくいと思います。
isNotSummer()になっているのは、リファクタリング前のコードの論理演算式が原因です。
date.isBefore(SUMMER_START)||date.isAfter(SUMMER_END)
この論理演算がtrueになるのは、次の場合ですね。
または
つまり、この演算は「夏ではない」ことを判断しています。
メソッドに抽出すれば、メソッド名はisNotSummer()になる、というわけです。
isNotSummer()という名前にしてみると、いかにも"Not"がわかりにくい。
つまり、この論理演算式の判定ロジック自体が元々、不自然でわかりにくかったのです。
isSummer()にしてみる
isNotSummer()ではなく、isSummer()にしてみましょう。
boolean isSummer()
{
if( date.isEqual(SUMMER_START)) return true;
else if( date.isEqual(SUMMER_END)) return true;
else if(date.isAfter(SUMMER_START)&&date.isBefore(SUMMER_END)) return true;
else return false;
}
isNotSummer()に比べて、ずいぶん入り組んできました。
LocalDateクラスのAPIの日付を比較する三つメソッド isEqual()/isAfter()/isBefore() で「夏」を判断するには、こんなロジックになってしまうわけです。
SQL文であれば、 date BETWEEN summerstart AND summerend で、シンプルに表現できます。JavaのLocalDateクラスだと、APIの仕様として、開始日から終了日まで、というロジックをすんなり表現できません。
利用者の関心事とコードのねじれ
利用者の関心事である「夏だったら」を素直にisSummer()で表現しようとしても、論理演算式は、プログラミング言語の仕様上、ねじった書き方になります。
- APIの都合を重視してシンプルに書けば、isNotSummer()になる。 (メソッド名がわかりにくい)
- 利用者の関心事を重視すれば、isSummer()になる。 (論理演算式は複雑になる)
どちらを設計スタイルが良いのでしょうか?
私は、後者のスタイルのほうが良いと思っています。
ソフトウェアは、利用者の関心事に合わせて設計すべきです。
クラス名やメソッド名は、利用者の関心事に合わせるべきです。プログラミング言語の都合は、メソッドの内部に押し込めて、隠ぺいします。
利用者の関心事で設計を駆動する
利用者の関心事を重視した設計をすれば、プログラムの全体の構造が、利用者の関心事の構造を反映したものになります。
利用者の関心事をソフトウェア設計に反映すると以下の利点があります。
- 利用者が期待していること、要求していることが、プログラムのどこに書かれているかわかりやすい
- 変更依頼があった時に、変更すべき箇所がわかりやすい
- プログラムを変更した時の影響範囲が、業務上の影響範囲と一致する
こういう構造のソフトウェアのほうが、コードが読みやすく、変更が楽で安全です。
if文のちょっとした書き方の違いです。私は、このちょっとした違いがドメイン駆動設計の基本コンセプトだと理解しています。
利用者の関心事を良く理解して、その概念や構造を、できるだけ、ソフトウェアの構造、パッケージ名/クラス名/メソッド名/変数名に反映する。実装上の都合はできるだけ隠ぺいする。
それが、役に立つソフトウェアを作り、かつ、ソフトウェアの変更を楽で安全にする、ソフトウェア設計の基本なんです。
isNotSummer()よりisSummer()の方が良い。この当たり前の感覚こそ、ドメイン駆動設計の原点です。
補足
isSummer()メソッドの、if-else if-else if- else は、もうちょっとなんとかしたいですね。
「『場合分け』の書き方あれこれ」ではこのisSummer()のif文を題材に、条件式をもっとシンプルにするためのリファクタリングテクニックを紹介しています。
新装版 リファクタリング―既存のコードを安全に改善する― (OBJECT TECHNOLOGY SERIES)
※注意:この記事は2014年10月2日にGuildWorks Blogで公開したエントリをリライトしたものです。
Photo credit:https://www.flickr.com/photos/is_kyoto_jp/7844032822/
1
取り消す
この記事に共感したら、何度でも押してこの記事のポイントをみんなでアップしよう。