プログラミングマガジン

プログラミングを中心にIT技術をできるだけわかりやすくまとめます。

  • ホーム
  • オブジェクト指向
  • 【オブジェクト指向】「オープン・クローズドの原則」とは
 
 
     
  • サーバー言語  
    • Python
    • Ruby
    • PHP
    • SQL
  •  
  • インフラ  
       
    • AWS
    •  
    • 基本
    • Git
  • Web
       
    • Web開発
    • JavaScript
    • Vue.js
    • React
  •  
  • 設計  
       
    • 実装設計
    • DB設計
  • 問い合わせ
  

【オブジェクト指向】「オープン・クローズドの原則」とは

08.28

  • miyabisan2
  • コメントを書く

この記事は3分で読めます

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

「機能を拡張する」(オープン)と「修正を行わない」(クローズド)という一見矛盾するこの二つを同時に要求する内容のこと。

要は。。。

モジュールに新たな振る舞いを追加する際に既存コードを修正せずに新しいコードを追加するだけで目的を達成できる状態になっていることです。

全ての場合に適用するべきなの?

バリエーションを起因する場合のみが適用の範囲となる。

なぜこの原則を守った方が良いのか?

既存のクラスにif文を追加していく形だと元のクラスも再度問題ないかテストをしなければならなくなります。拡張した部分のクラスだけテストすれば良い状況が実装工数的には望ましいためです。

また、何か新システムにバグがあったときにこの原則を守っていればすぐにリカバリも可能です。

準拠させるポイント

  • バリエーションによって変化する部分はどこか
  • バリエーションの軸に沿ってまとめられているか

バリエーションの軸に沿ってクラス化する。

例えば、「フォームの種類」のバリエーションが多く増えるのであれば、それごとにクラスを分ける。

よく開発時に対象となりやすいバリエーションの例

インフラ層であれば

  • データストア(Oracle、SQLServer、NoSQL、CSV、他のPCとの通信など)
  • データストアのバージョン(Oracle11、Oracle12など)

ドメイン(業務)層であれば

  • 会員ごとに処理を変えるケース
  • 決済手法ごと(現金、キャッシュカード、PayPayなど)
  • フォームの種類ごとに処理を変えるケース

概念

オープン

機能を拡張できる。

クローズド

修正を行わない。

オープンクローズドの原則の注意点

扱う業務や場合によっては、この原則に愚直に従うと非常にクラスの数が増えすぎてしまいますし、コードの可読性が下がります。

使うなら適量を守って使う。

適量を守って使うことが重要でしょう。例えば以下のようなケースではインターフェースにする必要もないのでオーバースペックな実装であると言えるでしょう。(アジャイルソフトウェアの奥義では「早まった抽象化をしないことも抽象化をすることと同じくらい大事」とも書かれています。)

  • インターフェースがあるのに実装クラスが一つしかない場合

あえてオブジェクト指向を使わないという選択肢

基本的な処理のアルゴリズムは同じで固定値だけ変わるなどと行った場合は経験上無理にオブジェクト指向は使わずにバリエーションをコレクション(Map)を使って実装する方が良いです。(あるバリエーションでは1%、あるバリエーションでは5%など程度の差分しかない場合などでそれが何十個もあるケースなど)

なぜなら、こうした固定値の値とかはバリエーションの数が多すぎるのでソースの数が増えすぎるためです。

Mapに対して一つのクラスで処理を記述する方がクラスの数も増えず、条件分岐も増えずにスッキリしたソースになります。

呼ぶ側の分岐を増やさない対策

条件によって呼ぶクラス(バリエーション)が変わる場合

例えば、条件1の場合はクラスA、条件2の場合はクラスBを呼ぶみたいな感じになる場合です。

インターフェース、抽象クラス、ファクトリクラスを使う。

以下の例であれば、呼ぶ側のクラスである「formClient」というクラスにおいてIFormというインターフェースを呼び出します。ただ、どうしてもどのクラスを呼び出すかの判断は必要になるので、その場合はファクトリクラス(デザインパターンの一つ)を作って呼び出すようにします。

具体的な実装のポイント

インターフェース

ストラテジーパターンになります。

インターフェースを使って同一視させることで、無駄な変数の数を減らすことができます。

同一視できてない場合

1
2
3
4
5
if (発注フォームの場合){
  const hattyuform:HattyuForm = new HattyuForm();
} else (受注フォームの場合) {
  const jutyuuform:JutyuForm = new JutyuForm();
}

同一視できている場合

1
2
3
4
5
6
var form:IForm;
if (発注フォームの場合){
  form = new HattyuForm();
} else (受注フォームの場合) {
  form = new JutyuForm();
}

