Une fois dans une conversation de travail, l'un de mes collègues programmeurs a remarqué que toutes sortes de principes et de modèles de conception de logiciels sont bons à utiliser lors de tâches de test, mais dans de vrais projets de combat, ils ne sont généralement pas applicables. Pourquoi donc? Il y a deux principales raisons:
Les principes et modèles prennent trop de temps à mettre en œuvre.
Le code devient lourd et difficile à comprendre.
Dans une série d'articles «En pratique», j'essaierai de dissiper ces idées préconçues en démontrant des cas qui implémentent des principes de conception dans des tâches pratiques de telle sorte que ce code ne soit pas trop compliqué et qu'il prenne un temps raisonnable pour l'écrire. Voici le premier article de cette série.
Le point de départ
Nous travaillons sur un projet en Yii2 , dans lequel nous utilisons ActiveRecord
. Le code client charge un ensemble de données à l'aide de ActiveRecord::find()
.
class ClientClass
{
// ...
public function buildQuery(): ActiveQueryImplementation
{
$query = ActiveRecordModel::find(); // ActiveQuery
$query->active()->unfinished(); // , ActiveQuery
return $query; // ActiveQuery , $query->all();
}
// ...
}
Ce code applique un ActiveQueryInterface
ensemble fixe de conditions à l' instance d'implémentation et renvoie l'instance configurée pour une utilisation future.
Que faire si vous devez ajouter de nouvelles conditions à la demande?
, , .
$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();
}
}
Nous avons résolu notre problème en complétant la méthode d'origine avec un seul appel (intervention minimale dans le code d'origine).
Le comportement par défaut de la méthode d'origine n'a pas changé.
La nouvelle méthode appelée à partir de la méthode d'origine n'implémente
applyFilters()
pas sa propre logique - toute la logique est déléguée aux classes de filtre. Ainsi, nous avons laissé inchangé le comportement de toute la classe d'origine.