The Clean Architecture in PHP読書ノート(二)

9041 ワード

本文はシリーズの文章の第2編で、第1編の住所は
The Clean Architecture in PHP読書ノート(一)
あなたのデカップリングツール
次の文章では、主にデカップリングをめぐって5つのツールを提供し、簡単にデカップリングすることができます.
  • Design Patterns,A Primer
  • SOLID Design Principles
  • Depedency Injection
  • Defining a Contract with Interfaces
  • Abstracting with Adapters

  • 最初のツール:デザインモードについて説明します.
    Design Patterns,A Primer
    設計モードはソフトウェアの中で通用する問題に対する総括で、設計モードがあって、私達が交流を行うのに便利で、例えばMVCといえば、私達は何が起こっているかを知っていて、さもなくば私達はバラバラのたくさんの話をして説明しなければならなくて、伝播しにくくて、交流しにくくて、1つの良い設計モードは必然的に1つの簡単で分かりやすい名前があって、簡単なものだけなため、才能が広く伝えられる.
    現在、デザインモデルといえば、1994年に4人組が提案した23のモデルが3つのカテゴリに分かれている.
  • Creational
  • Structural
  • Behavioral

  • 本書は23パターンを紹介することはありません.このように紹介する本が多すぎて、本書もそんなに多くの幅を持っていません.しかし、これらは再展開の基礎です.そのため、最近の本の概念を紹介します.
  • Factory:生産対象を担当し、言語本省が提供するnewキーワードと同様に、C++でnewと異なるのは、newがstringによって直接対象をインスタンス化できないことであるが、factoryは
  • である.
  • Repository:GoFで提案された設計モデルではありません.これは、
  • のデータの格納と取得を担当する倉庫に似ています.
  • Adapter:アダプタなので、インタフェースを
  • に変換することが考えられます.
  • Strategy:柔軟性を目的とし、1つまたは複数の動作をカプセル化し、
  • を容易に置き換えることができる.
    上の概念を具体的に展開する
    The Factory Patterns
    単純コード:
    $customer = new Customer();
    

    customerが十分に簡単であれば、上の作成には問題ありませんが、customerが作成するときに初期化をしなければならない場合は、私たちは気が狂っています.簡単な考えは、もちろんこれらの作成ロジックを抽出して関数にして多重化することです.そのため、次のバージョンがあります.
    複雑なコード:
    
    class CustomerFactory {
        protected $accountManagerRepo;
    
        public function __construct(AccountManagerRepository $repo) { 
            $this->accountManagerRepo = $repo;
        }
    
        public function createCustomer($name) { 
            $customer = new Customer(); 
            $customer->setName($name); 
            $customer->setCreditLimit(0); 
            $customer->setStatus('pending'); 
            $customer->setAccountManager(
              $this->accountManagerRepo->getRandom()
            );
            return $customer; 
        }
    }
    

    以上のコードが出てきた理由をまとめると、ソフトウェアの複雑さの向上は、ソフトウェアにおける様々な問題の発生は、ソフトウェアの複雑さ、複雑さによって対応方法が異なり、全体的な目標は複雑さを低減することである.CustomerFactoryのメリットは
  • Reusable code.作成した場所でこのコードを呼び出すことができ、コードの複雑さを低減します.
  • Testable code.注目点が分離され、テストが容易になるため、論理を作成するテスト
  • を単独で行うことができる.
  • Easy to change.作成ロジックは1箇所のみで、
  • を簡単に変更できます.
    Static Factories
    class CustomerFactory {
        public static function createCustomer($name) {
            $customer = new Customer(); 
            $customer->setName($name); 
            $customer->setCreditLimit(0); 
            $customer->setStatus('pending');
            return $customer; 
        }
    }
    $customer = CustomerFactory::createCustomer('ACME Corp');
    

    静的工場は、staticメソッドでアクセスしますが、静的工場が良いのか、それとも上の工場が良いのか、どうやって判断しますか.答えは次のとおりです.
    It depends
    最良ではなく、より適切なものしかありません.静的方法が需要を満たすことができる場合は、静的工場を使用し、データソースが常に変化する場合は、インスタンス化工場を使用し、必要な依存を注入します.
    Types of Factories
  • The Factory Method pattern:オブジェクトを作成する方法を定義しますが、どのオブジェクトを作成するかは、サブクラスによって決定されます.
  • The Abstract Factory pattern:抽象ファクトリモードの粒度は、1つのオブジェクトの作成だけでなく、関連するオブジェクトのセットの作成
  • にも管理されます.
    talk is cheap,show me the code
    コードをつけましょう.
    class Document {
    
        public function createPage() {
    
            return new Page();
        }
    
    }
    
    

    もし私たちがまたいくつかのpageを持っていたら、どうしますか?
    class Document {
    
        public function createPage($type) {
          switch $type {
            case "ResumeDocument":
              return new ResumePage();
            case "PortfolioDocument":
              return new PortfolioPage();
          }
        }
    
    }
    

    上のコードの問題は、新しいタイプのpageのたびに、createPageの方法を修正しなければなりません.オープンクローズ原則(OCP)を満たさないと、PHPにはnew文字列に直接伝えることでオブジェクトが作成され、コードを見ることができます.
    class Document {
    
        public function createPage($type) {
          return new $type;
        }
    }
    

    質問:$typeに転送された有効性を検証することはできません.また、createPageに戻ったタイプについてもチェックできません.どうすればいいですか.
    構想は:作成ロジックを注目点によって分離したロジックに従って、各タイプのdocumentは自分のpageを作成して、これは$typeの有効性の問題を解決して、それは戻り値に対して、私たちは1つの共通のインタフェースを定義して、このインタフェースを実現するだけが予想に合っています.
    abstract class AbstractDocument {
        abstract public function createPage():PageInterface;
    }
    class ResumeDocument extends AbstractDocument {         
      public function createPage():PageInterface {
            return new ResumePage(); 
        }
    }
    class PortfolioDocument extends AbstractDocument {      
      public function createPage():PageInterface {
            return new PortfolioPage(); 
        }
    }
    interface PageInterface {}
    class ResumePage implements PageInterface {} 
    class PortfolioPage implements PageInterface {}
    

    最初のコンセプトThe Abstract Factory patternを紹介します
    抽象ファクトリとは,ファクトリメソッドに対する1+1=2の重ね合わせである.
    Documentが1つのpageだけでなくcoverも作成されると、2つのcreateメソッドがあり、各サブクラスで1つのメソッドが実装されます.
    Repository Pattern
    倉庫モード:このモードはEric Evanの神書:Domain-Driven Design:Tackling Complexity in the Heart of Softwareで詳しく説明した
    A REPOSITORY represents all objects of a certain type as a conceptual set (usuallyemulated). It acts like a collection, except with more elaborate querying capability.
    Domain-Driven Design, Eric Evans, p. 151
    倉庫は1つのデータの集合に似ていて、比較の集合はもっと細かい設計のqueryがあって、私達がRepositoryについて話す時、私達の関心は“querying the database”ではありませんて、更に純粋な目的:データにアクセスします
    一般的なデータの読み取り方法
    
    class MyRepository {
        public function getById($id); 
        public function findById($id); 
        public function find($id); 
        public function retrieve($id);
    }
    

    一般的な書き方:
    class MyRepository {
        public function persist($object); 
        public function save($object);
    }
    

    各Repositoryは1つのクラスのアクセスに対応し,複数のクラスがあり,複数のRepositoryがある.
    Repositoryはどうやって仕事をしますか?
    Objects of the appropriate type are added and removed, and the machinery behindthe REPOSITORY inserts them or deletes them from the database.
    Domain-Driven Design, Eric Evans, p. 151
    主にいくつかのリソースを提供します.
    Dotrine ORM:Date Mapperを実現
    Eloquent ORM,Propel:Active Recordを実現
    Zend Framework 2:Table Data Gatewayモードを提供
    自分でRepositoryを実現したい場合は、Patterns of Enterprise Application Architectureを強くお勧めします.
    データベースの各種モードについて、pptを推薦します
    Adapter Pattern
    コードを直接見る:
    class GoogleMapsApi {
        public function getWalkingDirections($from, $to) {}
    }
    interface DistanceInterface {
        public function getDistance($from, $to);
    }
    class WalkingDistance implements DistanceInterface {    
      public function getDistance($from, $to) {
            $api = new GoogleMapsApi();
            $directions = $api->getWalkingDirections($from, $to);
            return $directions->getTotalDistance(); 
        }
    }
    

    アダプタは、非常に明確なテーブル名が意図されており、WalkingDistanceを通じてGoogleMapsApiをDistanceInterfaceに適合させる
    Strategy Pattern
    コードを見ても:
    public function invoiceCustomers(array $customers) { 
        foreach ($customers as $customer) {
            $invoice = $this->invoiceFactory->create(
              $customer,
              $this->orderRepository->getByCustomer($customer)
            );
            // send invoice...
        } 
    }
    //                   ,    
    
    
    interface InvoiceDeliveryInterface { 
      public function send(Invoice $invoice);
    }
    
    class EmailDeliveryStrategy implements InvoiceDeliveryInterface { 
        public function send(Invoice $invoice) {
        // Use an email library to send it
        } 
    }
    class PrintDeliveryStrategy implements InvoiceDeliveryInterface { 
        public function send(Invoice $invoice) {
        // Send it to the printer
        } 
    }
    //          ,      ,   print
    
    public function invoiceCustomers(array $customers) { 
        foreach ($customers as $customer) {
            $invoice = $this->invoiceFactory->create(
            $customer,
            $this->orderRepository->getByCustomer($customer));
        switch ($customer->getDeliveryMethod()) { 
        case 'email':
            $strategy = new EmailDeliveryStrategy();
            break; 
        case 'print': 
        default:
            $strategy = new PrintDeliveryStrategy();
            break; 
        }
        $strategy->send($invoice);
      }
    }
    //       :     runtime,       ,          
    //   :                 ,         ,          ,          :
    
    class InvoiceDeliveryStrategyFactory {
        public function create(Customer $customer) {
            switch ($customer->getDeliveryMethod()) { 
            case 'email':
                return new EmailDeliveryStrategy();
                break; 
            case 'print': 
            default:
                return new PrintDeliveryStrategy();
                break; 
            }
        } 
    }
    //       ,        ,  invoiceCustomers       ,    ,            
    public function invoiceCustomers(array $customers) {    
      foreach ($customers as $customer) {
        $invoice = $this->invoiceFactory->create(
          $customer,
          $this->orderRepository->getByCustomer($customer));
        $strategy = $this->deliveryMethodFactory->create(
          $customer);
        $strategy->send($invoice);
      }
    }
    

    より多くのデザインモード:
  • 設計モード:オブジェクト向けソフトウェアを多重化可能なベース
  • Head First Design Patterns

  • 次のSOLID Design Principlesをお楽しみください