実際にはSOLID。オープンクローズ原理とActiveQueryYii2

作業中の会話の中で、私の仲間のプログラマーの1人は、ソフトウェア設計のあらゆる種類の原則とパターンがテストタスクを実行するときに使用するのに適していることに気付きましたが、実際の戦闘プロジェクトでは、通常は適用できません。何故ですか?主な理由は2つあります。 





  • 原則とパターンの実装には時間がかかりすぎます。 





  • コードが煩雑になり、理解しにくくなります。 





一連の記事「InPractice」では、このコードが複雑すぎず、記述に妥当な時間がかかるように、実際の問題で設計原則を実装するケースを示すことによって、これらの先入観を払拭しようとします。これがこのシリーズの最初の記事です。 





出発点

Yii2で、を使用するプロジェクトに取り組んでいます ActiveRecord



クライアントコードは、を使用してデータのセットをロードします ActiveRecord::find()



。 





class ClientClass  


    //  ... 

    public function buildQuery(): ActiveQueryImplementation 
    
        $query = ActiveRecordModel::find(); //   ActiveQuery   
        $query->active()->unfinished(); //  ,     ActiveQuery         

        return $query; //    ActiveQuery      ,  $query->all(); 
    } 

    //  ... 

      
      



このコードは、構成ActiveQueryInterface



されたインスタンスを実装して後で使用できるように返すインスタンスに、固定された一連の条件を適用し  ます。 





リクエストに新しい条件を追加する必要がある場合はどうなりますか?

, , . 





$query->active()->unfinished()->newConditionA()->newConditionB(); 
      
      



! , , . 





, ? ? 





. , , , , . ? ... 





-

 , - SOLID : 





. 





   ,   .  





-,    , , .









class ClientClass  
{ 
    /** 
     * @var ActiveQueryFilter[] 
     */ 
    public $filters = []; //        

    //  ... 

    public function buildQuery(): ActiveQueryImplementation
    
        $query = ActiveRecordModel::find(); 
        $query->active()->unfinished();   
        $this->applyFilters($query); //     

				return $query; 
    } 

    private function applyFilters(ActiveQueryImplementation &$query): void  
    { 
        foreach ($this->filters as $filter) { 
            $filter->applyTo($query); 
        } 
    } 

    //  ... 

      
      



 ActiveQueryFitler



  applyTo()



, . 





interface ActiveQueryFilter 
{ 
    public function applyTo(ActiveQuery $query): void
      
      



 ActiveQueryFilter







class NewConditionsAAndBFilter implements ActiveQueryFilter 
{ 
    public function applyTo(ActiveQuery $query): void  
    
        $query->newCondtionA()->newConditionB(); 
    } 
      
      



 

  •  1回の呼び出しで元のメソッドを完了することで問題を解決 しました(元のコードへの介入を最小限に抑えます)。 





  • 元のメソッドのデフォルトの動作は変更されていません。 





  • 元のメソッドから呼び出された新しいメソッドは applyFilters()



    、独自のロジックを実装していません。すべてのロジックはフィルタークラスに委任されています。したがって、元のクラス全体の動作は変更されません。 












All Articles