Blog

オブジェクト指向設計のSOLID原則

solid-webepower

SOLIDは、Robert C. Martinによる最初の5つのオブジェクト指向設計(OOD)の原則の頭字語です。
これらの原則は、プログラマーが保守と拡張が容易なソフトウェアを簡単に開発できるようにするのに役立ちます。また、開発者がコードの臭いを避け、コードを簡単にリファクタリングし、アジャイル開発をサポートすることも容易になります。

S.O.L.I.Dとは何ですか?

S-単一責任の原則
O-オープンクローズドプリンシパル
L-リスコフの置換原則
I-インターフェイス分離の原則
D-依存性逆転の原則

S.O.L.I.Dが私たちをより良い開発者にするのに役立つ理由を理解するために、各原則を個別に理解しましょう。

1。単一責任の原則

「クラスには変更する理由が1つだけあるはずです。つまり、クラスには1つの仕事しかありません」と書かれています。

例を挙げてこれを理解しましょう。

次のようなユーザー向けのインターフェースがあると想像してください。

インターフェースUserInterface
{{
    パブリック関数setId($ id);
    パブリック関数getId();
    パブリック関数setName($ name);
    パブリック関数findById($ id);
    パブリック関数create();
    パブリック関数insert();
    パブリック関数update();
    パブリック関数delete();
}

この場合、CRUDメソッドを確認するには、アクセサーから分離されたデータアクセス層に配置する必要があります。これは、ここで2つの重複する責任が共存し、さまざまな要件に応じてクラスを変更することを意味します。

 

したがって、UserInterfaceと2つの異なるインターフェイスを作成することで、この問題を解決できます。
UserMapperInterface

インターフェースUserMapperInterface
{{
    パブリック関数findById($ id);
    パブリック関数create();
    パブリック関数insert(UserInterface $ user);
    パブリック関数update(UserInterface $ user);
    パブリック関数delete($ id);
}

インターフェイスUserInterface
{{
    パブリック関数setId($ id);
    パブリック関数getId();
    パブリック関数setName($ name);
    パブリック関数setEmail($ email);
    パブリック関数getEmail();
}

2。オープンクローズ原則

「オブジェクトまたはエンティティは拡張のために開いている必要がありますが、変更のために閉じている必要があります」と述べています。

これは、クラス自体を変更することなく、クラスを簡単に拡張できる必要があることを意味します。

これを理解し、長方形を含むBoardクラスがあり、長方形の面積を計算するとします。

 class Rectangle
{{
    public $ width;
    public $ height;
}
クラスボード
{{
    public $ rectangles [];
    パブリック関数calculateArea(){
        $ area = 0;
        foreach($ this-> rectangles as $ rectangle){
            $ area + = $ rectangle-> width * $ rectangle-> height;
        }
    }
}

しかし今、あなたがボードにサークルを追加する必要がある新しい要件がある場合。

ボードクラスに長方形と円について知らせる必要がありますが、OCPに従う場合は、ボードまたは長方形に触れる必要はありません。既存のボードを再利用して、サークルに適用する必要があります。

インターフェース形状{
    パブリック関数area();
}

クラスRectangleはShapeを実装します
{{
    public function area(){
        $ this-> width * $ this-> height;を返します。
    }
}

クラスCircleはShapeを実装します
{{
    public function area(){
        $ this-> radius * $ this-> radius * pi();を返します。
    }
}

クラスボード
{{
    パブリック関数calculateArea(){
        $ area = 0;
        foreach($ this-> shapes as $ shape){
            $ area + = $ shape-> area();
        }
    }
}

3。リスコフの置換原則

「q(x)をタイプTのxのオブジェクトに関するプロパティとします。その場合、q(y)はタイプSのオブジェクトyに対して証明可能である必要があります。ここで、SはTのサブタイプです」。

これは、すべてのサブクラス/派生クラスがそれらの基本/親クラスの代わりに使用できる必要があることを意味します。

例を挙げて理解しましょう。この場合、クラスRectangleがあり、Rectangleから拡張するクラスSquareを作成します。

 class Rectangle
{{
    パブリック関数setWidth($ w){$ this-> width = $ w; }
    パブリック関数setHeight($ h){$ this-> height = $ h; }
    public function getArea(){return $ this-> height * $ this-> width; }
}

クラスSquareはRectangleを拡張します
{{
    パブリック関数setWidth($ w){$ this-> width = $ w; $ this-> height = $ w; }
    パブリック関数setHeight($ h){$ this-> height = $ h; $ this-> width = $ h;
}

これで、クラスを計算する関数を作成できます。

 function areaOfRectangle(){
    $ rectangle = new Rectangle();
    $ r-> setWidth(7); $ r-> setHeight(3);
    $ r-> getArea(); // 21
}

LSPが言うように、長方形を正方形で変更できるはずです。

 function areaOfRectangle(){
    $ rectangle = new Square();
    $ r-> setWidth(7); $ r-> setHeight(3);
    $ r-> getArea(); // 9
}

「Squareクラスが動作を変更せずにRectangleを拡張していることを確認する必要があります」と述べました。しかし、9に等しくない出力21を受け取っているので

解決策は、たとえばインターフェイスQuardを導入することにより、クラス継承階層を正しく管理することです。

インターフェースQuard
{{
    パブリック関数setHeight($ h);
    パブリック関数setWidht($ w);
    パブリック関数getArea();
}
クラスRectangleはQuardを実装します。

クラスSquareはQuardを実装します。

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

「クラスは複数のインターフェースを同時に実装できます。クライアントに不要なメソッドをデプロイするように強制するべきではありません」と述べています。

これを理解して、ワーカーがいるデジタルエージェンシーについて考えてみましょう。そこで、インターフェイスワーカーを作成します。

インターフェースワーカー{
    パブリック関数takeBreak()
    パブリック関数code()
    パブリック関数callToClient()
    パブリック関数attendMeetings()
    パブリック関数getPaid()
}

未使用のメソッドで問題が発生するWorkerインターフェイスとTesterを実装するManagerクラスがあると仮定します。

クラスマネージャーはWorkerを実装します
{{
    public function code(){falseを返します; }
}

クラスDeveloperはWorkerを実装します
{{
    public function callToClient(){echo $ swear_word; }
}

次に、より多くのインターフェースを作成する必要があります。

インターフェースワーカー
{{
    パブリック関数takeBreak()
    パブリック関数getPaid()
}

インターフェイスコーダー{
    パブリック関数code()
}

インターフェイスClientFacer {
    パブリック関数callToClient()
    パブリック関数attendMeetings()
}

クラステスターはWorker、Coder {}を実装します

クラスマネージャーはWorker、ClientFacer {}を実装します

5。依存性逆転の原則

「エンティティは、具体化ではなく、抽象化に依存する必要があります。高レベルのモジュールは抽象化に依存してはならないと述べています。」

この原則を理解しましょう。高レベルのWorkerクラスと、Workerというクラスがあります。マネージャーはワーカーを働かせることができます。

クラスワーカー
{{
    パブリック関数work(){}
}

クラスマネージャー
{{
    プライベート$ worker;
    パブリック関数setWorker($ w){
        $ this-> worker = $ w;
    }

    パブリック関数manage(){
        $ this-> worker-> work();
    }
}

次に、新しい種類の専門ワーカーを追加する必要があります。このために、新しいクラスSpecializedWorkerを作成します。

クラスSpecializedWorker
{{
    パブリック関数work(){}
}

これにより、ManagerClassの変更などの問題が発生し、Managerの一部の機能が影響を受ける可能性があります。ここで、依存性逆転の原則に従い、前の例を実装します。

インターフェース従業員
{{
    パブリック関数work();
}

クラスWorkerはEmployeeを実装します
{{
    パブリック関数work(){}
}
クラスSpecializedWorkerはEmployeeを実装します
{{
    パブリック関数work(){}
}

結論

S.O.L.I.Dの原則を使用することは、労力の増加を意味します。その結果、維持するクラスとインターフェースが増えます。クラスを設計する際、S.O.L.I.Dの原則は、コードの臭いを取り除くために適用できるガイドラインです。

ご不明な点やご質問がございましたら、お気軽にお問い合わせいただくか、お問い合わせをお寄せください。すぐにご連絡いたします。私たちはあなたから聞きたい。