addictionwhite’s diary

考え中のことを整理と忘備録のために綴ります。ここに書かれている考えは翌日には変わる可能性があります

オープン・クローズドの原則(開放/閉鎖原則)

solid原則メモ
間違っている解釈などありましたらコメントなどしていただけると助かります。


オープン・クローズドの原則(開放/閉鎖原則)
shkn.hatenablog.com

拡張に対して開かれている(Open)
あるモジュールが拡張可能である場合、そのモジュールは拡張に対して開かれている(Open)と言います。
修正に対して閉じている(Closed)
あるモジュールが修正に対してソースコードに影響を受けない場合、そのモジュールは修正に対して閉じている(Closed)と言います。

まとめると 「ソースコードを修正せずにモジュールを拡張可能にすべき」という意味になります。
つまるところソフトウェアの機能の拡張というのは、既存のソースコードの「変更」ではなく「追加」によってなされるべきだということです。

この一言に尽きると思う

例えばA店、B店の会計を表示する処理があったとして

function 会計を表示する処理(int $type)
{
    if($type == 1){
        // A店用の会計処理 
    }
    if($type == 2){
        //B店用の会計処理 
    }
    // 画面に表示
}

これだとC店用の処理を追加する場合、既存のコード(会計を表示する処理メソッド)の変更によってしか機能を拡張できない。
メソッドが以下のような形であれば、呼び出し側で新しいクラスを作成してメソッドに投げてやれることで機能を拡張できる
(既存の会計を表示する処理には手を加えなくていい)

function 会計を表示する処理(Store $store)
{
    $accountin = $store->aggregate() // 会計処理 
    $store->display($accountin) // 画面に表示
}

要はインターフェース、ポリモーフィズムを用いて新しいオブジェクトを生成して、
既存で用意された処理は、その投げられたオブジェクトを抽象的に捌く仕組みを作っておく
(それによって既存の処理は変更しなくても済むようにする)。

www.ritolab.com

既存の処理に手を加えるということは、
上の例でいうとC店での処理を追加したいだけなのにA店B店のロジックに手をつけることになる
(関心事はC店なのにA店B店の処理を壊すリスクが発生する)

変更が確実に発生しない不確かなものである以上、あらゆるケースに完璧に閉じることはできません。 この問題について、Robert C.Martin は「最初の鉛玉は甘んじて食らってみる」という面白い例えをしています。あらゆる要求に対応しようとするのではなく、予測不可能な部分については実際に変更が発生してからOCPを適用するという方針です。早まった設計を行わず、戦略的に閉じていくのが重要といえそうです。

早すぎる適用に気をつける。
最初から完璧な設計で固めようとしすぎない(それによる不必要に複雑になるリスクを意識する)。
最初から完璧をめざすというよりは、継続的にリファクタリングしていくことを考えたほうがよい動きができそう。
ただ、惰性で負債的なコードを追加する流れになりがちなので注意しないといけない。


konboi-kun.hatenablog.com
この記事もわかりやすい


備考
serverspecのツールもこの原則を適用して拡張性を高くしているとオライリーの本で読んだ
Serverspec P12 より引用
www.oreilly.co.jp

これはツールとしての拡張性を高めるという狙い以外にも、筆者にとって必要がない機能を実装しなくて済むようにするためという狙いがあります

もし自分がプラグイン公開する機会があれば、意識したいところ