20-bo‘lim
Amaliy loyiha - to'lov tizimi
Sakkizta naqshni bitta ishlaydigan tizimda birlashtirish - va qaysilarini ishlatmaslik.
Ushbu bo‘lim mundarijasi
Yakuniy bo'limda internet-do'kon uchun buyurtma qabul qilish tizimini quramiz. Har naqsh aniq bir muammoni yechish uchun kiritiladi - shakl uchun emas.
Tayyor tizimda quyidagi naqshlar bo'ladi:
| Naqsh | Qaysi muammoni yechadi |
|---|---|
| Builder | Savatni bosqichma-bosqich yig'ish |
| Chain of Responsibility | Tekshiruvlar ketma-ketligi |
| Strategy | To'lov usullari |
| Factory Method | Usulni sozlamadan yaratish |
| Decorator | Jurnal va qayta urinish |
| State | Buyurtma bosqichlari |
| Observer | Buyurtmadan keyingi yon ishlar |
| Facade | Bitta kirish nuqtasi |
1-qism: domen #
final readonly class Pul
{
private function __construct(public int $tiyin) {}
public static function somdan(int $som): self
{
return new self($som * 100);
}
public static function nol(): self
{
return new self(0);
}
public function qosh(self $b): self
{
return new self($this->tiyin + $b->tiyin);
}
public function kopaytir(int $n): self
{
return new self($this->tiyin * $n);
}
public function foizAyir(int $foiz): self
{
return new self($this->tiyin - intdiv($this->tiyin * $foiz, 100));
}
public function som(): int
{
return intdiv($this->tiyin, 100);
}
public function __toString(): string
{
return number_format($this->som(), 0, '.', ' ') . " so'm";
}
}
final readonly class Qator
{
public function __construct(
public string $nom,
public Pul $narx,
public int $soni,
public int $ogirlik,
) {}
public function jami(): Pul
{
return $this->narx->kopaytir($this->soni);
}
}
$q = new Qator('Klaviatura', Pul::somdan(250000), 2, 1);
echo $q->nom, ': ', $q->jami(), PHP_EOL;
echo 'chegirma bilan: ', $q->jami()->foizAyir(15), PHP_EOL;
Klaviatura: 500 000 so'm
chegirma bilan: 425 000 so'm
Pul - nomlangan konstruktorlar (3-bo'lim) va
readonly bilan yozilgan qiymat obyekti. U hech qachon
o'zgarmaydi, har amal yangi obyekt qaytaradi.
2-qism: Builder - savatni yig'ish #
final class SavatQuruvchi
{
/** @var list<Qator> */
private array $qatorlar = [];
private int $chegirma = 0;
private string $mijoz = '';
private int $masofa = 0;
public function mijoz(string $ism): self
{
$this->mijoz = $ism;
return $this;
}
public function qosh(string $nom, int $narx, int $soni, int $ogirlik): self
{
$this->qatorlar[] = new Qator($nom, Pul::somdan($narx), $soni, $ogirlik);
return $this;
}
public function chegirma(int $foiz): self
{
$this->chegirma = $foiz;
return $this;
}
public function masofa(int $km): self
{
$this->masofa = $km;
return $this;
}
public function qur(): Savat
{
if ($this->mijoz === '') {
throw new LogicException('mijoz korsatilmagan');
}
return new Savat($this->mijoz, $this->qatorlar,
$this->chegirma, $this->masofa);
}
}
final readonly class Savat
{
/** @param list<Qator> $qatorlar */
public function __construct(
public string $mijoz,
public array $qatorlar,
public int $chegirma,
public int $masofa,
) {}
public function mahsulotJami(): Pul
{
$jami = Pul::nol();
foreach ($this->qatorlar as $q) {
$jami = $jami->qosh($q->jami());
}
return $jami->foizAyir($this->chegirma);
}
public function ogirlik(): int
{
return array_sum(array_map(
static fn(Qator $q): int => $q->ogirlik * $q->soni, $this->qatorlar));
}
public function soni(): int
{
return count($this->qatorlar);
}
}
function savatQur(): Savat
{
return (new SavatQuruvchi())
->mijoz('Husanboy Qodirov')
->qosh('Klaviatura', 250000, 2, 1)
->qosh('Monitor', 1800000, 1, 8)
->chegirma(10)
->masofa(30)
->qur();
}
$savat = savatQur();
printf("mijoz: %s%s", $savat->mijoz, PHP_EOL);
printf("qatorlar: %d%s", $savat->soni(), PHP_EOL);
printf("ogirlik: %d kg%s", $savat->ogirlik(), PHP_EOL);
printf("jami: %s (chegirma %d%%)%s",
$savat->mahsulotJami(), $savat->chegirma, PHP_EOL);
try {
(new SavatQuruvchi())->qosh('Kabel', 50000, 1, 1)->qur();
} catch (LogicException $x) {
echo 'RAD: ', $x->getMessage(), PHP_EOL;
}
mijoz: Husanboy Qodirov
qatorlar: 2
ogirlik: 10 kg
jami: 2 070 000 so'm (chegirma 10%)
RAD: mijoz korsatilmagan
3-qism: tekshiruvlar zanjiri #
final readonly class Natija
{
private function __construct(
public bool $ok,
public string $xabar,
) {}
public static function togri(): self
{
return new self(true, '');
}
public static function xato(string $xabar): self
{
return new self(false, $xabar);
}
}
abstract class Tekshiruv
{
private ?Tekshiruv $keyingi = null;
public function keyin(Tekshiruv $t): Tekshiruv
{
$this->keyingi = $t;
return $t;
}
final public function tekshir(Savat $s): Natija
{
$natija = $this->qoida($s);
if (!$natija->ok) {
return $natija;
}
return $this->keyingi?->tekshir($s) ?? Natija::togri();
}
abstract protected function qoida(Savat $s): Natija;
}
final class BoshEmas extends Tekshiruv
{
protected function qoida(Savat $s): Natija
{
return $s->soni() > 0
? Natija::togri()
: Natija::xato('savat bosh');
}
}
final class OgirlikChegarasi extends Tekshiruv
{
public function __construct(private readonly int $eng = 30) {}
protected function qoida(Savat $s): Natija
{
return $s->ogirlik() <= $this->eng
? Natija::togri()
: Natija::xato("ogirlik {$s->ogirlik()} kg, chegara {$this->eng} kg");
}
}
final class ChegirmaChegarasi extends Tekshiruv
{
protected function qoida(Savat $s): Natija
{
return $s->chegirma >= 0 && $s->chegirma <= 50
? Natija::togri()
: Natija::xato("chegirma {$s->chegirma}% - ruxsat etilmagan");
}
}
function tekshiruvZanjiri(): Tekshiruv
{
$birinchi = new BoshEmas();
$birinchi->keyin(new OgirlikChegarasi())->keyin(new ChegirmaChegarasi());
return $birinchi;
}
$zanjir = tekshiruvZanjiri();
$holatlar = [
'togri savat' => savatQur(),
'bosh savat' => (new SavatQuruvchi())->mijoz('Malika')->qur(),
'ogir savat' => (new SavatQuruvchi())->mijoz('Nodira')
->qosh('Seyf', 4000000, 1, 120)->qur(),
'katta chegirma' => (new SavatQuruvchi())->mijoz('Aziza')
->qosh('Kabel', 50000, 1, 1)->chegirma(90)->qur(),
];
foreach ($holatlar as $nom => $savat) {
$n = $zanjir->tekshir($savat);
printf('%-16s %s%s', $nom,
$n->ok ? 'OK' : 'RAD: ' . $n->xabar, PHP_EOL);
}
togri savat OK
bosh savat RAD: savat bosh
ogir savat RAD: ogirlik 120 kg, chegara 30 kg
katta chegirma RAD: chegirma 90% - ruxsat etilmagan
4-qism: to'lov strategiyalari va dekoratorlar #
interface Toolov
{
public function nomi(): string;
public function yech(Pul $summa, string $mijoz): Natija;
}
final class Karta implements Toolov
{
public function nomi(): string { return 'karta'; }
public function yech(Pul $summa, string $mijoz): Natija
{
return Natija::togri();
}
}
// Ataylab nosoz - qayta urinishni ko'rsatish uchun
final class Payme implements Toolov
{
private int $urinish = 0;
public function __construct(private readonly int $nechtaXato = 0) {}
public function nomi(): string { return 'payme'; }
public function yech(Pul $summa, string $mijoz): Natija
{
$this->urinish++;
return $this->urinish <= $this->nechtaXato
? Natija::xato("payme javob bermadi ({$this->urinish}-urinish)")
: Natija::togri();
}
}
final class NaqdPul implements Toolov
{
public function nomi(): string { return 'naqd'; }
public function yech(Pul $summa, string $mijoz): Natija
{
return $summa->som() <= 5000000
? Natija::togri()
: Natija::xato('naqd chegara: 5 000 000 som');
}
}
// DEKORATOR: jurnal
final class JurnalliToolov implements Toolov
{
/** @var list<string> */
private array $qatorlar = [];
public function __construct(private readonly Toolov $ichki) {}
public function nomi(): string { return $this->ichki->nomi(); }
public function yech(Pul $summa, string $mijoz): Natija
{
$n = $this->ichki->yech($summa, $mijoz);
$this->qatorlar[] = sprintf('%s %s %s',
$this->ichki->nomi(), $summa, $n->ok ? 'OK' : 'XATO');
return $n;
}
/** @return list<string> */
public function jurnal(): array { return $this->qatorlar; }
}
// DEKORATOR: qayta urinish
final class QaytaUrinuvchi implements Toolov
{
public function __construct(
private readonly Toolov $ichki,
private readonly int $marta = 3,
) {}
public function nomi(): string { return $this->ichki->nomi(); }
public function yech(Pul $summa, string $mijoz): Natija
{
$oxirgi = Natija::xato('urinish qilinmadi');
for ($i = 1; $i <= $this->marta; $i++) {
$oxirgi = $this->ichki->yech($summa, $mijoz);
if ($oxirgi->ok) {
return $oxirgi;
}
}
return $oxirgi;
}
}
// FABRIKA: sozlamadan usul yaratish
enum ToolovTuri: string
{
case Karta = 'karta';
case Payme = 'payme';
case Naqd = 'naqd';
public function yarat(): Toolov
{
return match ($this) {
ToolovTuri::Karta => new Karta(),
ToolovTuri::Payme => new Payme(2),
ToolovTuri::Naqd => new NaqdPul(),
};
}
}
foreach (ToolovTuri::cases() as $tur) {
$t = new JurnalliToolov($tur->yarat());
$n = $t->yech(Pul::somdan(2070000), 'Husanboy');
printf('%-7s %s%s', $tur->value,
$n->ok ? 'OK' : 'XATO: ' . $n->xabar, PHP_EOL);
foreach ($t->jurnal() as $j) {
echo ' ', $j, PHP_EOL;
}
}
karta OK
karta 2 070 000 so'm OK
payme XATO: payme javob bermadi (1-urinish)
payme 2 070 000 so'm XATO
naqd OK
naqd 2 070 000 so'm OK
payme birinchi urinishda yiqildi. Endi uni qayta urinish
dekoratori bilan o'raymiz:
$xom = ToolovTuri::Payme->yarat();
$jurnalli = new JurnalliToolov($xom);
$toolov = new QaytaUrinuvchi($jurnalli, 4);
$n = $toolov->yech(Pul::somdan(2070000), 'Husanboy');
printf('natija: %s%s', $n->ok ? 'OK' : 'XATO', PHP_EOL);
echo '--- jurnal', PHP_EOL;
foreach ($jurnalli->jurnal() as $i => $j) {
printf(' %d. %s%s', $i + 1, $j, PHP_EOL);
}
natija: OK
--- jurnal
1. payme 2 070 000 so'm XATO
2. payme 2 070 000 so'm XATO
3. payme 2 070 000 so'm OK
QaytaUrinuvchi(JurnalliToolov(Payme)) - jurnal ichkarida
turibdi, shuning uchun har urinish yozildi.
Agar teskari qo'ysak - JurnalliToolov(QaytaUrinuvchi(Payme))
- jurnalda faqat yakuniy natija bo'lardi.
9-bo'limda ko'rganimizdek, ikkalasi ham to'g'ri bo'lishi mumkin. Bu yerda har urinishni ko'rish kerak edi, shuning uchun jurnal ichkariga qo'yildi.
5-qism: holat mashinasi #
enum Bosqich: string
{
case Yangi = 'yangi';
case Tolangan = 'tolangan';
case Jonatilgan = 'jonatilgan';
case Bekor = 'bekor';
/** @return list<self> */
public function keyingilari(): array
{
return match ($this) {
Bosqich::Yangi => [Bosqich::Tolangan, Bosqich::Bekor],
Bosqich::Tolangan => [Bosqich::Jonatilgan, Bosqich::Bekor],
Bosqich::Jonatilgan, Bosqich::Bekor => [],
};
}
public function otishMumkinmi(self $keyingi): bool
{
return in_array($keyingi, $this->keyingilari(), true);
}
}
final class Buyurtma
{
private Bosqich $bosqich = Bosqich::Yangi;
/** @var list<string> */
private array $tarix = [];
public function __construct(
public readonly string $raqam,
public readonly Savat $savat,
public readonly Pul $jami,
) {}
public function otkaz(Bosqich $yangi): Natija
{
if (!$this->bosqich->otishMumkinmi($yangi)) {
return Natija::xato(
"{$this->bosqich->value} -> {$yangi->value} mumkin emas");
}
$this->tarix[] = $this->bosqich->value . ' -> ' . $yangi->value;
$this->bosqich = $yangi;
return Natija::togri();
}
public function bosqich(): Bosqich { return $this->bosqich; }
/** @return list<string> */
public function tarix(): array { return $this->tarix; }
}
$b = new Buyurtma('BUY-1001', savatQur(), Pul::somdan(2070000));
foreach ([Bosqich::Tolangan, Bosqich::Jonatilgan, Bosqich::Bekor] as $keyingi) {
$n = $b->otkaz($keyingi);
printf('-> %-11s %s%s', $keyingi->value,
$n->ok ? 'OK' : 'RAD: ' . $n->xabar, PHP_EOL);
}
echo 'tarix: ', implode(' | ', $b->tarix()), PHP_EOL;
-> tolangan OK
-> jonatilgan OK
-> bekor RAD: jonatilgan -> bekor mumkin emas
tarix: yangi -> tolangan | tolangan -> jonatilgan
6-qism: hodisalar #
final readonly class BuyurtmaTolandi
{
public function __construct(public Buyurtma $buyurtma) {}
}
final class Tarqatuvchi
{
/** @var array<string, list<callable>> */
private array $tinglovchilar = [];
/** @var list<string> */
private array $xatolar = [];
public function tingla(string $hodisa, callable $t): self
{
$this->tinglovchilar[$hodisa][] = $t;
return $this;
}
public function tarqat(object $hodisa): void
{
foreach ($this->tinglovchilar[$hodisa::class] ?? [] as $i => $t) {
try {
$t($hodisa);
} catch (Throwable $x) {
// bitta tinglovchi yiqilsa - qolganlari davom etadi
$this->xatolar[] = sprintf('%s#%d: %s',
$hodisa::class, $i, $x->getMessage());
}
}
}
/** @return list<string> */
public function xatolar(): array { return $this->xatolar; }
}
function tarqatuvchiQur(): Tarqatuvchi
{
return (new Tarqatuvchi())
->tingla(BuyurtmaTolandi::class, static function (BuyurtmaTolandi $h): void {
echo " [email] {$h->buyurtma->savat->mijoz}: "
. "{$h->buyurtma->raqam} qabul qilindi", PHP_EOL;
})
->tingla(BuyurtmaTolandi::class, static function (BuyurtmaTolandi $h): void {
throw new RuntimeException('sodiqlik xizmati javob bermadi');
})
->tingla(BuyurtmaTolandi::class, static function (BuyurtmaTolandi $h): void {
echo " [ombor] {$h->buyurtma->savat->soni()} "
. 'qator ajratildi', PHP_EOL;
});
}
$tarqatuvchi = tarqatuvchiQur();
$buyurtma = new Buyurtma('BUY-1002', savatQur(), Pul::somdan(2070000));
$tarqatuvchi->tarqat(new BuyurtmaTolandi($buyurtma));
echo '--- xatolar', PHP_EOL;
foreach ($tarqatuvchi->xatolar() as $x) {
echo ' ', $x, PHP_EOL;
}
[email] Husanboy Qodirov: BUY-1002 qabul qilindi
[ombor] 2 qator ajratildi
--- xatolar
BuyurtmaTolandi#1: sodiqlik xizmati javob bermadi
Sodiqlik xizmati yiqildi, lekin ombor baribir ishladi - 14-bo'limdagi qoida amalda.
7-qism: fasad - hammasini birlashtirish #
final readonly class BuyurtmaNatijasi
{
public function __construct(
public bool $ok,
public string $xabar,
public ?Buyurtma $buyurtma = null,
) {}
}
final class Kassa
{
private int $hisoblagich = 1000;
public function __construct(
private readonly Tekshiruv $tekshiruv,
private readonly Tarqatuvchi $tarqatuvchi,
) {}
public function rasmiylashtir(Savat $savat, ToolovTuri $tur): BuyurtmaNatijasi
{
// 1. tekshiruvlar zanjiri
$n = $this->tekshiruv->tekshir($savat);
if (!$n->ok) {
return new BuyurtmaNatijasi(false, 'Tekshiruv: ' . $n->xabar);
}
// 2. buyurtma yaratiladi
$buyurtma = new Buyurtma(
'BUY-' . ++$this->hisoblagich,
$savat,
$savat->mahsulotJami(),
);
// 3. to'lov - fabrika + dekoratorlar
$toolov = new QaytaUrinuvchi($tur->yarat(), 4);
$n = $toolov->yech($buyurtma->jami, $savat->mijoz);
if (!$n->ok) {
return new BuyurtmaNatijasi(false, 'Tolov: ' . $n->xabar, $buyurtma);
}
// 4. holat o'tishi
$n = $buyurtma->otkaz(Bosqich::Tolangan);
if (!$n->ok) {
return new BuyurtmaNatijasi(false, 'Holat: ' . $n->xabar, $buyurtma);
}
// 5. yon ishlar - hodisa orqali
$this->tarqatuvchi->tarqat(new BuyurtmaTolandi($buyurtma));
return new BuyurtmaNatijasi(true, 'Qabul qilindi', $buyurtma);
}
}
function kassaQur(): Kassa
{
return new Kassa(tekshiruvZanjiri(), tarqatuvchiQur());
}
$kassa = kassaQur();
echo '=== 1. muvaffaqiyatli buyurtma', PHP_EOL;
$n = $kassa->rasmiylashtir(savatQur(), ToolovTuri::Karta);
printf('%s | %s | %s%s',
$n->ok ? 'OK' : 'XATO', $n->xabar,
$n->buyurtma?->raqam ?? '-', PHP_EOL);
printf('bosqich: %s%s', $n->buyurtma?->bosqich()->value ?? '-', PHP_EOL);
=== 1. muvaffaqiyatli buyurtma
[email] Husanboy Qodirov: BUY-1001 qabul qilindi
[ombor] 2 qator ajratildi
OK | Qabul qilindi | BUY-1001
bosqich: tolangan
Nosoz provayder bilan - qayta urinish ishlaydi:
$kassa = kassaQur();
echo '=== 2. payme (2 marta yiqiladi, 3-da otadi)', PHP_EOL;
$n = $kassa->rasmiylashtir(savatQur(), ToolovTuri::Payme);
printf('%s | %s%s', $n->ok ? 'OK' : 'XATO', $n->xabar, PHP_EOL);
echo '=== 3. ogir savat - tekshiruvda toxtaydi', PHP_EOL;
$ogir = (new SavatQuruvchi())->mijoz('Kamola')
->qosh('Seyf', 4000000, 1, 120)->qur();
$n = $kassa->rasmiylashtir($ogir, ToolovTuri::Karta);
printf('%s | %s%s', $n->ok ? 'OK' : 'XATO', $n->xabar, PHP_EOL);
echo '=== 4. naqd chegarasi', PHP_EOL;
$qimmat = (new SavatQuruvchi())->mijoz('Dilnoza Yusupova')
->qosh('Server', 9000000, 1, 20)->qur();
$n = $kassa->rasmiylashtir($qimmat, ToolovTuri::Naqd);
printf('%s | %s%s', $n->ok ? 'OK' : 'XATO', $n->xabar, PHP_EOL);
printf('buyurtma bosqichi: %s%s',
$n->buyurtma?->bosqich()->value ?? '-', PHP_EOL);
=== 2. payme (2 marta yiqiladi, 3-da otadi)
[email] Husanboy Qodirov: BUY-1001 qabul qilindi
[ombor] 2 qator ajratildi
OK | Qabul qilindi
=== 3. ogir savat - tekshiruvda toxtaydi
XATO | Tekshiruv: ogirlik 120 kg, chegara 30 kg
=== 4. naqd chegarasi
XATO | Tolov: naqd chegara: 5 000 000 som
buyurtma bosqichi: yangi
To'rtinchi holatga e'tibor bering: to'lov o'tmagani uchun
buyurtma yangi bosqichida qoldi va hech qanday hodisa
tarqatilmadi.
Nima uchun aynan shu naqshlar #
$qaror = [
['Builder', 'savat bosqichli quriladi', 'ha'],
['Chain of Resp.', 'tekshiruvlar soni ozgaradi', 'ha'],
['Strategy', '3 tolov usuli', 'ha'],
['Factory', 'usul sozlamadan keladi', 'ha'],
['Decorator', 'jurnal + qayta urinish', 'ha'],
['State', 'notogri otish taqiqlanadi', 'ha'],
['Observer', 'yon ishlar, soni ozgaradi', 'ha'],
['Facade', 'oqim 4 joyda chaqiriladi', 'ha'],
['Singleton', 'kerak emas - injeksiya bor', 'yoq'],
['Prototype', 'nusxa olish kerak emas', 'yoq'],
['Composite', 'daraxt yoq', 'yoq'],
['Proxy', 'kechiktirish kerak emas', 'yoq'],
];
printf("%-16s %-32s %s%s", 'Naqsh', 'Sabab', 'Ishlatildi', PHP_EOL);
echo str_repeat('-', 62), PHP_EOL;
foreach ($qaror as $q) {
printf("%-16s %-32s %s%s", ...[...$q, PHP_EOL]);
}
$ishlatilgan = count(array_filter($qaror,
static fn(array $q): bool => $q[2] === 'ha'));
printf('%sishlatildi: %d, ishlatilmadi: %d%s',
PHP_EOL, $ishlatilgan, count($qaror) - $ishlatilgan, PHP_EOL);
Naqsh Sabab Ishlatildi
--------------------------------------------------------------
Builder savat bosqichli quriladi ha
Chain of Resp. tekshiruvlar soni ozgaradi ha
Strategy 3 tolov usuli ha
Factory usul sozlamadan keladi ha
Decorator jurnal + qayta urinish ha
State notogri otish taqiqlanadi ha
Observer yon ishlar, soni ozgaradi ha
Facade oqim 4 joyda chaqiriladi ha
Singleton kerak emas - injeksiya bor yoq
Prototype nusxa olish kerak emas yoq
Composite daraxt yoq yoq
Proxy kechiktirish kerak emas yoq
ishlatildi: 8, ishlatilmadi: 4
Yuqoridagi jadvalning pastki qismi - loyihaning eng muhim qismlaridan biri.
To'rtta naqsh ataylab ishlatilmadi, chunki ular yechadigan muammo bu tizimda yo'q:
| Naqsh | Qachon kerak bo'lardi |
|---|---|
| Singleton | Global holat majburiy bo'lganda (deyarli hech qachon) |
| Prototype | Buyurtma andozalari qo'shilsa |
| Composite | Mahsulot to'plamlari (komplekt ichida komplekt) bo'lsa |
| Proxy | Katalogni kechiktirib yuklash kerak bo'lsa |
19-bo'limdagi asosiy fikr shu edi: naqshni muammo chaqiradi, bilim emas.
Tizimni kengaytirish #
Yangi to'lov usuli qo'shamiz - hech qanday mavjud faylni
ochmasdan bo'lmaydi, chunki enum da yangi case kerak.
Lekin o'zgarish bitta joyda:
final class BolibTolash implements Toolov
{
public function __construct(private readonly int $oylar = 6) {}
public function nomi(): string { return "bolib-{$this->oylar}"; }
public function yech(Pul $summa, string $mijoz): Natija
{
if ($summa->som() < 500000) {
return Natija::xato('bolib tolash uchun eng kam summa 500 000');
}
$oylik = intdiv($summa->som(), $this->oylar);
echo " [bolib] oyiga {$oylik} som x {$this->oylar}", PHP_EOL;
return Natija::togri();
}
}
// Dekoratorlar, tekshiruvlar, holat mashinasi, hodisalar -
// hech biri o'zgarmadi
$t = new QaytaUrinuvchi(new BolibTolash(6), 2);
$n = $t->yech(Pul::somdan(2070000), 'Husanboy');
printf('%s: %s%s', $t->nomi(), $n->ok ? 'OK' : $n->xabar, PHP_EOL);
$n = $t->yech(Pul::somdan(200000), 'Malika');
printf('kichik summa: %s%s', $n->ok ? 'OK' : $n->xabar, PHP_EOL);
[bolib] oyiga 345000 som x 6
bolib-6: OK
kichik summa: bolib tolash uchun eng kam summa 500 000
Yangi tekshiruv qoidasi ham xuddi shunday - faqat zanjirga qo'shiladi:
final class MijozIsmi extends Tekshiruv
{
protected function qoida(Savat $s): Natija
{
return mb_strlen($s->mijoz) >= 3
? Natija::togri()
: Natija::xato('mijoz ismi juda qisqa');
}
}
$zanjir = new BoshEmas();
$zanjir->keyin(new MijozIsmi())
->keyin(new OgirlikChegarasi())
->keyin(new ChegirmaChegarasi());
$qisqa = (new SavatQuruvchi())->mijoz('Ali')
->qosh('Kabel', 50000, 1, 1)->qur();
$juda = (new SavatQuruvchi())->mijoz('Ab')
->qosh('Kabel', 50000, 1, 1)->qur();
printf('"Ali": %s%s', $zanjir->tekshir($qisqa)->ok ? 'OK' : 'RAD', PHP_EOL);
printf('"Ab": %s%s',
$zanjir->tekshir($juda)->ok ? 'OK' : 'RAD: '
. $zanjir->tekshir($juda)->xabar, PHP_EOL);
"Ali": OK
"Ab": RAD: mijoz ismi juda qisqa
Tizim haqida raqamlar #
$sinflar = [
'domen' => ['Pul', 'Qator', 'Savat', 'Buyurtma', 'Natija'],
'quruvchi' => ['SavatQuruvchi'],
'tekshiruv' => ['Tekshiruv', 'BoshEmas', 'OgirlikChegarasi',
'ChegirmaChegarasi'],
'tolov' => ['Toolov', 'Karta', 'Payme', 'NaqdPul', 'ToolovTuri'],
'dekorator' => ['JurnalliToolov', 'QaytaUrinuvchi'],
'holat' => ['Bosqich'],
'hodisa' => ['BuyurtmaTolandi', 'Tarqatuvchi'],
'fasad' => ['Kassa', 'BuyurtmaNatijasi'],
];
foreach ($sinflar as $guruh => $royxat) {
printf('%-11s %2d: %s%s', $guruh, count($royxat),
implode(', ', array_slice($royxat, 0, 3))
. (count($royxat) > 3 ? '...' : ''), PHP_EOL);
}
printf('%sjami %d ta tur%s', PHP_EOL,
array_sum(array_map('count', $sinflar)), PHP_EOL);
echo 'chaqiruvchi kod biladigan turlar: Kassa, ToolovTuri, ',
'SavatQuruvchi = 3', PHP_EOL;
domen 5: Pul, Qator, Savat...
quruvchi 1: SavatQuruvchi
tekshiruv 4: Tekshiruv, BoshEmas, OgirlikChegarasi...
tolov 5: Toolov, Karta, Payme...
dekorator 2: JurnalliToolov, QaytaUrinuvchi
holat 1: Bosqich
hodisa 2: BuyurtmaTolandi, Tarqatuvchi
fasad 2: Kassa, BuyurtmaNatijasi
jami 22 ta tur
chaqiruvchi kod biladigan turlar: Kassa, ToolovTuri, SavatQuruvchi = 3
Halol javob: bog'liq.
Agar bu do'kon bir yil ishlab, uchta to'lov usuli va to'rtta tekshiruvi bo'lsa - bu me'yorda.
Agar u bir martalik skript bo'lsa va hech qachon kengaytirilmasa - 22 tur o'rniga 100 qatorlik bitta fayl yetarli edi.
Muhim ko'rsatkich - chaqiruvchi kod nechta turni biladi.
Bizda bu uchta: SavatQuruvchi, Kassa, ToolovTuri.
Qolgan 19 turi ichkarida va ular o'zgarganda tashqi kod tegilmaydi. Naqshlarning asosiy va'dasi shu edi.
- Ushbu tizimni noldan yozing (kod ko'chirmang, qayta yozing).
- Yangi to'lov usuli qo'shing - nechta mavjud fayl o'zgardi?
- Yangi tekshiruv qoidasi qo'shing.
- Tekshiruvlar tartibini almashtiring va natija o'zgarishini ko'ring.
- Jurnal dekoratorini tashqariga chiqaring - jurnal qanday o'zgardi?
- Holat mashinasiga
Yetkazilganbosqichini qo'shing. - Yangi hodisa turi qo'shing (
BuyurtmaBekorQilindi). - Bitta tinglovchida istisno tashlang - qolganlari ishladimi?
- Har naqsh uchun "nima uchun kerak" degan bir jumla yozing.
- Ishlatmagan to'rtta naqshdan birini qo'shishga sabab toping - topolmasangiz qo'shmang.
Xulosa #
- Bu loyihada sakkizta naqsh ishlatildi, har biri aniq bir muammoni yechish uchun.
- Builder savatni bosqichli qurdi va
qur()da tekshirdi. - Chain of Responsibility tekshiruvlarni mustaqil sinflarga ajratdi.
- Strategy va Factory to'lov usullarini sozlamadan tanlash imkonini berdi.
- Decorator jurnal va qayta urinishni asosiy kodga tegmasdan qo'shdi.
- State noto'g'ri bosqich o'tishlarini taqiqladi.
- Observer yon ishlarni ajratdi va bitta tinglovchining yiqilishi qolganlarini to'xtatmadi.
- Facade butun oqimni bitta metod ortiga yashirdi.
- To'rtta naqsh ataylab ishlatilmadi - ularning muammosi bu tizimda yo'q edi.
- Asosiy o'lchov: 22 turdan chaqiruvchi kod faqat uchtasini biladi.
Darslik shu bilan yakunlandi. Endi eng muhim maslahat: kodni naqshlarga moslashtirmang - naqshlarni kodingizdagi haqiqiy muammolarga moslashtiring.
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.