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.