分かりやすいコードの書き方
第8話:変更に強いコードの設計
(最終更新日:2025.4.30)
(絵が小さい場合はスマホを横に)
変更に強いコードへ!
「仕様変更が入った瞬間、あちこちのコードを書き直す羽目になった」
そんな経験はないだろうか。
分かりやすく、保守性が高いコードを目指すなら「変更に強いコード」という視点は欠かせない。
今回は、それを実現するための基本原則と設計アプローチを紹介する。
1.OCP(オープン・クローズドの原則)とは
OCPに関しては、以前もいくつか記事を書いている。 ここにリンクを貼る。 開放/閉鎖の原則(1)システムの拡張性を保つ設計方法、 開放/閉鎖の原則(2)継承とポリモーフィズムの活用。 この考え方を簡単に記すと「拡張に対しては開いているが、修正に対しては閉じているべき」になる。 つまり、新しい機能を追加するときは、既存のコードを変更せずに対応できるようにする。 そして、条件分岐を増やさずに、処理の追加は差し替えや継承・委譲で対応すると言うことだ。 言葉だけ聞くと分かりにくい。例で示そう。下記は悪い例になる。 この書き方だと、ユーザーの種類が増える度に関数を書き直さなくてはならない。
悪い例
次に良い例を示す。dictionaryを使って、ユーザーの種類と割引率を示している。 この書き方であれば、ユーザーの種類が増えたとしても関数自体を変える必要がない。 変えるのは、dictionaryの部分だけである。しかも、同様に種類と値を追加して書くだけなので、非常に分かりやすい。 これだけでもOCPの優位性は伝わるだろう。
良い例
2.関数型 vs 手続き型:どちらが変更に強いか
関数型と手続き型、どちらが変更に強いか。 この問いに対しては、関数型の方が強いと答える。 ただし、組み込みや状態制御を行う場合は、手続き型でないと実装が難しい場合が多い。 なので、臨機応変にコードを書いていけばよい。手続き型でも書き方次第で変更に強いコードにはなる。
関数型と手続き型の比較
タイプ | 関数型 | 手続き型 |
---|---|---|
状態管理 | 状態を持たない(不変) | グローバル変数や状態に依存しがち |
副作用 | 少ない(純粋関数) | 多くなりがち |
再利用性 | 関数合成でしやすい | 流れの中で再利用しにくい |
初期学習コスト | やや高い | 低い(慣れやすい) |
3.変更の影響範囲を最小限に抑える方法
変更の影響範囲を最小限に抑えるには、次の4点を押さえよう。 1、3、4は今までも良く言ってきたことだ。2はPythonのコードでは特に重要だ。 使い方も難しくないので、本項の1を参考に、ぜひマスターしよう。
- 関数を小さく・明確に分ける : 1つの関数の責任を小さくすることで、修正時の影響を限定
- 条件分岐をデータ化・構造化: if文を増やすより、辞書やマッピングで処理を抽象化
- インターフェースや抽象化に依存: 直接具体クラスを使わない。抽象に依存する
- 変更に備えたレイヤー構造にする: UI / ロジック / データアクセスを分離、クリーンアーキテクチャを使う
4.データ駆動設計とは
データ駆動設計とは「コードを書かずに振る舞いを変える」設計のことだ。 例えば、設定ファイル(JSON、YAML)でフローや閾値を変更する。 これにより、下記のメリットが得られる。
- 振る舞いを コード変更なしで切り替えられる
- 非エンジニアでも設定が可能
- テストや本番環境での運用フレンドリーな作りになる
実際にJavaScriptでデータ駆動設計すると、以下のようになる。 最初に定義したdiscountRulesの部分がデータ駆動になる。 これを引数にして、関数が機能する。 executiveというユーザータイプ(type)、割引率(rate)0.15という新たなルールを加えることだって容易だ。
データ駆動設計を用いた例
5.まとめ
今回は変更に強いコードについて紹介した。 OCPの法則、関数型の採用、条件分岐を避け、構造化・データかする、データ駆動設計という手段をとることで、 変更に強いコードを実現することができる。 そして、今回はOCPの中でもデータ駆動設計を中心に説明した。 データ駆動設計は、非エンジニアでもメンテナンス、拡張が可能である。ぜひ、これを機に使ってみよう。
▼参考図書、サイト
「リーダブルコード」 Dustin Boswell、Trevor Foucher 著、角 征典 訳 オライリー