Interface va abstract class

~20 daq · PHP · OOP — Ob'ektga yo'naltirilgan PHP

Muammodan boshlaylik

Do'koningiz to'lovlarni Payme orqali qabul qiladi. Kod hamma joyda PaymeTolov classini ishlatadi. Bir kuni buyurtmachi: "Click ni ham qo'shing, keyinroq Uzum ni ham".

PaymeTolov ishlatilgan 15 ta joyga if ($tizim === 'click') ... elseif ... qo'shasizmi? Har yangi tizimda yana 15 joyni o'zgartirasizmi?

Kerakli narsa — kelishuv: "to'lov tizimi qanday bo'lishidan qat'i nazar, unda tolash() va qaytarish() metodlari bo'ladi". Kod esa aniq tizimni emas, shu kelishuvni biladi. PHP da bunday kelishuv — interface.


Interface — kelishuv (shartnoma)

Interface faqat qanday metodlar bo'lishi kerakligini aytadi — ular qanday ishlashini emas:

<?php

interface TolovTizimi
{
    public function tolash(int $summa, string $buyurtmaId): string;   // tranzaksiya ID qaytaradi
    public function qaytarish(string $tranzaksiyaId): bool;
}

Classlar bu kelishuvni amalga oshiradi (implements):

<?php

class PaymeTolov implements TolovTizimi
{
    public function tolash(int $summa, string $buyurtmaId): string
    {
        // Payme API ga so'rov...
        return 'payme_' . bin2hex(random_bytes(8));
    }

    public function qaytarish(string $tranzaksiyaId): bool
    {
        // Payme API ga so'rov...
        return true;
    }
}

class ClickTolov implements TolovTizimi
{
    public function tolash(int $summa, string $buyurtmaId): string
    {
        // Click API ga so'rov — butunlay boshqacha ishlaydi
        return 'click_' . bin2hex(random_bytes(8));
    }

    public function qaytarish(string $tranzaksiyaId): bool
    {
        return true;
    }
}

Agar class interface dagi biror metodni yozmasa — PHP xato beradi:

Fatal error: Class ClickTolov contains 1 abstract method and must therefore be declared abstract
or implement the remaining methods (TolovTizimi::qaytarish)

Kelishuvni buzib bo'lmaydi.


Interface ning foydasi

Endi buyurtma kodi aniq tizimni bilmaydi:

<?php

class BuyurtmaXizmati
{
    public function __construct(private TolovTizimi $tolov) {}

    public function rasmiylashtirish(string $buyurtmaId, int $summa): string
    {
        $tranzaksiya = $this->tolov->tolash($summa, $buyurtmaId);
        // buyurtmani bazaga saqlash...
        return $tranzaksiya;
    }
}

// Qaysi tizim ishlatilishini bitta joyda hal qilamiz
$tolov = match ($_POST['tolov_turi'] ?? 'payme') {
    'click' => new ClickTolov(),
    default => new PaymeTolov(),
};

$xizmat = new BuyurtmaXizmati($tolov);
$xizmat->rasmiylashtirish('B-1043', 315000);

Uzum qo'shish kerak bo'lsa:

  1. UzumTolov implements TolovTizimi classini yozasiz
  2. match ga bitta qator qo'shasiz

BuyurtmaXizmati — o'zgarmaydi. Bu OOP ning eng muhim tamoyillaridan biri: kod kengaytirish uchun ochiq, o'zgartirish uchun yopiq.

Test yozishda

<?php

class SoxtaTolov implements TolovTizimi
{
    public function tolash(int $summa, string $buyurtmaId): string { return 'test_123'; }
    public function qaytarish(string $tranzaksiyaId): bool { return true; }
}

$xizmat = new BuyurtmaXizmati(new SoxtaTolov());   // haqiqiy pul yechilmaydi

Bir nechta interface

Class faqat bitta otadan meros oladi, lekin istalgancha interface ni amalga oshira oladi:

<?php

interface JsongaAylanadi
{
    public function toArray(): array;
}

interface Yetkaziladi
{
    public function ogirlik(): float;
}

class Kitob implements JsongaAylanadi, Yetkaziladi
{
    public function __construct(private string $nom, private float $kg) {}

