LoginSignup
6
4

More than 1 year has passed since last update.

5分で理解するオープン・クローズドの原則

Last updated at Posted at 2020-12-23

この記事はエイチーム引越し侍 / エイチームコネクトの社員による、Ateam Hikkoshi samurai Inc.× Ateam Connect Inc. Advent Calendar 2020 13日目の記事です。2記事めの投稿!

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

この記事のゴール

  • 10分で、オープン・クローズドの原則(OCP)の概要を理解する

前提:なぜ設計やアーキテクチャを考える必要があるのか

Screenshot 2020-12-23 13.11.03.png

システムに求められるニーズを満たすために、必要な労力をできるだけ少なくしなければならない


オブジェクト指向設計(SOLID)について

Robert C. Martinによって作り出された、オブジェクト指向プログラミングにおける5つのガイドライン。

  • SRP 単一責任の原則
  • OCP オープン・クローズドの原則
  • LSP リスコフの置換原則
  • ISP インターフェイス分離の原則
  • DIP 依存関係逆転の原則

それぞれの頭文字を一文字ずつとって、SOLID原則とも呼ばれます。


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

「ソフトウェアの構成要素(クラス、モジュール、関数など)は拡張に対しては開いていて、修正に対して閉じていなければならない。」

(Bertrand Meyer. Object Oriented Software Construction, Printice Hallm 1988m p.23.)


つまりどういうこと?

変更が発生した場合に、既存のコードには修正を加えずに、新しくコードを追加するだけで対応できるような設計にしましょう!

ということ。


どのような具体例があるの?

オープン・クローズドの原則に則らず設計すると...?

Badなクラス😑

<?php

/**
 * AreaCalcService
 * 面積を計算するクラス
 */
class AreaCalcService
{
  private $length;

  public function __construct(int $length)
  {
    $this->length = $length;
  }

  /**
   * call
   * $lengthを二乗した値を返す
   * @return integer
   */
  public function call(): int
  {
    return $this->length ** 2;
  }
}

なぜBadなのか

  • 円の面積など、新しいケースを計算できない

    • 「面積を計算するクラス」という役割に相応しくない
  • 正方形以外の面積を計算できるようにするために、改修コストがかかる。

    • 既存コードを読む、書き直す... という作業が発生する

Case: 👦<円の面積を計算したいです!

<?php

/**
 * AreaCalcService
 * 面積を計算するクラス
 */
class AreaCalcService
{
  private $length;

  public function __construct(int $length, string $shape)
  {
    $this->length = $length;
    $this->shape = $shape;
  }

  /**
   * call
   * @return integer
   */
  public function call(): int
  {
    if ($this->shape === 'square') {
      return $this->length ** 2;
    } elseif ($this->shape === 'circle') {
      return $this->length ** 2 * 3.14;
    } else {
      return $this->length;
    }
  }
}

対応ケースが増えるごとに条件文が増えてしまい、AreaCalcServiceが肥大化してしまう😑


オープン・クローズドの原則にのっとって設計してみる🧑🏻‍💻

<?php

// 正方形
class Square
{
  public $length;

  public function __construct(int $length)
  {
    $this->length = $length;
  }

  public function area(): int
  {
    return $this->length ** 2;
  }
}

class AreaCalcService
{
  private $shape;

  public function __construct(object $shape)
  {
    $this->shape = $shape;
  }

  public function call(): mixed
  {
    return $this->shape->area();
  }
}


Case: 👦<円の面積を計算したいです!

<?php

class Square
{
  public $length;

  public function __construct(int $length)
  {
    $this->length = $length;
  }
  
  public function area(): int
  {
    return $this->length ** 2;
  }
}

// 円のクラスを新しく定義
class Circle
{
  public $length;

  public function __construct(int $length)
  {
    $this->length = $length;
  }

  public function area(): float
  {
    return $this->length ** 2 * 3.14;
  }
}

// ↓のクラスは変更しなくてOK!
class AreaCalcService
{
  private $shape;

  public function __construct(object $shape)
  {
    $this->shape = $shape;
  }

  public function call(): mixed
  {
    return $this->shape->area();
  }
}

このように記述することで、Circleクラスを定義するだけで改修がOK!

(追記:このままだと長方形などのパターンに対応できないので、例としてよろしくない設計です..インターフェースを使うとかが良いかもです)


まとめ

  • システムに求められるニーズを満たすために、必要な労力をできるだけ少なくしなければならない。

  • オープン・クローズドの原則にのっとってクラス設計を行うことによって、リファクタ時に労力が少なくなる🥂

  • この原則をすぐに既存コードに応用!は難しいかもしれないが、新しくクラスを作る際や、メソッド作成時に頭の片隅においておくと👍

次回予告

Ateam Hikkoshi samurai Inc.× Ateam Connect Inc. Advent Calendar 2020 13日目の記事でした!(めちゃめちゃ遅れての投稿になってしまいました:qiitan-cry:

14日目のアドベントカレンダー記事は、尊敬する先輩エンジニア、@anneauさんです!

6
4
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
4