FEST in der Praxis. Open-Closed-Prinzip und ActiveQuery Yii2

Einmal in einem Arbeitsgespräch bemerkte einer meiner Programmierkollegen, dass alle möglichen Prinzipien und Muster des Software-Designs bei Testaufgaben gut anzuwenden sind, aber in realen Kampfprojekten sind sie normalerweise nicht anwendbar. Warum so? Es gibt zwei Hauptgründe: 





  • Die Implementierung von Prinzipien und Mustern dauert zu lange. 





  • Der Code wird umständlich und schwer zu verstehen. 





In einer Reihe von Artikeln "In der Praxis" werde ich versuchen, diese Vorurteile zu zerstreuen, indem ich Fälle demonstriere, in denen Konstruktionsprinzipien in praktischen Aufgaben so implementiert werden, dass dieser Code nicht zu kompliziert ist und das Schreiben eine angemessene Zeit in Anspruch nimmt. Hier ist der erste Artikel in dieser Reihe. 





Der Startpunkt

Wir arbeiten an einem Projekt in Yii2 , in dem wir verwenden  ActiveRecord



. Der Client-Code lädt einen Datensatz mit dem  ActiveRecord::find()







class ClientClass  


    //  ... 

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

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

    //  ... 

      
      



Dieser Code wendet einen ActiveQueryInterface



festen Satz von Bedingungen auf die implementierende Instanz an   und gibt die konfigurierte Instanz für die zukünftige Verwendung zurück. 





Was ist, wenn Sie der Anfrage neue Bedingungen hinzufügen müssen?

, , . 





$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(); 
    } 
      
      



 

  • Wir haben unser Problem gelöst, indem wir  die  ursprüngliche Methode mit nur einem Aufruf abgeschlossen haben (minimaler Eingriff in den ursprünglichen Code). 





  • Das Standardverhalten der ursprünglichen Methode wurde nicht geändert. 





  • Die neue Methode, die von der ursprünglichen Methode aufgerufen wurde,  applyFilters()



    implementiert keine eigene Logik - die gesamte Logik wird an die Filterklassen delegiert. Somit haben wir das Verhalten der gesamten ursprünglichen Klasse unverändert gelassen. 












All Articles