1. 初めに
    前投稿にデザインパターンについて書こうと思いましたが、デザインパターンのベースをまとめた方がいいと思い、Laravelと関係があまりないですが、個人的にとても大切だと思うため、当投稿を作成し、チップシリーズ に追加したいと思います。有用であれば、「いいね」つけていただければ幸いです。


  2. なぜSOLIDの世界
    SOLID
    とは
    Single responsibility principle
    Open/closed principle
    Liskov substitution principle
    Interface segregation principle
    Dependency inversion principle
    です。なぜSOLIDが大切だかみんなさんご存知だと思いますが、説明します。SOLIDはOOPの原則であり、明確にしないといけないことです。SOLIDを提供するのはメリットは:
    ・保守性が高い
    ・拡張性が高い
    ・エラーハンドリング性が高い
    なのでOOP使ったら、当然SOLIDを重視するのが必要でしょう。SOLIDの世界に入りましょう。


  3. Single responsibility principle
    日本語では「 単一責任の原則」であり、単一責任というキーワードがポイントです。単一責任の原則とは「クラスに変更が起こる理由は、一つであるべき。」です。
    この原則の意味は「一つクラスは一つ役割であり、一つタスクだけ解決する」です。この原則の意味を理解するのがシンプルですが、実現が難しいです。以下のサンプルを参考にしましょう

    // RSP Violation
    class Post
    {
    
        public function getTitle() {
            return "Welcome to SOLID's world";
        }
    
        public function getAuthor() {
            return  "Laziii";
        }
    
        public function getPostDetails() {
            return [
                'title' => $this->getTitle(),
                'author' => $this->getAuthor(),
                'content' => 'SOLID is simple principle but hard to apply'
            ];
        }
    
        public function genPostJson() {
            return json_encode($this->getPostDetails());
        }
    }

    上記のサンプルには単一責任の原則に違反していますが、なぜかというのは自分で考えてみてください。
    上記のソースコードをリファクタリングし、単一責任の原則に守りましょう

    // Refactored
    class Post
    {
    
        public function getTitle() {
            return "Welcome to SOLID's world";
        }
    
        public function getAuthor() {
            return  "Laziii";
        }
    
        public function getPostDetails() {
            return [
                'title' => $this->getTitle(),
                'author' => $this->getAuthor(),
                'content' => 'SOLID is simple principle but hard to apply'
            ];
        }
    }
    
    interface PostFormattable {
        public function format(Post $post);
    }
    
    class JsonPostFormatter implements PostFormattable {
        public function format(Post $post) {
            return json_encode($post->getPostDetails());
        }
    }

  4. Open – closed principle
    日本語では「開放/閉鎖原則」であり、もちろんキーワードは「開放/閉鎖」です。開放/閉鎖原則というのは
    ・拡張に対して開いて (open) いなければならず
    ・修正に対して閉じて (closed) いなければならない
    開放/閉鎖原則の意味もシンプルですが、メリットがが有用です。
    ・保守性が高い
    ・再使用性が高い
    ・エラーハンドリング性が高い
    もちろん開放/閉鎖原則に守るのが難しいですが、実現できたら、仕様変更対応の時に楽になるので、必ず検討しましょう。
    もっと明確にするために、サンプルを分析しようと思います。

    // OCP Violation
    class Square
    {
        protected $width;
        protected $height;
    
        public function __construct($width, $height) {
            $this->width = $width;
            $this->height = $height;
        }
    
        public function getWidth() {
            return $this->width;
        }
        public function getHeight() {
            return $this->height;
        }
    }
    
    class AreaCalculator
    {
    
        public function calculate($squares) {
            foreach ($squares as $square) {
                $area[] = square->getWidth() * square->getHeight();
            }
            return array_sum($area)
        }
    }

    この実装方は問題なさそうですよね。でもちょっと仕様を変更しようと思ったら、$squaresに四角だけではなく、丸等もある場合は、calculateメソッドの修正が必要となります。

    class Square
    {
        protected $width;
        protected $height;
    
        public function __construct($width, $height) {
            $this->width = $width;
            $this->height = $height;
        }
    
        public function getWidth() {
            return $this->width;
        }
        public function getHeight() {
            return $this->height;
        }
    }
    
    
    class Circle
    {
        protected $radius;
        function __construct($radius) {
            $this->radius = $radius;
        }
    
    
        public function getRadius() {
            return $this->radius;
        }
    }
    
    class AreaCalculator
    {
    
        public function calculate($shapes) {
            foreach ($shapes as $shape) {
                // $area[] = square->getWidth() * square->getHeight();
                if (is_a($shape, 'Square')) {
                    $area[] = square->getWidth() * square->getHeight();
                }
                else {
                    area[] = $shape->getRadius() * $shape->getRadius() * pi();
                }
    
            }
            return array_sum($area)
        }
    }

    しかし、三角も追加したい場合はもう一度修正が必要となるので、開放/閉鎖原則に違反しましたよね。改善しましようと思いますが改善する前に、開放/閉鎖原則をもう一度読みましょう。
    その後に、リファクタリングを行います。

    public interface Shape {
        public function area();
    }
    
    class Square implements Shape
    {
        protected $width;
        protected $height;
    
        public function __construct($width, $height) {
            $this->width = $width;
            $this->height = $height;
        }
    
        public function area() {
            return $width * $height;
        }
    }
    
    /**
    *
    */
    class Circle implements Shape
    {
        protected $radius;
        function __construct($radius) {
            $this->radius = $radius;
        }
    
    
        public function area() {
            return $radius * $radius * pi();
        }
    }
    
    
    class AreaCalculator
    {
    
        public function calculate($shapes) {
            foreach ($shapes as $shape) {
                $area[] = $shape->area();
    
            }
            return array_sum($area)
        }
    }

  5. まとめ
    上記に「 単一責任の原則」と「開放/閉鎖原則」について説明しましたが、私の方も不明点がたくさんありますから、みなさまに補足いただければ幸いです。次の投稿でも引き続きSOLIDの世界を紹介しようと思います。やる気が上がるので、有用であれば、「いいね」つけていただければ幸いです。



  6. 参考
    単一責任の原則
    開放/閉鎖原則
    SOLID