19-bo‘lim

Naqshlarni noto'g'ri ishlatish

Naqsh bilan buzilgan kod naqshsiz buzilgan koddan qiyinroq tuzatiladi.

🕑 17 daqiqa o‘qish 📄 1 307 so‘z 👁 1 marta ko‘rilgan
Ushbu bo‘lim mundarijasi
  1. 1. Kargo-kult: muammosiz naqsh
  2. 2. Bitta amalga oshiruvchili interfeys
  3. 3. Naqshni noto'g'ri masalaga qo'llash
  4. 4. Erta umumlashtirish
  5. 5. Abstraksiyani olib tashlash
  6. 6. Naqshlarning ortiqcha qavati
  7. 7. Naqsh nomlari sinf nomiga aylanganda
  8. 8. Anemik model
  9. Tekshiruv ro'yxati
  10. Xulosa

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 #

PHP
// "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);
    }
}
PHP
$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;
Natija
Salom, Husanboy!
Salom, Husanboy!
To'rt sinf, bitta interfeys - bir qatorlik ish uchun

Yuqoridagi ikkala kod aynan bir xil natija beradi.

Naqshli versiyaOddiy funksiya
Fayllar41
Kod qatorlari~304
Tushunish uchun ochiladigan fayl41
Almashtirilishi mumkin bo'lgan qism1 (lekin hech qachon almashmaydi)-

Bu kargo-kult: naqshning shakli bor, mazmuni yo'q.

Belgilari:

BelgiNima uchun yomon
Interfeysning bitta amalga oshiruvchisi borAbstraksiya hech narsa yashirmaydi
Fabrika faqat new ni o'raydiQaror yo'q
"Xizmat" sinfi faqat uzatadiQatlam 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 #

PHP
// 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;
Natija
Husanboy
(yoq)
Interfeys qachon oqlanadi

"Har sinfga interfeys" qoidasi keng tarqalgan, lekin u o'ylab qo'llanishi kerak.

Interfeys oqlanadi, agar:

SababMisol
Ikkinchi amalga oshiruvchi borSms, Telegram
Testda soxta dublyor kerakTashqi API, to'lov tizimi
Chegara belgilanadiModullar orasidagi shartnoma
Kutubxona chiqaryapsizFoydalanuvchi o'zinikini yozadi

Interfeys oqlanmaydi, agar:

HolatIzoh
Bitta amalga oshiruvchi va ikkinchisi kutilmaydiFoydasiz
Faqat "qoida shunday" degani uchunKargo-kult
Sinfning hamma metodi interfeysdaInterfeys 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 #

PHP
// 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;
Natija
boshlandi -> tekshirildi -> saqlandi -> xabar berildi
Bu Observer emas - bu oddiy ketma-ketlik

Kod ishlaydi, lekin naqsh noto'g'ri tanlangan:

Observer nima va'da qiladiBu kodda
Obunachilar mustaqilBir-biriga bog'liq
Tartib muhim emasTartib hal qiluvchi
Soni o'zgaruvchanAniq uchta qadam
Natija kerak emasHar qadam natijani uzatadi

Bunday kodni oddiy metod bilan yozish aniqroq:

KOD
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 #

PHP
// "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;
Natija
Oylik hisobot
Oltita abstrakt metoddan bittasi ishlatilgan

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.

YondashuvNatija
Oldindan abstraksiya qurishNoto'g'ri joyda, ishlatilmaydi
Ehtiyoj ko'ringach qurishTo'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:

PHP
// 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);
Natija
Salom, Husanboy!
Assalomu alaykum, Malika!
--- solishtirish
interfeys+fabrika+xizmat 4 fayl
bitta sinf             1 fayl
"O'zgaruvchanlik" har doim ham naqsh talab qilmaydi

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:

BosqichQachon yetarli
1. KonstantaO'zgarmaydi
2. ParametrQiymat o'zgaradi
3. Yopilma (callable)Mantiq o'zgaradi, sodda
4. Interfeys + sinflarMantiq murakkab, holati bor
5. Naqshlar birikmasiBir necha o'lchov

Ko'p dasturchi darhol 4-bosqichdan boshlaydi. Aksariyat masala esa 2-bosqichda hal bo'ladi.

6. Naqshlarning ortiqcha qavati #

