4-bo‘lim
Abstract Factory
Bitta emas, bir-biriga mos keladigan obyektlar oilasini yaratish.
Ushbu bo‘lim mundarijasi
Factory Method bitta obyekt yaratadi. Abstract Factory esa bir-biriga mos keladigan obyektlar oilasini yaratadi.
Asosiy g'oya: oiladagi qismlarni aralashtirib bo'lmasin.
Muammo: aralashib ketgan oila #
Interfeys elementlarini matn ko'rinishida chizamiz. Ikkita mavzu bor: yorug' va qorong'i.
interface Tugma
{
public function chiz(string $matn): string;
}
interface Panel
{
public function chiz(string $sarlavha, string $ichi): string;
}
final class YorugTugma implements Tugma
{
public function chiz(string $matn): string
{
return "[ {$matn} ]";
}
}
final class QorongTugma implements Tugma
{
public function chiz(string $matn): string
{
return "< {$matn} >";
}
}
final class YorugPanel implements Panel
{
public function chiz(string $sarlavha, string $ichi): string
{
$chiziq = str_repeat('-', 28);
return "+{$chiziq}+\n| " . str_pad($sarlavha, 26) . " |\n"
. "| " . str_pad($ichi, 26) . " |\n+{$chiziq}+";
}
}
final class QorongPanel implements Panel
{
public function chiz(string $sarlavha, string $ichi): string
{
$chiziq = str_repeat('=', 28);
return "#{$chiziq}#\n# " . str_pad($sarlavha, 26) . " #\n"
. "# " . str_pad($ichi, 26) . " #\n#{$chiziq}#";
}
}
Endi ularni qo'lda yig'amiz - va xato qilamiz:
// Diqqat: panel qorong'i, tugma yorug'
$panel = new QorongPanel();
$tugma = new YorugTugma();
echo $panel->chiz('Sozlamalar', $tugma->chiz('Saqlash')), PHP_EOL;
#============================#
# Sozlamalar #
# [ Saqlash ] #
#============================#
Panel # va = bilan chizildi (qorong'i uslub), tugma esa
[ ] bilan (yorug' uslub). Ikki oila aralashib ketdi.
Hech qanday xato yo'q, hech qanday istisno yo'q. Faqat foydalanuvchi buzuq interfeysni ko'radi.
Bunday xatolar eng yomoni, chunki:
| Sabab | Oqibati |
|---|---|
| Turlar tekshiruvi o'tadi | Ikkalasi ham to'g'ri interfeysni bajaradi |
| Test odatda tutmaydi | Har biri alohida to'g'ri ishlaydi |
| Faqat ko'z bilan ko'rinadi | Dizayner shikoyat qilgach topiladi |
Muammoning ildizi: oila haqidagi bilim kodda yo'q. Hech kim "qorong'i panel qorong'i tugma bilan yuradi" degan qoidani yozib qo'ymagan.
Yechim: oila fabrikasi #
interface InterfeysFabrikasi
{
public function nomi(): string;
public function tugmaYarat(): Tugma;
public function panelYarat(): Panel;
}
final class YorugFabrika implements InterfeysFabrikasi
{
public function nomi(): string { return 'yorug'; }
public function tugmaYarat(): Tugma { return new YorugTugma(); }
public function panelYarat(): Panel { return new YorugPanel(); }
}
final class QorongFabrika implements InterfeysFabrikasi
{
public function nomi(): string { return 'qorong'; }
public function tugmaYarat(): Tugma { return new QorongTugma(); }
public function panelYarat(): Panel { return new QorongPanel(); }
}
// Mijoz kod faqat fabrikani biladi, konkret sinflarni emas
function sozlamaOynasi(InterfeysFabrikasi $fabrika): string
{
$panel = $fabrika->panelYarat();
$tugma = $fabrika->tugmaYarat();
return $panel->chiz('Sozlamalar', $tugma->chiz('Saqlash'));
}
foreach ([new YorugFabrika(), new QorongFabrika()] as $f) {
echo "--- mavzu: {$f->nomi()}", PHP_EOL;
echo sozlamaOynasi($f), PHP_EOL;
}
--- mavzu: yorug
+----------------------------+
| Sozlamalar |
| [ Saqlash ] |
+----------------------------+
--- mavzu: qorong
#============================#
# Sozlamalar #
# < Saqlash > #
#============================#
Endi har ikkala mavzu butun: yorug'da +, - va [ ];
qorong'ida #, = va < >. Aralashish imkonsiz, chunki
mijoz kod konkret sinf nomini umuman ko'rmaydi.
Buni tekshirib ham ko'rish mumkin:
$oyna = sozlamaOynasi(new QorongFabrika());
printf("panel qorong'imi: %s%s",
str_contains($oyna, '#====') ? 'ha' : 'yoq', PHP_EOL);
printf("tugma qorong'imi: %s%s",
str_contains($oyna, '< Saqlash >') ? 'ha' : 'yoq', PHP_EOL);
printf("yorug' iz qoldimi: %s%s",
str_contains($oyna, '[ Saqlash ]') ? 'ha' : 'yoq', PHP_EOL);
panel qorong'imi: ha
tugma qorong'imi: ha
yorug' iz qoldimi: yoq
Uchinchi oila qo'shish #
final class QavsTugma implements Tugma
{
public function chiz(string $matn): string
{
return "(( {$matn} ))";
}
}
final class QavsPanel implements Panel
{
public function chiz(string $sarlavha, string $ichi): string
{
$chiziq = str_repeat('~', 28);
return "({$chiziq})\n( " . str_pad($sarlavha, 26) . " )\n"
. "( " . str_pad($ichi, 26) . " )\n({$chiziq})";
}
}
final class QavsFabrika implements InterfeysFabrikasi
{
public function nomi(): string { return 'qavs'; }
public function tugmaYarat(): Tugma { return new QavsTugma(); }
public function panelYarat(): Panel { return new QavsPanel(); }
}
// sozlamaOynasi() umuman o'zgarmadi
echo sozlamaOynasi(new QavsFabrika()), PHP_EOL;
(~~~~~~~~~~~~~~~~~~~~~~~~~~~~)
( Sozlamalar )
( (( Saqlash )) )
(~~~~~~~~~~~~~~~~~~~~~~~~~~~~)
Uchta yangi sinf yozildi, bitta ham mavjud fayl o'zgarmadi.
Naqshning narxi #
Abstract Factory ikki o'lchovda o'sadi: oilalar va mahsulotlar.
$oilalar = ['Yorug', 'Qorong', 'Qavs'];
$mahsulotlar = ['Tugma', 'Panel'];
printf("%-10s %-14s %-14s%s", 'oila', $mahsulotlar[0], $mahsulotlar[1], PHP_EOL);
echo str_repeat('-', 40), PHP_EOL;
foreach ($oilalar as $oila) {
printf("%-10s %-14s %-14s%s", $oila,
$oila . $mahsulotlar[0], $oila . $mahsulotlar[1], PHP_EOL);
}
printf("%s%d mahsulot sinfi + %d fabrika sinfi + %d interfeys = %d fayl%s",
PHP_EOL,
count($oilalar) * count($mahsulotlar),
count($oilalar),
count($mahsulotlar) + 1,
count($oilalar) * count($mahsulotlar) + count($oilalar)
+ count($mahsulotlar) + 1,
PHP_EOL);
oila Tugma Panel
----------------------------------------
Yorug YorugTugma YorugPanel
Qorong QorongTugma QorongPanel
Qavs QavsTugma QavsPanel
6 mahsulot sinfi + 3 fabrika sinfi + 3 interfeys = 12 fayl
Yangi oila qo'shish oson: bitta fabrika + ikkita mahsulot sinfi, mavjud kod tegilmaydi.
Lekin yangi mahsulot turi (masalan Maydon) qo'shish
uchun:
| Nima o'zgaradi | Nechta fayl |
|---|---|
Maydon interfeysi | 1 (yangi) |
InterfeysFabrikasi interfeysi | 1 (o'zgaradi) |
| Har bir fabrika sinfi | 3 (o'zgaradi) |
Har oila uchun Maydon sinfi | 3 (yangi) |
Jami 8 fayl, ulardan 4 tasi mavjud. Interfeysga metod qo'shilgani uchun uni bajaruvchi barcha sinflar buziladi
- to hammasi yozilmaguncha kod ishlamaydi.
Bu naqshning asosiy cheklovi: oila qo'shish arzon, mahsulot qo'shish qimmat.
Shuning uchun Abstract Factory ni faqat mahsulotlar ro'yxati barqaror bo'lganda ishlating.
Qachon ishlatiladi #
| Holat | Mos keladimi |
|---|---|
| UI mavzulari (yorug'/qorong'i) | Ha |
| Baza drayverlari (ulanish + so'rov quruvchi + migratsiya) | Ha |
| Test va ishlab chiqarish muhitlari | Ha |
| Operatsion tizimga qarab fayl va yo'l ishlovchilari | Ha |
| Bir-biriga bog'liq bo'lmagan obyektlar | Yo'q |
| Faqat bitta oila bor | Yo'q |
// Bir-biriga bog'liq bo'lmagan obyektlar - fabrika kerak emas
final readonly class Sozlama
{
public function __construct(
public string $kalit,
public string $qiymat,
) {}
}
$sozlamalar = [
new Sozlama('til', 'uz'),
new Sozlama('mavzu', 'qorong'),
];
foreach ($sozlamalar as $s) {
printf("%-6s = %s%s", $s->kalit, $s->qiymat, PHP_EOL);
}
til = uz
mavzu = qorong
Ko'p loyihada mavzu bitta bo'ladi va hech qachon ikkinchisi qo'shilmaydi.
Shunday holatda InterfeysFabrikasi interfeysi va
YorugFabrika sinfi hech narsa qo'shmaydi - ular faqat
new YorugTugma() ni o'raydi.
Ikkinchi oila paydo bo'lgandan keyin naqshni kiritish oson: mavjud sinflar allaqachon interfeyslarni bajaradi, faqat fabrika qo'shiladi.
Bu 1-bo'limdagi uch marta qoidasi ning aynan o'zi.
Fabrikani tanlash #
Amalda fabrika odatda sozlamadan tanlanadi:
function fabrikaTanla(string $mavzu): InterfeysFabrikasi
{
return match ($mavzu) {
'yorug' => new YorugFabrika(),
'qorong' => new QorongFabrika(),
'qavs' => new QavsFabrika(),
default => new YorugFabrika(),
};
}
$sozlama = ['mavzu' => 'qorong'];
$fabrika = fabrikaTanla($sozlama['mavzu']);
echo 'tanlangan: ', $fabrika->nomi(), PHP_EOL;
echo $fabrika->tugmaYarat()->chiz('Kirish'), PHP_EOL;
// noma'lum qiymat - zaxira variant
echo fabrikaTanla('mavjud-emas')->nomi(), PHP_EOL;
tanlangan: qorong
< Kirish >
yorug
Bu match - butun dasturdagi yagona joy bo'lib, u
konkret fabrika sinflarini biladi. Qolgan hamma kod faqat
interfeys bilan ishlaydi.
| Factory Method | Abstract Factory | |
|---|---|---|
| Nechta mahsulot | Bitta | Bir necha (oila) |
| Asosiy mexanizm | Meros - voris metodni qayta yozadi | Kompozitsiya - fabrika uzatiladi |
| Kengaytirish | Yangi voris sinf | Yangi fabrika sinfi |
| Asosiy maqsad | Yaratishni ajratish | Oila butunligi |
Amalda Abstract Factory ning har bir metodi - bu bitta Factory Method. Ya'ni ikkinchisi birinchisining to'plami.
- Ikkita mavzu uchun
TugmavaPanelsinflarini yozing. - Ularni ataylab aralashtiring va natijani ko'ring.
InterfeysFabrikasiinterfeysini yozing va mijoz funksiyasini unga o'tkazing.- Uchinchi mavzu qo'shing - mijoz kodi o'zgardimi?
Maydondegan yangi mahsulot turi qo'shing va nechta mavjud fayl o'zgarganini sanang.- Nima uchun oila qo'shish arzon, mahsulot qo'shish qimmatligini tushuntiring.
fabrikaTanlafunksiyasinienumbilan qayta yozing.- Mijoz kodida konkret sinf nomi qolib ketmaganini
grepbilan tekshiring. - Factory Method va Abstract Factory farqini uch qatorda yozing.
- Bu naqsh kerak emas bo'lgan ikkita holat yozing.
Xulosa #
- Abstract Factory - bir-biriga mos obyektlar oilasini yaratadi.
- Asosiy maqsad - oila qismlarini aralashtirishning oldini olish.
- Aralashish xatosi turlar tekshiruvida ham, testda ham ko'rinmaydi.
- Mijoz kod faqat fabrika interfeysini biladi, konkret sinflarni emas.
- Yangi oila qo'shish arzon: mavjud fayllar tegilmaydi.
- Yangi mahsulot turi qo'shish qimmat: interfeys va barcha fabrikalar o'zgaradi.
- Shuning uchun mahsulotlar ro'yxati barqaror bo'lishi kerak.
- Konkret fabrikani tanlash dasturdagi yagona
matchda qoladi. - Factory Method merosga, Abstract Factory kompozitsiyaga tayanadi.
- Oila bitta bo'lsa yoki obyektlar bog'liq bo'lmasa - naqsh kerak emas.
Keyingi bo'limda ko'p parametrli obyektlarni bosqichma-bosqich quradigan Builder ni ko'ramiz.
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.