Suatu kali dalam percakapan kerja, salah satu rekan pemrogram saya memperhatikan bahwa semua jenis prinsip dan pola desain perangkat lunak baik untuk digunakan saat melakukan tugas pengujian, tetapi dalam proyek nyata, pertempuran, biasanya tidak berlaku. Mengapa demikian? Ada dua alasan utama:
Prinsip dan pola membutuhkan waktu terlalu lama untuk diterapkan.
Kode menjadi rumit dan sulit dipahami.
Dalam serangkaian artikel "Dalam Praktek", saya akan mencoba menghilangkan prasangka ini dengan mendemonstrasikan kasus yang menerapkan prinsip desain dalam tugas praktis sedemikian rupa sehingga kode ini tidak terlalu rumit dan perlu waktu yang wajar untuk menulisnya. Inilah artikel pertama dari seri ini.
Titik awal
Kami sedang mengerjakan proyek di Yii2 , yang kami gunakan ActiveRecord
. Kode klien memuat sekumpulan data menggunakan ActiveRecord::find()
.
class ClientClass
{
// ...
public function buildQuery(): ActiveQueryImplementation
{
$query = ActiveRecordModel::find(); // ActiveQuery
$query->active()->unfinished(); // , ActiveQuery
return $query; // ActiveQuery , $query->all();
}
// ...
}
Kode ini menerapkan ActiveQueryInterface
sekumpulan kondisi tetap ke instance yang mengimplementasikan dan mengembalikan instance yang dikonfigurasi untuk digunakan nanti.
Bagaimana jika Anda perlu menambahkan ketentuan baru ke permintaan?
, , .
$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();
}
}
Kami telah memecahkan masalah kami dengan menyelesaikan metode asli hanya dengan satu panggilan (intervensi minimal dalam kode asli).
Perilaku default dari metode asli tidak berubah.
Metode baru yang dipanggil dari metode asli
applyFilters()
tidak menerapkan logikanya sendiri - semua logika didelegasikan ke kelas filter. Jadi, kami membiarkan perilaku seluruh kelas asli tidak berubah.