Una vez en una conversación de trabajo, uno de mis compañeros programadores notó que todo tipo de principios y patrones de diseño de software son buenos para usar cuando se realizan tareas de prueba, pero en proyectos de combate reales, generalmente no son aplicables. ¿Porqué es eso? Hay dos razones principales:
Los principios y patrones tardan demasiado en implementarse.
El código se vuelve engorroso y difícil de entender.
En una serie de artículos "En la práctica" intentaré disipar estas ideas preconcebidas demostrando casos que implementan principios de diseño en tareas prácticas de tal manera que este código no sea demasiado complicado y lleve un tiempo razonable escribirlo. Aquí está el primer artículo de esta serie.
El punto de partida
Estamos trabajando en un proyecto en Yii2 , en el que usamos ActiveRecord
. El código de cliente carga un conjunto de datos usando ActiveRecord::find()
.
class ClientClass
{
// ...
public function buildQuery(): ActiveQueryImplementation
{
$query = ActiveRecordModel::find(); // ActiveQuery
$query->active()->unfinished(); // , ActiveQuery
return $query; // ActiveQuery , $query->all();
}
// ...
}
Este código aplica un ActiveQueryInterface
conjunto fijo de condiciones a la instancia de implementación y devuelve la instancia configurada para su uso posterior.
¿Qué sucede si necesita agregar nuevas condiciones a la solicitud?
, , .
$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();
}
}
Hemos resuelto nuestro problema completando el método original con una sola llamada (intervención mínima en el código original).
El comportamiento predeterminado del método original no ha cambiado.
El nuevo método llamado desde el método original
applyFilters()
no implementa su propia lógica; toda la lógica se delega a las clases de filtro. Por tanto, hemos dejado sin cambios el comportamiento de toda la clase original.