現場で役立つシステム設計の原則 〜変更を楽で安全にするオブジェクト指向の実践技法〜 CHAPTER2 場合分けのロジックを整理する
増田 亨. 現場で役立つシステム設計の原則 〜変更を楽で安全にするオブジェクト指向の実践技法 (Japanese Edition). Kindle 版.
前回
プログラムを複雑にする「場合分け」のコード
区分や種別がコードを複雑にする
システムを実現するプログラムには様々な「区分」や「分類」(= つまり実コード上では Enum で表現されるものか)がある。
この「区分」「分類」に対して条件分岐を実装するが、複数の「区分」「分類」が入り組み、分岐が複雑になると修正が厄介になる。
そのため、変更しやすい実装となるように設計するにはどうすればよいか、という話。
判断や処理のロジックをメソッドに独立させる
「区分」「分類」もやはりオブジェクト指向の考え方で実装する。
「区分」「分類」を一つの塊としてメソッドに抽出し、関連するデータやロジックを一つのクラスにまとめる。
else句をなくすと条件分岐が単純になる
else をなくして条件分岐を単純化するとのことで、それには「早期リターン」が良いとのこと。
Java を書いていたときは結構意識してたな。
例では条件分岐結果を設定して最後に return するためのローカル変数を排除し、else を使わずに早期リターンを行う。
この「else を使わずに早期リターン」を「ガード節」と言うらしい。初めて知った。
また、早期リターンをしやすくするために予め条件文をメソッドに抽出しておくとよい。
複文は単文に分ける
if の中に if が入ったりと、「複文」は意図がわかりにくくなるので避ける。
ここでもやはり「ガード節」を使えばシンプルになる。
また、「ガード節」は疎結合なため順序を入れ替えても良い。
区分ごとのロジックを別クラスに分ける
区分ごとのクラスを同じ「型」として扱う
区分ごとにクラスを分けた結果、区分同士を使い分ける条件分岐を書くと、クラスに整理した意味がなくなってしまう。
そのためにインターフェイスを使って同じ型として扱うようにプログラミングしていく。
つまりポリモーフィズムのことか(「多態」)。この本を読んで気づいたのは、カタカナ名詞をなるべく使わないように説明しているところ。
区分ごとのクラスのインスタンスを生成する
Map を使って区分ごとのインスタンスを受け取るテクニック。
Javaの列挙型を使えばもっとかんたん
列挙型(Enum)と多態を組み合わせることによって、if文/Switch文でごちゃごちゃしがちなコードをすっきりさせることができる。
これは10数年前にいた現場で実際やっていたのだけど、そのときはまだ「多態」とかあまり言われてなかったかもしれない時代(あくまでも観測範囲で)だった。
しかし、こういうテクニックが設計手法として確立されていくんだなと思うなど。
区分ごとの業務ロジックを区分オブジェクトで分析し整理する
「区分オブジェクト」とは、区分ごとの業務ロジックを、区分ごとにクラスとして独立させたオブジェクトである。
業務ロジックの見通しを良くし、わかりやすく整理する。
こうすることで複雑さを分析・整理することができる。
状態の遷移ルールをわかりやすく記述する
状態遷移を管理することも重要な関心事である。状態の制約条件を管理するにも列挙型を使うと良い。
まず組み合わせをマトリクスで整理する。次にある状態から遷移可能な状態をSetで宣言する。宣言元を「キー」に、遷移可能な状態をSetとしたMapを宣言する。こうすることで、現在の状態がどの状態に遷移できるのかを整理することができる。
まとめ
業務要件から出てくる「区分」に対して愚直に条件分岐を書いた場合、あっという間にソースコードが複雑化していくという課題から、以下にして見通しの良いコードにするのかというテーマ。
曰く、業務要件から「区分」を整理し、メソッドやクラスにまとめ、さらにif文・Switch文を削減するために「インターフェイス、多態」「関心の分離」「区分オブジェクト」「列挙型」を利用するといった設計・実装をする。
こうすることで「区分」が増えた場合でも変更が容易になったりと変更にも強いソースになるのだと思う。
実はこういった列挙型や多態などのテクニックは十数年前に Java を書いていた時にはもう利用していた覚えがあるのだけど、こうしてきちんと言語化と知識として理解していた気はしない。
もちろん今は知識としても持っているのだが、再確認する意味でもとても良い CHAPTER だった。
次回
フィールド・オブ・ドリームス
社内でこのニュースが話題になり鑑賞。
初めは映画のワンシーンだと思ったら、これが「フィールド・オブ・ドリームス」を題材にした現実のイベントでめっちゃ良かったのだけど、実は映画を観たことがなかった。
イメージはマネーボールみたいな感じかなと思ったのだけど、90年代にあったような懐かしい造りの映画ですごく良かった。
80〜90年台はいつもテレビで映画を観ていたのでなんとなく吹き替えで観たのだけど、すごく懐かしい雰囲気だった。
このくらい古い映画は吹き替えで観ようかな。
現場で役立つシステム設計の原則 〜変更を楽で安全にするオブジェクト指向の実践技法〜 CHAPTER 1 小さくまとめてわかりやすくする
増田 亨. 現場で役立つシステム設計の原則 〜変更を楽で安全にするオブジェクト指向の実践技法 (Japanese Edition). Kindle 版.
恥ずかしながら今の会社に来てから初めて「ソフトウェア設計」というものに向き合った気がする。
しかし、これまで見様見真似や感覚でしかやってこなかったため、意思を疎通できなかった。
あるいは体系的に学び直すことで今後のエンジニア人生においても豊かになるのではと考え、この本を読んだのでまとめていく。
まとめ方は、この本の10あるCHATERの項目ごとに自分が考えた・感じ取ったことを書いていくことで、中身をしっかりと把握していくことにした。
なお、そのため目次だけは書いているが、以下に公開されているのでここに書いても大丈夫かなと判断。
https://gihyo.jp/book/2017/978-4-7741-9087-7
もちろん中身や詳細は買って読んでください。
なぜソフトウェアの変更は大変なのか
ソフトウェアの変更に立ち向かう
曰く、「ソフトウェア設計」を一言で表すと「どこに何が書いてあるのかわかりやすくする」。
これは設計に向き合うようになって初めに感じたことと一致して納得。
ソースコードも変数もメソッドもそれぞれ役割があって、どこに居るべきかが決まっていれば良く、それを関係者全員が同じように実現する ために必要なのが設計なんだなと。
変更が大変なプログラムの特徴
メソッドやクラスの大きさ、引数の多さが変更を大変にする。
実際プロダクトの一部で実際にこういうことがおこっていて、とにかく変数や条件分岐や無駄なステップが多く、そうなると関心事が増えて くるというのは体感である。
変更するたびに変更が大変になる
ちょっとした修正の積み重ねから、コードのじわじわと増えて構造が入れ組んでくる。
成長や機能改修が多い箇所はかなりの頻度でこういうことが起こりそうで、変更に強い(腐りにくい)ソースとなる設計が必要。
プログラムの変更が楽になる書き方
わかりやすい名前を使う
変数を一目瞭然の名称にするということで、現在、実際の業務で使っている変数名そのものが載っていてびっくりした。
もしかしたらシステムの種類、規模によっては英語圏の全世界共通で使えるユビキタス言語のようなものが実はあるのかもしれない。
というか、まあプログラミングは英数字を使うので、「英語の単語でこう書くよね」というのに則れば、そんな変な変数名にならないのかな。
長いメソッドは「段落」に分けて読みやすくする
ここら辺もリーダブルコードに書いてあった気がする。
目的ごとに変数を用意する
Scala では Immutable に実装すること(変数の値が変わらないように実装すること - Scala にはその仕組がある)を推奨していて、Immutabel に実装していけばこれは起こらない。
メソッドとして独立させる
「メソッドの抽出」はリファクタリングの第一歩。
異なるクラスの重複したコードをなくす
「メソッドの抽出」後は異なるクラス間で重複するコードをなくしていくとのこと。参照関係があれば書き換え、なければ別クラスに抽出するといった手順でリファクタリングしていく。
狭い関心事に特化したクラスにする
送料に関する値、メソッドだけ切り出して、「送料クラス」という小さな専用のクラスを作成する。これを「ドメインオブジェクト」と呼ぶ。
メソッドは短く、クラスは小さく
とにかく共通化と小分けしてコードの重複をなくすようにしていく。こうすることで変更箇所の特定が容易になるだけではなく、修正箇所が1過所になったりする。
小さなクラスでわかりやすく安全に
データとロジック
基本データ型を数値、日付、文字とし、プログラムの基本である演算(判断、加工、計算)を記述するのが業務ロジック。
この組み合わせをロジックの最小単位とする。
基本データ型の落とし穴
しかし、例えば数値の基本である int は-21億から+21億まであるので、一般的に金額項目に使用するのは危ない。
値の範囲を制限してプログラムをわかりやすく安全にする
そのため、「独自クラス(=例えば金額クラス)」の内部でバリデーションをかけることで、値の整合性を担保する。
このように、目的に特化した「型」を宣言することでソースコードは見違えるように意図が明確になり、動作が安定する。
「型」最高。
「値」を扱うための専用のクラスを作る
この「独自クラス」を「値オブジェクト(Value Object) 」と呼ぶ。
「値オブジェクト」は業務の用語そのもの。
DDD と親和性高そう。
「値オブジェクト」は「不変」にする
内部のインスタンス変数が変化しない不変な「値オブジェクト」は、副作用を減らしてバグが混入しにくくなる。
これを「完全コンストラクタ」と呼ぶ。
「値オブジェクト」を不変にするやりかたは
「型」を使ってコードをわかりやすく安全にする
「値オブジェクト」は業務の関心事を直接的に表現する。
「値オブジェクト」を作るには、実務で使っている具体的なデータを考えてみると良いとのこと。
例えば範囲が決まっている数値があったり、一定期間に収まっている必要があるなどの決め事があるはずで、妥当な範囲だけを扱うように設計する。
複雑さを閉じ込める
配列やコレクションはコードを複雑にする
コレクション型を扱うコードの整理
コレクション型を扱うロジックを専用クラスに閉じ込める
「値オブジェクト」のように「コレクション型」というものを作って、配列と業務ロジックを閉じ込めるテクニック。
コレクションのっデータとロジックを専用のクラスにして、オブジェクトを1つだけコレクションを持つ専用クラスを作る。
これを「コレクションオブジェクト」もしくは「ファーストクラスコレクション」と呼ぶ。
コレクションオブジェクトを安定させる
「値オブジェクト」と同様に、コレクションオブジェクトも不変にする。
コレクションオブジェクトは業務の関心事
コレクションオブジェクトの使い所として、例えば「売れ行きが好調な商品一覧」などに使用する。
まとめ
リーダブルコードに書いてあることもあれば、オブジェクト指向プログラミングの基礎から応用まであって振り返りのようなCHAPTERだった。
リファクタリングの基礎的な手順や、漠然とイメージしていた「ドメインオブジェクト」「値オブジェクト」「コレクションオブジェクト」などの理解が進んだ。