[Architecture] OOP SOLID


開始します.
最近CLINアーキテクチャに興味が出てきましたが、関連記事を読んでいるうちにCLINANITCHとSOLIDの文章を説明しますを読んだ後、CLINアーキテクチャを理解する前に、SOLIDを理解しておくべきだと思います.
SOLIDとは?
SOLIDは対象開発の5つの原理を暗記しやすい略語で,以下の5つの概念を含む.
単一責任の原則
オープンクローズの原則
L-LSP(Liskov Subsation Principle)、リスコフ交換の原則
かいめんぶんりのげんり
依存独立の原則
SOLIDは「クラスレベルの原則」ですが、より広い範囲(クラスグループ)に適用できる要素も含まれています.
SRP、単一責任原則
定義#テイギ#
  • クラスには1つの機能しかない(複数の方法があるかもしれないが、最終的には1つの機能しか実行できない)
  • すなわちクラスの変更理由は1つのみである.

  • Bad
    class Book{
        String title;
        String author;
    
        public void save(){
            // DB와 연결
            // DB에 저장
            // DB와의 연결 종료
        }
    }
    違反
  • 多重責任:Bookは情報を担当する役割であり、データベースの接続と格納を担当する役割
  • である.
  • には複数の変更要因があります.
    1.データベースの情報が変更されました
    2.Bookのデータ構成を変更した場合:
  • Refactoring
    class Book{
        String title;
        String author;
        ...
    }
    
    class BookPersistenceService implements EntityPersistenceService<Book>{
        @Override
        public void save(Book entity) {
            // DB에 저장..
        }
    }
  • マニュアルには情報のみが含まれています
  • BookPersistenceServiceクラスでは、データベースに格納されているロールを分離(もちろん、データベース接続に関連するロールも分離すべき)
  • 評価
  • 総じて、SRPを無条件に遵守することは難しく、ある程度妥協すべきである.
  • SRPでは、「責任」と「変更」の定義が曖昧だと考える人が多い.
  • 評価リファレンス
    I don't love the single responsibility principle
    単一責任の原則とは何ですか。
    Shades of the Single Responsibility Principle(この文章は懸念を解消するのに役立ちます)
    OCP、オープンクローズの原則
    定義#テイギ#
  • ソフトウェアのコンポーネントは、拡張時に開き、変更時に閉じる必要があります.
  • 拡張で開く::
  • 新機能
  • 国境閉鎖?:
  • とは、拡張が発生した場合、拡張クラスを使用するコードが変更されないことを意味します.
    これは、
  • のビジネスロジックが変更されても、既存のコンポーネントを変更することなく、コンポーネントを簡単に拡張および再利用できることを意味します.
  • OCPは、管理可能、再利用可能なコードを作成する基礎であり、OCPを可能にする主なメカニズムは抽象的および多形式である.

  • ソース:オープンクローズの原則:Open Close Principle
    public static class PaymentRequest {
        private String cardNumber;
        private String csv;
        private CardType type;
    }
    
    // Controller
    @RequestMapping(value = "/ocp/anti/payment", method = RequestMethod.POST)
    public void pay(@RequestBody CardPaymentDto.PaymentRequest req){
        if(req.getType() == CardType.SHINHAN){
            shinhanCardPaymentService.pay(req);
        }else if(req.getType() == CardType.WOORI){
            wooriCardPaymentService.pay(req);
        }
    }
    違反
  • 以上の構成では、新しいカードタイプが生成されると、拡張機能を呼び出す「コントローラ」のコードを変更(変更)する必要がある.
  • は上記の構成であり、変更中はクローズされていない.
  • Refactoring
    public class PaymentController {
        @RequestMapping(value = "/payment", method = RequestMethod.POST)
        public void pay(@RequestBody CardPaymentDto.PaymentRequest req) {
            final CardPaymentService cardPaymentService = cardPaymentFactory.getType(req.getType());
            cardPaymentService.pay(req);
        }
    }
    
    public class ShinhanCardPaymentService implements CardPaymentService {
        @Override
        public void pay(CardPaymentDto.PaymentRequest req) {
            final ShinhanCardDto.PaymentRequest paymentRequest = buildPayment(req);
            shinhanCardApi.pay(paymentRequest);
        }
    }
    
    public class WooriCardPaymentService implements CardPaymentService {    
        @Override
        public void pay(CardPaymentDto.PaymentRequest req) {
            final WooriCardDto.PaymentRequest paymentRequest = buildPayment(req);
            wooriCardApi.pay(paymentRequest);
        }
    }
  • 構造では、新しい支払サービスが必要な場合、「CardPaymentService」を実装するクラスを生成し、拡張カードタイプに応じてCardPaymentFactoryにサービスを返すことができる.
  • すなわち、実際の拡張機能を実行する論理(Controller)のコードは変更されていないため、変更はクローズされる.
  • 評価
  • OCPの負の内容を検索しようとしたが、よく見えなかった.これからは実感で補う.
  • LSP、リスコフ置換の原則
    定義#テイギ#
  • は、より高いレベルのクラスおよびコンポーネントに影響を与えず、より低いレベルに置き換えることができます.
  • 例えば
  • では、ArrayListおよびLinkedListは共にListをインタフェースとしており、これはそれらが互いに置き換えられることを意味する.
  • をさらに拡張すると、データベース
  • をMySQLまたはMongoDBのドメインロジックに影響を与えることなく置き換えることができます.
        public void f(){
            ArrayList arrayList = new ArrayList();
            doSomeThing(arrayList);
    
        }
        public void doSomeThing(ArrayList list){
            list.add(...);
        }
    違反
  • 上のコードではdoSomeThing関数はArrayListをパラメータとして受け入れ、場合によってはLinkedListの方が有利である可能性がある.
  • Refactoring
        public void f(){
            ArrayList arrayList = new ArrayList();
            doSomeThing(arrayList);
            LinkedList linkedList = new LinkedList();
            doSomeThing(linkedList);
        }
        public void doSomeThing(List list){
            list.add(...);
        }
  • LinkedListとArrayListは共にListインタフェースを実現しているので、互いに代替することができる.
  • 参照として、この場合、LSPは再パッケージされているが、OCPが保護されていることは自然に示されている.このように,SOLIDは完全分離の5原則ではなく,相互共有範囲の原則である.
  • 評価
  • 以降、明確な根拠が見つかれば、
  • に再登録される.
    ISP,インタフェース分離の原則
    定義#テイギ#
  • は、1つの汎用インターフェース
  • ではなく、複数の明確なインターフェースを使用する.
  • SRPがクラスに対する単一の責任を強調する場合、ISPはインタフェースに対する単一の責任を強調する.
  • DIP,依存性逆転の原則
    定義#テイギ#
  • サブモジュールの変更は、親モジュールの関係の変更を要求することを中断します.これは、
  • を意味します.
  • 実際の使用関係は変わらないが、抽象を呼び出すことによって関係を解放することは
  • を意味する.
  • DIPには、「Inversion of Control」、「Hookメソッド」、「拡張性」の3つのキーワードがあります.

  • DIPの場合、これは少し複雑で、リンクを保持し、次回の更新を計画します.
    参考としてDIPとOCPが非常に似ているので調べてみると下記のような文章が見つかり参考になります.
    Open Close Principle (OCP) vs Dependency Inversion Principle (DIP)