また、クラスの生成自体はファクトリークラスなどにやらせるとソースがスッキリします。

抽象クラス

テンプレートパターンになります。

インターフェースとは異なり、共通的な部分がありつつも一部分だけサブクラスにやらせたい場合などに使えます。

例

「フォームインターフェース」を継承した以下二つのフォームを用意するとします。

  • 発注フォーム
  • 受注フォーム

「テキストボックス自体をユーザーが削除できる機能」を作りたいとなったとしましょう。その際に、インターフェースを使った状態だと全ての実装クラスに対してその処理を追加しなけばならなくなって保守性が下がってしまいます。そこで「抽象クラス」を使うことによって共通部分を定義したメソッドを定義し、それを継承させるようにすればいちいち全ての実装クラスに対して手入れを行う必要がなくなります。

非推奨:仮想メソッド

具象クラスを継承して作ることになり「抽象クラス/テンプレートパターン」に劣ります。(そもそも、継承は具象クラスに対してしない方が良い。)

ファクトリークラスに関しては以下の記事でまとめています。

【DDD】「ファクトリ」オブジェクトとは?

レゾルバーを使う。

その場合、普通に実装するとswitch文が発生してしまいます。その対策に使えるのが「レゾルバー」です。

レゾルバーの導入(マッピング)

switchでやっていることを代わりに実行してくれます。

プログラム的に言えば

処理が入力に応じて変わる場合に、入力に対応する処理オブジェクトを返します。(要は、どのクラスを使えば良いかのマッピングの仕組みになります。)

具体的には
  • 各インスタンスにマッピングの処理を持たせます。(supportメソッドなど)
  • resolverクラスを別途作り、メソッドを登録させて、supportを呼ぶ作りにする。

全てのクラス(バリエーション)を呼ぶ場合

全てのクラスをループ処理で実行するので、オブジェクト指向のポリモーフィズムが使えます。

スポンサーリンク
  • 2021 08.28
  • miyabisan2
  • コメントを書く
  • オブジェクト指向
  • Tweets Twitter
  • このエントリーをはてなブックマークに追加
  • LINEで送る

関連記事

  1. 2018 05.12

    【GoFのデザインパターン】「Adapter」ってどんなパターン?、設計など

  2. 2022 04.10

    【オブジェクト指向】「DI(依存性の注入)」、「DIコンテナ」について

  3. 2021 12.13

    【オブジェクト指向】「ガーベジコレクション」の仕組み

  4. 2018 04.30

    【Java】オブジェクト指向:カプセル化

  5. 2018 05.13

    【GoFのデザインパターン】「Proxy(プロキシ)」ってどんなパターン?

  6. 2018 05.12

    【GoFのデザインパターン】「Singleton(シングルトン)」ってどんなパターン?、注意点なども。

  • コメント ( 0 )
  • トラックバック ( 0 )
  1. この記事へのコメントはありません。

  1. この記事へのトラックバックはありません。

返信をキャンセルする。

【オブジェクト指向】「SOLIDの原則」の概要

【リファクタリング】「クラスのよくある命名」

RETURN TOP

著者プロフィール

エンジニア歴10年で過去に業務系、Webデザイン、インフラ系なども経験あります。現在はWeb系でフロントエンド開発中心です。

詳細なプロフィールはこちら

スポンサーリンク

カテゴリー

  • Android
  • AngularJS
  • API
  • AWS
  • C++
  • CSS
  • cursor
  • C言語
  • DDD
  • DevOps
  • Django
  • Docker
  • Figma
  • Git
  • GitLab
  • GraphQL
  • gRPC
  • Hasura
  • Java
  • JavaScript
  • Kubernetes
  • Laravel
  • linux
  • MySQL
  • Next.js
  • nginx
  • Node.js
  • NoSQL
  • Nuxt.js
  • Oracle
  • PHP
  • Python
  • React
  • Redux
  • Rspec
  • Ruby
  • Ruby on Rails
  • Sass
  • Spring Framework
  • SQL
  • TypeScript
  • Unity
  • Vue.js
  • Webサービス開発
  • Webデザイン
  • Web技術
  • インフラ
  • オブジェクト指向
  • システム開発
  • セキュリティ
  • その他
  • データベース
  • デザインパターン
  • テスト
  • ネットワーク
  • プログラミング全般
  • マイクロサービス
  • マイクロソフト系技術
  • マルチメディア
  • リファクタリング
  • 副業
  • 未分類
  • 業務知識
  • 生成AI
  • 設計
  • 関数型言語
RETURN TOP

Copyright ©  プログラミングマガジン | プライバシーポリシー