3
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

SOLIDの原則

Last updated at Posted at 2019-12-26

こんにちは!
オブジェクト指向が気になり出す季節がやってきましたね!
ざっくり、自己紹介。
2年程フロントエンド担当( HTML5, CSS3, JavaScript, PHP←少し)程度の経験です。
最近、業後の精神的余裕をついに獲得したので、アウトプットとして少しづつ投稿していきます。

今回はオブジェクト指向勉強する為の忘備録的な感じで書いて行きます。

間違った事やズレている事があるのでご了承ください。←なんなら教えてください!!笑
##単一責任の原則

1つのクラスは1つだけの責任を持たなければならない。すなわち、ソフトウェアの仕様の一部分を変更したときには、それにより影響を受ける仕様は、そのクラスの仕様でなければならない。
引用元:https://ja.wikipedia.org/wiki/SOLID

SRP違反.php
public class UpdateUserCommand
{
  public function updata_user_data()
  {
    get_user_data();
    insert_user_data();
    echo 'ユーザーデータが更新されました。'
  }

  public function get_user_data()
  {
    echo 'ユーザーのデータ取得します';
  }

  public function get_user_data()
  {
    echo 'ユーザーのデータをデータベースに入れます(db接続)';
  }
}

ちょっと強引な上記の例だと、UpdateUserCommandは「ユーザーデータの更新」を一つの意味に持ちます。
パッとみ更新処理が書いてあるように見えますが、これ function get_user_data でdataのやり取りをやってます。
一つのクラスに一つの意味なのに、
・ユーザーデータの参照
・ユーザーデータの更新
の2つの意味を持っている事になります。
なので、

SRP.php
public class UpdateUserCommand
{
  public function updata_user_data()
  {
    insert_user_data();
    echo 'ユーザーデータが更新されました。'
  }

  public function get_user_data()
  {
    echo 'ユーザーのデータをデータベースに入れます(db接続)';
  }
}

こうすると「一つのクラスに一つの意味」になりました。

##オープン/クロースドの原則

「ソフトウェアのエンティティは(中略)拡張に対して開かれていなければならないが、変更に対しては閉じていなければならない。」
引用元:https://ja.wikipedia.org/wiki/SOLID

OCP違反.php
class マリオ
{
    private $marioType;

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

    public function jump ()
    {
        echo 'jump!';
    }

    public function attack()
    {
        switch ($this->type)
        {
            case 'ファイアーマリオ':
                echo 'fireeeee!!!';
                break;
            case 'ハンマーマリオ':
                echo 'hammerrr!!!';
                break;
            default:
                echo '...';
                break;
        }
    }

    public function miss()
    {
        echo 'game over';
    }
}

上記の場合だと新たにbuttonを追加したときに、function attackを修正しなければなりません。これは修正に対して閉じていません。

#####interfaceを使おう
この マリオ を、変更でなく、追加で機能拡張できるようにします。
interface を使い、抽象化してみます。

OCP.php
interface 振る舞い
{
    public function attack();
    public function miss();
}

class マリオ  implements 振る舞い
{
    public function attack()
    {
        echo '...';
    }
    
    public function miss()
    {
        echo 'game over';
    }
}

class ハンマーマリオ  implements 振る舞い
{
    public function attack()
    {
        echo 'hammerrrr!!!';
    }
    
    public function miss()
    {
        echo 'mamma mia..';
    }
}

class 無敵マリオ  implements 振る舞い
{
    public function attack()
    {
        echo 'I am invincible.';
    }
    
    public function miss()
    {
        echo 'I am invincible.';
    }
}
   

上記のコードで、attack に対し変更を加えることなく機能を拡張できるようになりました。
ただ、 jump の振る舞いを変更しなければならなくなった場合は既存のソースコードに変更を加える必要があります。これはOCP違反となります。
振る舞い にあらかじめ jump を加えておけばよさそうにも思えますが、あらゆる修正に対して閉じるのは無理なので、どの修正に対して閉じるか戦略的に閉じましょう。

##リスコフの置換原則

「プログラムの中にある任意のオブジェクトは、プログラムの正しさを変化させることなく、そのサブクラスのオブジェクトと置換できなければならない。」詳しくは契約プログラミングも参照。
引用元:https://ja.wikipedia.org/wiki/SOLID

LSP.php
public function 社畜(UserA $user)
{
    $働いた時間 = $user->働く();

    $合計 = $働いた時間 + $休憩した時間;

    return $合計;
}

class 社畜A
{
    public function 働く()
    {
        $time = 8; //実際には代入は必要なし
        return $time;
    }
}

class 社畜B extends 社畜A
{
    public function 働く()
    {
        return '働きました!';
    }
}

単純な例ですが、社畜Bを渡したときに社畜Aと同じ動きをするでしょうか?
これは意図した結果とは違いますよね。

継承したクラスは、元のクラスと同じ対応できるようにしてね、っていうことです。

##インターフェース分離の原則

「汎用的な目的のインターフェイスが1つだけあるよりも、特定のクライアント向けのインターフェイスが多数あった方がよりよい。」
引用元:https://ja.wikipedia.org/wiki/SOLID

ISP違反.php
interface ワザマシン
{
    public function 穴をほる();
    public function 泳ぐ();
    public function 空を飛ぶ();
}

class ディグダ implements ワザマシン
{
    public function 穴をほる()
    {
          あなほります;
    }
    public function 泳ぐ()
    {
          //実装しないでください
    }
    public function 空を飛ぶ()
    {
          //実装しないでください
    }
}

使わないのに実装するのは意味がないですね。

ISP.php
interface ワザマシンA
{
    public function 穴をほる();
}

interface ワザマシンB
{
    public function 泳ぐ();
}

interface ワザマシンC
{
    public function 空を飛ぶ();
}



class ディグダ implements ワザマシンA
{
    public function 穴をほる()
    {
        あなほります;
    }
}

class ゼニガメ implements ワザマシンB
{
    public function 泳ぐ()
    {
        泳げます;
    }
}

class ピジョン implements ワザマシンC
{
    public function 空を飛ぶ()
    {
        そらとびます;
    }
}

きちんとインターフェースをグループごとに分けて適切に使いましょう。
「単一責務の原則」のインターフェイス版」みたいな感じですね。

##依存性逆転の原則

「具体ではなく、抽象に依存しなければならない」
引用元:https://ja.wikipedia.org/wiki/SOLID

DIP違反.php

public class Dbinp()
{
    $db = new Db();
    $db->save();
}

public class Db()
{
    public function save()
    {
        // insert、、、、、
    }
}

Dbinpクラスからは、Dbクラスをクラスを使っています。(ここ重要)
使っているということはDbinpクラスの方がより上位にいるということです。
そして、依存していると言う事です。
なので、早速インターフェイスに依存してもらいましょう。

DIP.php
public class Dbinp(Dbinterface $Dbinterface)
{
    $Dbinterface->save();
}


interface Dbinterface()
{
    public function save();
}

public class Db() implements Dbinterface
{
    public function save()
    {
        // insert、、、、、
    }
}

インターフェースを使うことで、下位クラスへの依存がなくなりました。
ただ、インターフェースには依存しているのでインターフェースが変更されればDbinpクラスの変更することになります。

依存関係逆転の原則の「逆転」とは、依存の向きを逆にすることですね。
###まとめ

今回わかってる風にまとめてみましたが、ぶっちゃけ良くわかってません!!!
なので、SOLID初学者の方はあまりこの記事信用しないでください。
すでにSOLIDを理解している方は指摘して頂けたら、泣いて喜びます。
読んで頂きありがとうございました。

3
6
0

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
3
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?