3-bo‘lim
Factory Method
Obyektni kim yaratishini vorislarga topshirish - va PHP 8 da buning soddaroq yo'li.
Ushbu bo‘lim mundarijasi
Factory Method - obyekt yaratishni alohida metodga chiqarish va uni vorislarga qoldirish naqshi.
Muammo bitta so'zda: kodda new qayerda yozilgan.
Muammo: tarqoq new #
interface Formatlovchi
{
public function kengaytma(): string;
public function format(array $qatorlar): string;
}
final class CsvFormat implements Formatlovchi
{
public function kengaytma(): string { return 'csv'; }
public function format(array $qatorlar): string
{
$natija = [];
foreach ($qatorlar as $q) {
$natija[] = implode(',', $q);
}
return implode("\n", $natija);
}
}
final class JsonFormat implements Formatlovchi
{
public function kengaytma(): string { return 'json'; }
public function format(array $qatorlar): string
{
return json_encode($qatorlar, JSON_UNESCAPED_UNICODE);
}
}
final class MatnFormat implements Formatlovchi
{
public function kengaytma(): string { return 'txt'; }
public function format(array $qatorlar): string
{
$natija = [];
foreach ($qatorlar as $q) {
$natija[] = sprintf('%-12s %8s', $q[0], $q[1]);
}
return implode("\n", $natija);
}
}
Uchta format tayyor. Endi ularni ishlatadigan kod:
$malumot = [['Klaviatura', '250000'], ['Sichqoncha', '120000']];
function eskiEksport(string $tur, array $malumot): string
{
if ($tur === 'csv') {
$f = new CsvFormat();
} elseif ($tur === 'json') {
$f = new JsonFormat();
} else {
$f = new MatnFormat();
}
return "hisobot.{$f->kengaytma()}\n" . $f->format($malumot);
}
echo eskiEksport('csv', $malumot), PHP_EOL;
echo eskiEksport('json', $malumot), PHP_EOL;
hisobot.csv
Klaviatura,250000
Sichqoncha,120000
hisobot.json
[["Klaviatura","250000"],["Sichqoncha","120000"]]
new bitta joyda emasReal loyihada bunday if zanjiri bir necha faylda
takrorlanadi: kontrollerda, cron skriptida, konsol
buyrug'ida.
Yangi format qo'shganda ularning hammasini topib o'zgartirish kerak. Bittasini unutsangiz - xato faqat ishlab chiqarishda ko'rinadi.
Naqshning maqsadi - new ni bitta boshqariladigan joyga
yig'ish.
Oddiy fabrika #
Eng sodda yechim - yaratishni bitta sinfga yig'ish.
final class FormatFabrikasi
{
public static function yarat(string $tur): Formatlovchi
{
return match ($tur) {
'csv' => new CsvFormat(),
'json' => new JsonFormat(),
'txt' => new MatnFormat(),
default => throw new InvalidArgumentException(
"Noma'lum format: {$tur}"
),
};
}
}
$malumot = [['Klaviatura', '250000'], ['Sichqoncha', '120000']];
foreach (['txt', 'csv'] as $tur) {
$f = FormatFabrikasi::yarat($tur);
echo "--- hisobot.{$f->kengaytma()}", PHP_EOL;
echo $f->format($malumot), PHP_EOL;
}
try {
FormatFabrikasi::yarat('pdf');
} catch (InvalidArgumentException $x) {
echo 'Xato: ', $x->getMessage(), PHP_EOL;
}
--- hisobot.txt
Klaviatura 250000
Sichqoncha 120000
--- hisobot.csv
Klaviatura,250000
Sichqoncha,120000
Xato: Noma'lum format: pdf
"Oddiy fabrika" rasmiy naqsh emas, shunchaki foydali usul. Lekin amalda u eng ko'p ishlatiladigan variant.
Uning kuchli tomoni: match bitta joyda, hamma uni
chaqiradi. Yangi format qo'shish - shu bitta match ga bir
qator.
Zaif tomoni: FormatFabrikasi hamma formatlarni biladi.
Ya'ni ochiq/yopiq tamoyili to'liq bajarilmaydi - fabrika
fayli har safar o'zgaradi.
Ko'p loyihada bu yetarlicha yaxshi. Haqiqiy Factory Method esa bundan bir qadam narida.
Factory Method naqshi #
Endi haqiqiy naqsh. Farqi: yaratish statik funksiya emas, sinfning metodi, va uni voris aniqlaydi.
abstract class HisobotChiqaruvchi
{
// Factory Method: qaysi formatlovchi kerakligini voris hal qiladi
abstract protected function formatlovchiYarat(): Formatlovchi;
// Umumiy jarayon - vorislarda takrorlanmaydi
public function chiqar(string $nom, array $malumot): string
{
$f = $this->formatlovchiYarat();
$tana = $f->format($malumot);
$qator = substr_count($tana, "\n") + 1;
return sprintf(
"%s.%s (%d qator)\n%s",
$nom, $f->kengaytma(), $qator, $tana
);
}
}
final class CsvChiqaruvchi extends HisobotChiqaruvchi
{
protected function formatlovchiYarat(): Formatlovchi
{
return new CsvFormat();
}
}
final class JsonChiqaruvchi extends HisobotChiqaruvchi
{
protected function formatlovchiYarat(): Formatlovchi
{
return new JsonFormat();
}
}
$malumot = [['Klaviatura', '250000'], ['Sichqoncha', '120000']];
/** @var list<HisobotChiqaruvchi> $chiqaruvchilar */
$chiqaruvchilar = [new CsvChiqaruvchi(), new JsonChiqaruvchi()];
foreach ($chiqaruvchilar as $ch) {
echo $ch->chiqar('oylik', $malumot), PHP_EOL;
}
oylik.csv (2 qator)
Klaviatura,250000
Sichqoncha,120000
oylik.json (1 qator)
[["Klaviatura","250000"],["Sichqoncha","120000"]]
Yangi format qo'shish - hech qanday mavjud faylni ochmasdan:
final class XmlFormat implements Formatlovchi
{
public function kengaytma(): string { return 'xml'; }
public function format(array $qatorlar): string
{
$natija = ['<mahsulotlar>'];
foreach ($qatorlar as $q) {
$natija[] = " <mahsulot nom=\"{$q[0]}\" narx=\"{$q[1]}\"/>";
}
$natija[] = '</mahsulotlar>';
return implode("\n", $natija);
}
}
final class XmlChiqaruvchi extends HisobotChiqaruvchi
{
protected function formatlovchiYarat(): Formatlovchi
{
return new XmlFormat();
}
}
$malumot = [['Klaviatura', '250000'], ['Sichqoncha', '120000']];
echo (new XmlChiqaruvchi())->chiqar('oylik', $malumot), PHP_EOL;
oylik.xml (4 qator)
<mahsulotlar>
<mahsulot nom="Klaviatura" narx="250000"/>
<mahsulot nom="Sichqoncha" narx="120000"/>
</mahsulotlar>
Farqni ko'rish #
| Simple Factory | Factory Method | |
|---|---|---|
| Yaratish qayerda | Bitta statik metod | Har vorisda alohida metod |
| Yangi tur qo'shish | match ni tahrirlash | Yangi voris sinf |
| Sinflar soni | Kam | Ko'p |
| Umumiy jarayon | Yo'q | chiqar() da |
| Qachon | Faqat tanlash kerak | Tanlash + umumiy jarayon |
Savol bitta: yaratishdan tashqari umumiy ish bormi?
Bizning misolda bor edi - chiqar() fayl nomini yasaydi,
qatorlarni sanaydi, sarlavha qo'shadi. Bu kod har formatda
bir xil.
Agar shunday umumiy ish bo'lmasa - Factory Method ortiqcha.
match bilan cheklaning.
PHP 8 da soddaroq yo'l #
Ko'p holatda enum fabrikaning o'rnini bosadi:
enum Format: string
{
case Csv = 'csv';
case Json = 'json';
case Matn = 'txt';
public function formatlovchi(): Formatlovchi
{
return match ($this) {
Format::Csv => new CsvFormat(),
Format::Json => new JsonFormat(),
Format::Matn => new MatnFormat(),
};
}
}
$malumot = [['Klaviatura', '250000']];
foreach (Format::cases() as $format) {
$f = $format->formatlovchi();
printf("%-5s -> %s%s", $format->value, $f->format($malumot), PHP_EOL);
}
// satrdan enum ga - noto'g'ri qiymat null qaytaradi
var_dump(Format::tryFrom('pdf'));
echo Format::from('json')->name, PHP_EOL;
csv -> Klaviatura,250000
json -> [["Klaviatura","250000"]]
txt -> Klaviatura 250000
NULL
Json
enum ning uchta ustunligi| Ustunlik | Izoh |
|---|---|
match to'liqligi tekshiriladi | Yangi case qo'shsangiz, PHP xato beradi |
tryFrom tayyor validatsiya | null qaytaradi, istisno kerak emas |
cases() ro'yxatni beradi | Formani to'ldirish uchun qulay |
Birinchi punkt eng qimmatli: enum ga case Pdf qo'shsangiz
va match ga qator qo'shishni unutsangiz, PHP
UnhandledMatchError tashlaydi.
Fabrika sinfida esa default bo'lgani uchun xato ishlab
chiqarishgacha yashirin qoladi.
Kamchiligi: enum ichida barcha turlar sanab o'tiladi -
plagin kabi tashqaridan kengaytirib bo'lmaydi.
Fabrika qachon kerak emas #
final readonly class Nuqta
{
public function __construct(public int $x, public int $y) {}
public function __toString(): string
{
return "({$this->x}, {$this->y})";
}
}
// Bitta tur, murakkab qaror yo'q - fabrika ortiqcha
echo new Nuqta(3, 4), PHP_EOL;
(3, 4)
| Holat | Nima qilish |
|---|---|
| Faqat bitta amalga oshirish bor | To'g'ridan-to'g'ri new |
| Yaratish tanlovi yo'q | new |
| Konstruktor sodda | new |
Fabrika faqat new ni o'raydi | Foydasiz qatlam |
Eng ko'p uchraydigan xato - shunday sinf:
final class NuqtaFabrikasi {
public static function yarat(int $x, int $y): Nuqta {
return new Nuqta($x, $y);
}
}
Bu hech narsa qo'shmaydi. new Nuqta(3, 4) qisqaroq va
aniqroq.
Nomlangan konstruktorlar #
PHP da fabrikaning eng qulay shakli - sinfning o'z statik metodlari:
final readonly class Pul
{
private function __construct(
public int $tiyin,
public string $valyuta,
) {}
public static function somdan(float $som): self
{
return new self((int) round($som * 100), 'UZS');
}
public static function tiyindan(int $tiyin): self
{
return new self($tiyin, 'UZS');
}
public static function nol(): self
{
return new self(0, 'UZS');
}
public function qosh(self $boshqa): self
{
return new self($this->tiyin + $boshqa->tiyin, $this->valyuta);
}
public function __toString(): string
{
return number_format($this->tiyin / 100, 2, '.', ' ') . ' ' . $this->valyuta;
}
}
echo Pul::somdan(12500.75), PHP_EOL;
echo Pul::tiyindan(990), PHP_EOL;
echo Pul::nol()->qosh(Pul::somdan(1500))->qosh(Pul::somdan(2.50)), PHP_EOL;
12 500.75 UZS
9.90 UZS
1 502.50 UZS
private__construct yopiq - obyektni faqat nomlangan metodlar
orqali yaratish mumkin.
Foydasi: new Pul(1250075, 'UZS') degan chalkash chaqiruv
o'rniga Pul::somdan(12500.75) yoziladi. Nom o'zi
tushuntiradi.
Bu ham fabrika, lekin alohida sinf kerak emas. PHP kodida juda ko'p uchraydi.
ifzanjirli eksport funksiyasini yozing va uni sanang.- Uni
matchli oddiy fabrikaga aylantiring. HisobotChiqaruvchiga to'rtinchi vorisni qo'shing.- Qo'shishda nechta mavjud faylni ochganingizni yozing.
- Simple Factory va Factory Method farqini uch qatorda ayting.
Formatenum igacase Pdfqo'shing vamatchni tahrirlamang - xatoni ko'ring.tryFromvafromfarqini sinang.Pulsinfigaayir()metodini qo'shing.- Konstruktor
privatebo'lishining foydasini yozing. - Kodingizdan fabrika kerak bo'lmagan ikkita joyni toping.
Xulosa #
- Factory Method - obyekt yaratishni ajratish naqshi.
- Muammo:
newbir necha faylda tarqalib ketadi. - Simple Factory - bitta
match, GoF naqshi emas, lekin eng ko'p ishlatiladi. - Factory Method - yaratishni voris hal qiladi, umumiy jarayon ota-sinfda qoladi.
- Tanlov mezoni: yaratishdan tashqari umumiy ish bormi.
- PHP 8 da
enum+matchko'p holatda fabrikani almashtiradi. enumustunligi:matchto'liqligi tekshiriladi,tryFromvacases()tayyor.enumkamchiligi: tashqaridan kengaytirib bo'lmaydi.- Bitta tur uchun fabrika - ortiqcha qatlam.
- Nomlangan konstruktorlar (
Pul::somdan()) - PHP dagi eng qulay fabrika shakli.
Keyingi bo'limda bir-biriga bog'liq obyektlar oilasini yaratadigan Abstract Factory 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.