PHP
// 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;
Natija
    jurnal: 10 -> 15
15
15
chuqurlik: 4 qatlam, natija: 10 + 5
To'rt qatlam - $x + 5 uchun

Har qatlam alohida qaraganda to'g'ri va foydali. Birgalikda esa:

SavolJavob topish
bajar(10) nima qaytaradi4 faylni o'qish kerak
Nosozlik steki4 daraja chuqur
Kesh qachon tozalanadiHech qachon - xotira sizishi
Tekshiruv keshdan oldinmiTartibga 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 soniBaho
1-2Yaxshi
3-4Chegarada
5+Qayta ko'rib chiqing

7. Naqsh nomlari sinf nomiga aylanganda #

PHP
$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);
}
Natija
Yomon                                  Yaxshi
----------------------------------------------------------
FoydalanuvchiManager                   Foydalanuvchi
BuyurtmaHelper                         Buyurtma
TolovProcessorFactoryImpl              KartaToolovi
MalumotUtils                           SanaOraligi
AbstractBaseHisobotServiceProvider     OylikHisobot
Manager, Helper, Utils - nima qilishini aytmaydi

Bu 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 #

PHP
// 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;
Natija
425000
-2000000
Chegirma 500% bo'ldi va jami manfiy

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 modelTo'liq model
public maydonlarprivate maydonlar
Qoidalar servisdaQoidalar sinf ichida
Har kim o'zgartiradiFaqat metodlar orqali
Noto'g'ri holat mumkinKonstruktor 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.

PHP
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;
Natija
425000
RAD: chegirma 0-50% oraligida bolishi kerak, berildi: 500
jami ozgarmadi: 425000
Abstraksiya zinapoyasi - pastdan yuqoriga 1. Konstanta hech qachon o'zgarmaydi 2. Parametr qiymat o'zgaradi - masalalarning ko'pi shu yerda tugaydi 3. Yopilma (callable) mantiq o'zgaradi, lekin sodda 4. Interfeys + sinflar mantiq murakkab, holati va nomi bor 5. Naqshlar birikmasi bir necha o'lchov bir vaqtda o'zgaradi narx o'sadi fayllar, qatlamlar, tushunish vaqti Bu yerdan boshlang va faqat ehtiyoj ko'ringanda yuqoriga chiqing Eng ko'p uchraydigan xato Naqshni endigina o'rgangan dasturchi to'g'ridan-to'g'ri 4-5-bosqichdan boshlaydi.
Har bosqich oldingisidan qimmatroq - shoshilmang

Tekshiruv ro'yxati #

Naqsh qo'shishdan oldin o'zingizga bering:

Savol"Yo'q" bo'lsa
Muammo hozir mavjudmiQo'shmang
Kamida ikkita haqiqiy variant bormiQo'shmang
Uchinchi marta takrorlanyaptimiKuting
Naqshsiz yechim ancha yomonmiQo'shmang
Jamoa bu naqshni biladimiAvval tushuntiring
Parametr yoki yopilma yetarli emasmiSoddaroq yo'lni tanlang

Va naqsh qo'shgandan keyin:

Savol"Ha" bo'lsa
Fayllar soni ikki barobar oshdimiQayta ko'rib chiqing
Bir amalni tushunish 5 fayl talab qiladimiQatlamni kamaytiring
Interfeysning bitta amalga oshiruvchisi qoldimiInterfeysni olib tashlang
Yangi hamkasb tushunmadimiSodda yozing
Amaliy topshiriq
  1. Bir qatorlik ishni to'rt sinfga o'rang va qatorlarni sanang.
  2. Uni bitta funksiyaga qaytaring.
  3. Kodingizdan bitta amalga oshiruvchili interfeyslarni toping.
  4. Ularning qaysilari haqiqatan kerakligini belgilang.
  5. Tartib muhim bo'lgan "Observer" ni oddiy metodga aylantiring.
  6. Ishlatilmaydigan abstrakt metodlarni toping va o'chiring.
  7. To'rt qavatli dekorator zanjiri yozing va nosozlik stekiga qarang.
  8. Manager, Helper, Utils bilan tugaydigan sinflarni sanang.
  9. Anemik modelni to'liq modelga aylantiring.
  10. 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 new ni 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, Utils nomlari 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.

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.