19-bo‘lim
Naqshlarni noto'g'ri ishlatish
Naqsh bilan buzilgan kod naqshsiz buzilgan koddan qiyinroq tuzatiladi.
Ushbu bo‘lim mundarijasi
Oldingi o'n sakkiz bo'limda naqshlar nima uchun kerakligini ko'rdik. Bu bo'lim - qarama-qarshi tomon: naqsh qachon zarar keltiradi.
Bu mavzu muhim, chunki naqshni o'rgangan dasturchi uni qo'llashga shoshiladi. Natijada oddiy masala uch qavatli abstraksiyaga o'raladi.
1. Kargo-kult: muammosiz naqsh #
// "To'g'ri" yozilgan versiya
interface SalomlashuvchiInterfeysi
{
public function salomlash(string $ism): string;
}
final class OddiySalomlashuvchi implements SalomlashuvchiInterfeysi
{
public function salomlash(string $ism): string
{
return "Salom, {$ism}!";
}
}
final class SalomlashuvchiFabrikasi
{
public static function yarat(): SalomlashuvchiInterfeysi
{
return new OddiySalomlashuvchi();
}
}
final class SalomlashuvXizmati
{
public function __construct(
private readonly SalomlashuvchiInterfeysi $salomlashuvchi,
) {}
public function bajar(string $ism): string
{
return $this->salomlashuvchi->salomlash($ism);
}
}
$xizmat = new SalomlashuvXizmati(SalomlashuvchiFabrikasi::yarat());
echo $xizmat->bajar('Husanboy'), PHP_EOL;
// Va bu - xuddi shu natija
function salomlash(string $ism): string
{
return "Salom, {$ism}!";
}
echo salomlash('Husanboy'), PHP_EOL;
Salom, Husanboy!
Salom, Husanboy!
Yuqoridagi ikkala kod aynan bir xil natija beradi.
| Naqshli versiya | Oddiy funksiya | |
|---|---|---|
| Fayllar | 4 | 1 |
| Kod qatorlari | ~30 | 4 |
| Tushunish uchun ochiladigan fayl | 4 | 1 |
| Almashtirilishi mumkin bo'lgan qism | 1 (lekin hech qachon almashmaydi) | - |
Bu kargo-kult: naqshning shakli bor, mazmuni yo'q.
Belgilari:
| Belgi | Nima uchun yomon |
|---|---|
| Interfeysning bitta amalga oshiruvchisi bor | Abstraksiya hech narsa yashirmaydi |
Fabrika faqat new ni o'raydi | Qaror yo'q |
| "Xizmat" sinfi faqat uzatadi | Qatlam bo'sh |
| Sinf nomida naqsh nomi | ...Factory, ...Strategy, ...Manager |
Oxirgisi kuchli belgi: yaxshi kodda sinf nomi domendan
olinadi (Chegirma, Buyurtma), naqsh nomidan emas.
2. Bitta amalga oshiruvchili interfeys #
// Kodingizda shunday joylar bormi?
interface FoydalanuvchiOmboriInterfeysi
{
public function top(int $id): ?string;
}
final class FoydalanuvchiOmbori implements FoydalanuvchiOmboriInterfeysi
{
/** @var array<int, string> */
private array $malumot = [1 => 'Husanboy', 2 => 'Malika'];
public function top(int $id): ?string
{
return $this->malumot[$id] ?? null;
}
}
$ombor = new FoydalanuvchiOmbori();
echo $ombor->top(1) ?? '(yoq)', PHP_EOL;
echo $ombor->top(9) ?? '(yoq)', PHP_EOL;
Husanboy
(yoq)
"Har sinfga interfeys" qoidasi keng tarqalgan, lekin u o'ylab qo'llanishi kerak.
Interfeys oqlanadi, agar:
| Sabab | Misol |
|---|---|
| Ikkinchi amalga oshiruvchi bor | Sms, Telegram |
| Testda soxta dublyor kerak | Tashqi API, to'lov tizimi |
| Chegara belgilanadi | Modullar orasidagi shartnoma |
| Kutubxona chiqaryapsiz | Foydalanuvchi o'zinikini yozadi |
Interfeys oqlanmaydi, agar:
| Holat | Izoh |
|---|---|
| Bitta amalga oshiruvchi va ikkinchisi kutilmaydi | Foydasiz |
| Faqat "qoida shunday" degani uchun | Kargo-kult |
| Sinfning hamma metodi interfeysda | Interfeys sinfning nusxasi |
Uchinchi qator nozik: interfeys sinfning umumiy qismini ifodalashi kerak. Agar u shunchaki barcha public metodlarni takrorlasa, u hech narsa umumlashtirmaydi.
PHP da testlar uchun interfeys shart emas - sinfni ham
soxtalashtirish mumkin (agar u final bo'lmasa). Lekin
interfeys niyatni aniqroq ko'rsatadi.
3. Naqshni noto'g'ri masalaga qo'llash #
// Observer - lekin tartib MUHIM
final class NotogriOqim
{
/** @var list<callable> */
private array $qadamlar = [];
public function qosh(callable $q): void
{
$this->qadamlar[] = $q;
}
public function ishga(array $malumot): array
{
foreach ($this->qadamlar as $q) {
$malumot = $q($malumot);
}
return $malumot;
}
}
$oqim = new NotogriOqim();
$oqim->qosh(static fn(array $d): array => [...$d, 'tekshirildi']);
$oqim->qosh(static fn(array $d): array => [...$d, 'saqlandi']);
$oqim->qosh(static fn(array $d): array => [...$d, 'xabar berildi']);
echo implode(' -> ', $oqim->ishga(['boshlandi'])), PHP_EOL;
boshlandi -> tekshirildi -> saqlandi -> xabar berildi
Kod ishlaydi, lekin naqsh noto'g'ri tanlangan:
| Observer nima va'da qiladi | Bu kodda |
|---|---|
| Obunachilar mustaqil | Bir-biriga bog'liq |
| Tartib muhim emas | Tartib hal qiluvchi |
| Soni o'zgaruvchan | Aniq uchta qadam |
| Natija kerak emas | Har qadam natijani uzatadi |
Bunday kodni oddiy metod bilan yozish aniqroq:
public function ishga(array $malumot): array {
$malumot = $this->tekshir($malumot);
$malumot = $this->saqla($malumot);
return $this->xabarBer($malumot);
}
Bu yerda tartib ko'rinadi, IDE metodlarni topadi, va kimdir tasodifan qadam qo'shib tartibni buzolmaydi.
14-bo'limdagi qoidani eslang: tartib muhim bo'lsa - Observer noto'g'ri tanlov.
4. Erta umumlashtirish #
// "Kelajakda kerak bo'ladi" deb yozilgan kod
abstract class HisobotAsosi
{
abstract protected function sarlavha(): string;
abstract protected function qatorlar(): array;
abstract protected function altSarlavha(): string;
abstract protected function kolontitul(): string;
abstract protected function valyuta(): string;
abstract protected function tilKodi(): string;
final public function chiqar(): string
{
return $this->sarlavha();
}
}
// Bir yildan keyin - yagona voris
final class OylikHisobot extends HisobotAsosi
{
protected function sarlavha(): string { return 'Oylik hisobot'; }
protected function qatorlar(): array { return []; }
protected function altSarlavha(): string { return ''; }
protected function kolontitul(): string { return ''; }
protected function valyuta(): string { return 'UZS'; }
protected function tilKodi(): string { return 'uz'; }
}
echo (new OylikHisobot())->chiqar(), PHP_EOL;
Oylik hisobot
chiqar() faqat sarlavha() ni chaqiradi. Qolgan beshtasi
bo'sh qiymat qaytaradi va hech kimga kerak emas.
Bu erta umumlashtirish. Muallif "kelajakda kerak bo'ladi" deb o'ylagan, lekin kelajak boshqacha bo'lib chiqqan.
Amaliy kuzatuv: oldindan o'ylab topilgan kengaytirish nuqtalari deyarli har doim noto'g'ri joyda bo'ladi. Haqiqiy ehtiyoj kutilmagan tomondan keladi.
| Yondashuv | Natija |
|---|---|
| Oldindan abstraksiya qurish | Noto'g'ri joyda, ishlatilmaydi |
| Ehtiyoj ko'ringach qurish | To'g'ri joyda, ishlatiladi |
YAGNI (You Aren't Gonna Need It) - bu shunchaki "dangasalik" emas, bu kuzatuvga asoslangan qoida.
5. Abstraksiyani olib tashlash #
Naqshni qo'shish kabi, uni olib tashlash ham mahorat. Yuqoridagi salomlashuv misolini tekshiramiz:
// Abstraksiya olib tashlangan versiya
final readonly class Salomlashuv
{
public function __construct(private string $shablon = 'Salom, %s!') {}
public function bajar(string $ism): string
{
return sprintf($this->shablon, $ism);
}
}
$s = new Salomlashuv();
echo $s->bajar('Husanboy'), PHP_EOL;
// Kerak bo'lsa - shablonni almashtirish yetarli
echo (new Salomlashuv('Assalomu alaykum, %s!'))->bajar('Malika'), PHP_EOL;
echo '--- solishtirish', PHP_EOL;
printf('%-22s %d fayl%s', 'interfeys+fabrika+xizmat', 4, PHP_EOL);
printf('%-22s %d fayl%s', 'bitta sinf', 1, PHP_EOL);
Salom, Husanboy!
Assalomu alaykum, Malika!
--- solishtirish
interfeys+fabrika+xizmat 4 fayl
bitta sinf 1 fayl
Yuqorida salomlashuv matni o'zgaruvchan bo'ldi - lekin buning uchun interfeys ham, strategiya ham kerak bo'lmadi.
Parametr yetarli edi.
Naqshga o'tishdan oldin quyidagi zinapoyani sinab ko'ring:
| Bosqich | Qachon yetarli |
|---|---|
| 1. Konstanta | O'zgarmaydi |
| 2. Parametr | Qiymat o'zgaradi |
3. Yopilma (callable) | Mantiq o'zgaradi, sodda |
| 4. Interfeys + sinflar | Mantiq murakkab, holati bor |
| 5. Naqshlar birikmasi | Bir necha o'lchov |
Ko'p dasturchi darhol 4-bosqichdan boshlaydi. Aksariyat masala esa 2-bosqichda hal bo'ladi.
6. Naqshlarning ortiqcha qavati #
// Har qavat "to'g'ri", lekin birgalikda - o'qib bo'lmaydi
interface Amal { public function bajar(int $x): int; }
final class Qoshuvchi implements Amal
{
public function __construct(private readonly int $qancha) {}
public function bajar(int $x): int { return $x + $this->qancha; }
}
final class Jurnalli implements Amal
{
public function __construct(private readonly Amal $ichki) {}
public function bajar(int $x): int
{
$n = $this->ichki->bajar($x);
echo " jurnal: {$x} -> {$n}", PHP_EOL;
return $n;
}
}
final class Keshli implements Amal
{
/** @var array<int, int> */
private array $kesh = [];
public function __construct(private readonly Amal $ichki) {}
public function bajar(int $x): int
{
return $this->kesh[$x] ??= $this->ichki->bajar($x);
}
}
final class Tekshiruvchi implements Amal
{
public function __construct(private readonly Amal $ichki) {}
public function bajar(int $x): int
{
if ($x < 0) { throw new InvalidArgumentException('manfiy'); }
return $this->ichki->bajar($x);
}
}
$amal = new Tekshiruvchi(new Keshli(new Jurnalli(new Qoshuvchi(5))));
echo $amal->bajar(10), PHP_EOL;
echo $amal->bajar(10), PHP_EOL; // keshdan
echo 'chuqurlik: 4 qatlam, natija: 10 + 5', PHP_EOL;
jurnal: 10 -> 15
15
15
chuqurlik: 4 qatlam, natija: 10 + 5
$x + 5 uchunHar qatlam alohida qaraganda to'g'ri va foydali. Birgalikda esa:
| Savol | Javob topish |
|---|---|
bajar(10) nima qaytaradi | 4 faylni o'qish kerak |
| Nosozlik steki | 4 daraja chuqur |
| Kesh qachon tozalanadi | Hech qachon - xotira sizishi |
| Tekshiruv keshdan oldinmi | Tartibga bog'liq, ko'rinmaydi |
Uchinchi qator haqiqiy xato: Keshli da chegara yo'q, demak
uzoq ishlaydigan jarayonda xotira to'xtovsiz o'sadi.
Amaliy o'lchov: bitta amalni tushunish uchun nechta fayl ochish kerak?
| Fayl soni | Baho |
|---|---|
| 1-2 | Yaxshi |
| 3-4 | Chegarada |
| 5+ | Qayta ko'rib chiqing |
7. Naqsh nomlari sinf nomiga aylanganda #
$yomon = [
'FoydalanuvchiManager',
'BuyurtmaHelper',
'TolovProcessorFactoryImpl',
'MalumotUtils',
'AbstractBaseHisobotServiceProvider',
];
$yaxshi = [
'Foydalanuvchi',
'Buyurtma',
'KartaToolovi',
'SanaOraligi',
'OylikHisobot',
];
printf("%-38s %s%s", 'Yomon', 'Yaxshi', PHP_EOL);
echo str_repeat('-', 58), PHP_EOL;
foreach ($yomon as $i => $n) {
printf("%-38s %s%s", $n, $yaxshi[$i], PHP_EOL);
}
Yomon Yaxshi
----------------------------------------------------------
FoydalanuvchiManager Foydalanuvchi
BuyurtmaHelper Buyurtma
TolovProcessorFactoryImpl KartaToolovi
MalumotUtils SanaOraligi
AbstractBaseHisobotServiceProvider OylikHisobot
Manager, Helper, Utils - nima qilishini aytmaydiBu qo'shimchalar hech narsa anglatmaydi. FoydalanuvchiManager
foydalanuvchini yaratadimi, o'chiradimi, tekshiradimi -
noma'lum.
Natijada bunday sinflar hamma narsani o'ziga tortadi va ming qatorlik "xudo-sinf" ga aylanadi.
Yaxshi nom - domen tilida: Buyurtma, Chegirma,
YetkazishUsuli. Jamoadagi dasturchi bo'lmagan odam ham
tushunadigan so'zlar.
Istisno: naqsh nomi haqiqatan ma'lumot bersa, uni
qoldirish mumkin - SmsGatewayAdapter degan nom "bu tashqi
kutubxonani moslashtiradi" deb aniq aytadi.
8. Anemik model #
// Ma'lumot bir joyda, mantiq boshqa joyda
final class AnemikBuyurtma
{
public int $summa = 0;
public string $holat = 'yangi';
public int $chegirma = 0;
}
final class BuyurtmaServis
{
public function chegirmaQoy(AnemikBuyurtma $b, int $foiz): void
{
$b->chegirma = $foiz;
}
public function jami(AnemikBuyurtma $b): int
{
return $b->summa - intdiv($b->summa * $b->chegirma, 100);
}
}
$b = new AnemikBuyurtma();
$b->summa = 500000;
$servis = new BuyurtmaServis();
$servis->chegirmaQoy($b, 15);
echo $servis->jami($b), PHP_EOL;
// Hech kim to'sqinlik qilmaydi:
$b->chegirma = 500;
echo $servis->jami($b), PHP_EOL;
425000
-2000000
AnemikBuyurtma - shunchaki maydonlar to'plami. Uning
qoidalari yo'q, shuning uchun uni buzish oson.
To'g'ri yondashuv - qoidalarni ma'lumot bilan bir joyda saqlash:
| Anemik model | To'liq model |
|---|---|
public maydonlar | private maydonlar |
| Qoidalar servisda | Qoidalar sinf ichida |
| Har kim o'zgartiradi | Faqat metodlar orqali |
| Noto'g'ri holat mumkin | Konstruktor kafolat beradi |
Anemik model naqsh emas, lekin u naqshlar bilan tez-tez birga keladi: "Service", "Manager", "Handler" sinflari paydo bo'lganda ma'lumot sinflari bo'shab qoladi.
final class TogriBuyurtma
{
private int $chegirma = 0;
public function __construct(private readonly int $summa)
{
if ($summa <= 0) {
throw new InvalidArgumentException('summa musbat bolishi kerak');
}
}
public function chegirmaQoy(int $foiz): void
{
if ($foiz < 0 || $foiz > 50) {
throw new InvalidArgumentException(
"chegirma 0-50% oraligida bolishi kerak, berildi: {$foiz}");
}
$this->chegirma = $foiz;
}
public function jami(): int
{
return $this->summa - intdiv($this->summa * $this->chegirma, 100);
}
}
$b = new TogriBuyurtma(500000);
$b->chegirmaQoy(15);
echo $b->jami(), PHP_EOL;
try {
$b->chegirmaQoy(500);
} catch (InvalidArgumentException $x) {
echo 'RAD: ', $x->getMessage(), PHP_EOL;
}
echo 'jami ozgarmadi: ', $b->jami(), PHP_EOL;
425000
RAD: chegirma 0-50% oraligida bolishi kerak, berildi: 500
jami ozgarmadi: 425000
Tekshiruv ro'yxati #
Naqsh qo'shishdan oldin o'zingizga bering:
| Savol | "Yo'q" bo'lsa |
|---|---|
| Muammo hozir mavjudmi | Qo'shmang |
| Kamida ikkita haqiqiy variant bormi | Qo'shmang |
| Uchinchi marta takrorlanyaptimi | Kuting |
| Naqshsiz yechim ancha yomonmi | Qo'shmang |
| Jamoa bu naqshni biladimi | Avval tushuntiring |
| Parametr yoki yopilma yetarli emasmi | Soddaroq yo'lni tanlang |
Va naqsh qo'shgandan keyin:
| Savol | "Ha" bo'lsa |
|---|---|
| Fayllar soni ikki barobar oshdimi | Qayta ko'rib chiqing |
| Bir amalni tushunish 5 fayl talab qiladimi | Qatlamni kamaytiring |
| Interfeysning bitta amalga oshiruvchisi qoldimi | Interfeysni olib tashlang |
| Yangi hamkasb tushunmadimi | Sodda yozing |
- Bir qatorlik ishni to'rt sinfga o'rang va qatorlarni sanang.
- Uni bitta funksiyaga qaytaring.
- Kodingizdan bitta amalga oshiruvchili interfeyslarni toping.
- Ularning qaysilari haqiqatan kerakligini belgilang.
- Tartib muhim bo'lgan "Observer" ni oddiy metodga aylantiring.
- Ishlatilmaydigan abstrakt metodlarni toping va o'chiring.
- To'rt qavatli dekorator zanjiri yozing va nosozlik stekiga qarang.
Manager,Helper,Utilsbilan tugaydigan sinflarni sanang.- Anemik modelni to'liq modelga aylantiring.
- Tekshiruv ro'yxatini o'z loyihangizga qo'llang va natijani yozing.
Xulosa #
- Kargo-kult - naqshning shakli bor, muammosi yo'q.
- Bitta amalga oshiruvchili interfeys hech narsani yashirmaydi.
- Faqat
newni o'raydigan fabrika foydasiz qatlam. - Naqshni noto'g'ri masalaga qo'llash undan ham yomonroq: tartib muhim bo'lsa Observer noto'g'ri.
- Erta umumlashtirish deyarli har doim noto'g'ri joyda bo'ladi.
- Abstraksiyani olib tashlash ham mahorat.
- Zinapoya: konstanta → parametr → yopilma → interfeys → naqshlar birikmasi.
- Ko'p qatlam alohida to'g'ri bo'lib, birgalikda o'qib bo'lmas kod hosil qiladi.
Manager,Helper,Utilsnomlari hech narsa anglatmaydi - domen tilida nomlang.- Anemik model: qoidalar ma'lumotdan ajralsa, noto'g'ri holat oson yuzaga keladi.
Keyingi, yakuniy bo'limda bir necha naqshni birga ishlatadigan to'liq loyiha quramiz.
O‘qish tarixini saqlamoqchimisiz?
Tizimga kirsangiz, tugatgan bo‘limlaringiz saqlanadi va qoldirgan joyingizdan davom etasiz.
Xatolik topdingizmi?
Imlo xatosi, ishlamaydigan kod yoki noto‘g‘ri ma‘lumotni ko‘rsangiz - bizga xabar bering. Har bir xabar administrator tomonidan ko‘rib chiqiladi.