بمجرد الدخول في محادثة عمل ، لاحظ أحد زملائي المبرمجين أن جميع أنواع مبادئ وأنماط تصميم البرامج جيدة للاستخدام عند القيام بمهام الاختبار ، ولكن في المشاريع القتالية الحقيقية ، لا يمكن تطبيقها عادةً. لماذا هذا؟ هناك سببان رئيسيان:
تستغرق المبادئ والأنماط وقتًا طويلاً في التنفيذ.
يصبح الرمز مرهقًا ويصعب فهمه.
في سلسلة من المقالات "عمليًا" سأحاول تبديد هذه الأفكار المسبقة من خلال توضيح الحالات التي تنفذ مبادئ التصميم في المشكلات العملية بطريقة لا تكون هذه التعليمات البرمجية معقدة للغاية وتستغرق وقتًا معقولاً للكتابة. ها هي المقالة الأولى في هذه السلسلة.
نقطة البداية
نحن نعمل على مشروع في 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();
}
}
لقد حللنا مشكلتنا عن طريق إكمال الطريقة الأصلية بمكالمة واحدة فقط (الحد الأدنى من التدخل في الكود الأصلي).
لم يتغير السلوك الافتراضي للطريقة الأصلية.
الطريقة الجديدة التي يتم استدعاؤها من الطريقة الأصلية
applyFilters()
لا تنفذ منطقها الخاص - يتم تفويض كل منطق إلى فئات التصفية. وهكذا ، تركنا سلوك الفصل الأصلي بأكمله دون تغيير.