🐸

Goで学ぶSOLID原則:開発工程から理解するオープン・クローズドの原則

に公開

承知しました!
先ほどの 「Goで学ぶSOLID原則:開発工程から理解する単一責任の原則」 の構成を参考にして、
同じトーン・フォーマットで オープン・クローズドの原則(OCP) を記事化しました。


Goで学ぶSOLID原則:開発工程から理解するオープン・クローズドの原則

ソフトウェア設計を学ぶ上で必ず登場するのが SOLID原則
今回はその中から オープン・クローズドの原則(OCP: Open-Closed Principle) を解説します。

本記事では、Go言語での具体例を交えながら、開発工程の観点でOCPを学んでいきます。

オープン・クローズドの原則とは?

OCPの定義は以下の通りです。

ソフトウェアの成果物は拡張に対して開かれていなければならないが、修正に対して閉じられていなければならない

つまり、新しい要件を満たすためには「追加」で対応できるべきであり、既存のコードを直接「修正」することは極力避けるべき、という考え方です。

これにより、既存機能を壊すリスクを最小限に抑えながら、ソフトウェアを進化させられます。

アクターの例え:デザイナーの業務拡張

現場をイメージするために、デザイナーの役割を例に考えてみましょう。

OCP違反の例

新しい業務が増えるたびに、Designer のメソッドを直接編集していくケースです。

type Designer struct{}

func (d Designer) DoWork() {
	fmt.Println("デザインを作成する")
	// 新しい要件が追加されるたびに追記していく
	fmt.Println("フロントエンド実装を行う")
	fmt.Println("マーケ前処理を行う")
}

この場合、新しい要件が来るたびに DoWork を修正する必要があります。
つまり「拡張=修正」になっており、OCP違反です。

OCPを守った例

インターフェースを使って業務を「追加」で表現できるようにします。

type Role interface {
    Work()
}

// 各業務を独立した構造体に分離
type Design struct{}
func (d Design) Work() { fmt.Println("デザインを作成する") }

type Coding struct{}
func (c Coding) Work() { fmt.Println("フロントエンド実装を行う") }

type Marketing struct{}
func (m Marketing) Work() { fmt.Println("マーケ前処理を行う") }

// デザイナーは複数の業務を「合成」して担う
type Designer struct {
	Roles []Role
}
func (d Designer) DoWork() {
	for _, r := range d.Roles {
		r.Work()
	}
}

利用する側は新しい業務を「追加」するだけで、既存のDesignerRoleを修正する必要はありません。

func main() {
	designer := Designer{
		Roles: []Role{Design{}, Coding{}, Marketing{}},
	}
	designer.DoWork()
}

これにより「拡張=追加」となり、OCPを守ることができます。

開発工程での学び

実際の現場でも、新しい要件は常に発生します。
そのたびに既存のコードを直接書き換えていたら、過去の仕様を壊すリスクが高まります。

  • 違反ケース: デザイナーの業務メソッドに、都度「コーディング」「マーケ」などを追記する。
  • 遵守ケース: 業務を役割ごとに分けておき、必要になったら「新しい役割を追加」するだけで対応する。

OCPとはつまり、「システムを壊さずに進化させる仕組み」と言えます。

まとめ

  • オープン・クローズドの原則とは「拡張に開かれ、修正に閉じられる」設計を目指すこと
  • 新しい要件は「追加」で対応し、既存コードの「修正」を極力避ける
  • 現実の開発工程でも、要件が増えても既存を壊さず進化できる設計が求められる

参考文献

* Robert C. Martin, Clean Architecture: A Craftsman’s Guide to Software Structure and Design, Prentice Hall, 2017

Discussion