    public function toArray(): array { return ['nom' => $this->nom]; }
    public function ogirlik(): float { return $this->kg; }
}

PHP ning o'zida ham tayyor interface lar bor: Countable (count($obj) ishlashi uchun), JsonSerializable (json_encode($obj) uchun), Stringable, IteratorAggregate (foreach uchun).

<?php

class Savat implements Countable
{
    private array $qatorlar = [];

    public function count(): int
    {
        return count($this->qatorlar);
    }
}

echo count(new Savat());   // 0 — oddiy count() ishlaydi

Abstract class — yarim tayyor class

Ba'zan classlar faqat kelishuvni emas, umumiy kodni ham bo'lishadi. Interface kod saqlay olmaydi. Abstract class — saqlay oladi:

<?php

abstract class Bildirishnoma
{
    public function __construct(protected string $qabulQiluvchi) {}

    // Har bir bola o'zi yozishi SHART
    abstract protected function yuborish(string $matn): bool;

    // Umumiy mantiq — hamma bolalar uchun bir xil
    public function jonat(string $matn): bool
    {
        if (trim($matn) === '') {
            throw new InvalidArgumentException("Bo'sh xabar");
        }
        $natija = $this->yuborish($matn);
        error_log(static::class . " → {$this->qabulQiluvchi}: " . ($natija ? 'OK' : 'XATO'));
        return $natija;
    }
}

class SmsBildirishnoma extends Bildirishnoma
{
    protected function yuborish(string $matn): bool
    {
        // SMS shlyuzi API...
        return true;
    }
}

class TelegramBildirishnoma extends Bildirishnoma
{
    protected function yuborish(string $matn): bool
    {
        // Telegram Bot API...
        return true;
    }
}

$b = new TelegramBildirishnoma('@foydalanuvchi');
$b->jonat("Buyurtmangiz yo'lda");
  • abstract class — undan to'g'ridan-to'g'ri ob'ekt yaratib bo'lmaydi (new Bildirishnoma() — xato)
  • abstract protected function yuborish() — tanasiz metod; bolalar uni yozishga majbur
  • jonat() — tayyor metod: tekshirish va loglash hamma uchun bir xil

Bu naqsh Template Method deyiladi: umumiy algoritm otada, o'zgaruvchan qadam bolalarda.


Interface yoki abstract class?

Interface Abstract class
Kod saqlaydimi Yo'q (faqat metod imzolari) Ha
Xususiyatlar Yo'q (faqat konstantalar) Ha
Nechta Istalgancha implements Faqat bitta extends
Ma'nosi "Nima qila oladi" "Nima ekanligi" + umumiy kod

Qoida:

  • Avval interface dan boshlang — u eng moslashuvchan
  • Bir nechta amalga oshirish bir xil kodni takrorlayotganini ko'rsangiz — umumiy qismni abstract class ga chiqaring
  • Ko'pincha ikkalasi birga ishlatiladi: abstract class AsosiyTolov implements TolovTizimi

Nima uchun shunday?

Nima uchun "aniq classga emas, interface ga bog'lan" degan qoida bunchalik muhim?

Tasavvur qiling: devordagi rozetka. U aniq bir dazmol yoki choynakni bilmaydi — u faqat "vilka standarti" ni biladi (interface). Shuning uchun istalgan qurilmani ulash mumkin va rozetkani o'zgartirish shart emas. Agar har bir qurilmaga alohida rozetka kerak bo'lganida — har yangi qurilmada devorni buzardingiz. Kodda ham shunday: interface — standart vilka, classlar — qurilmalar.


Xulosa

Eslab qoling:

  • interface — metodlar kelishuvi; implements bilan amalga oshiriladi
  • Class bir nechta interface ni amalga oshira oladi
  • Kodni aniq classga emas, interface ga bog'lang — almashtirish va test oson
  • abstract class — umumiy kod + bolalar majburiy yozadigan abstract metodlar
  • abstract class dan new qilib bo'lmaydi
  • Interface dan boshlang, takrorlanish paydo bo'lsa — abstract class

Keyingi dars

32-dars: static, trait, readonly va enum