Jdi na navigaci předmětu

Základní techniky objektově orientovaného programování

Základní principy a pojmy

  • zapouzdření - těsný vztah mezi daty a operacemi
  • dědičnost - sdílení stejných vlastností
  • polymorfismus - různá implementace v potomcích stejného rodiče
  • třída - definuje formát dat a operace
  • objekt (instance) - realizace třídy, konkrétní hodnoty atributů
    • V některých jazycích je i třída objektem.

OOP v PHP

Definice třídy

  • klíčové slovo class

    class Account {
      protected $number;
      protected $amount;
      protected $transactions = [];
      public function addTransaction($transaction) { ... }
      public function getTransactions() { ... }
      ...
    }

Instanciace, přiřazení

  • instanciace pomocí operátoru new
  • přiřazení referencí
  • kopírování explicitně pomocí operátoru clone
$account = new Account; // pokud se konstruktoru nepředávají argumenty nemusí být ()
$card = new Card(12345);
$skim = clone $card;

Členy třídy

  • instance
    • instanční atributy - zvlášť pro každou instanci
    • instanční metody - pracují s konkrétní instancí
  • třída
    • třídní atributy - sdílené pro všechny instance (modifikátor static)
    • třídní metody - nemohou pracovat s instancí (modifikátor static)
    • konstanty

Odkazování na třídu a instanci

  • instance
    • názvem proměnné (proměnná = reference) - zvenku
    • speciální proměnná $this - zevnitř, reprezentuje volající instanci
    • k členům se přistupuje pomocí object operator ->
  • třída
    • k členům se přistupuje pomocí scope resolution operator :: (Paamayim Nekudotayim)
    • pomocí názvu třídy - zvenku i zevnitř
    • pomocí self nebo static - pouze zevnitř

Atributy

class Bank {
  public static array $accounts = [];
}

class Account {
  public string $number;
}

// instanční atribut - zvenku
echo $account->number;
// instanční atribut - zevnitř
echo $this->number;
// proměnná proměnná
$property = 'number';
echo $account->$property;
// vytvoření instance a přístup k atributu je možné (od PHP 8.4) zřetězit bez nutnosti uzávorkování
echo new Account()->number;

// třídní atribut - zvenku
Bank::$accounts
// třídní atribut - zevnitř
self::$accounts
// třídní atribut - zevnitř, late static binding
static::$accounts

Metody

  • platí totéž, co u atributů
class Card {
  ...

  public function getPin(): string {
    return substr($this->number, 0, 4); // zevnitř $this->...
  }

  public static function issue(): self {
    return new self; // vrátí vlastní instanci
  }
}

$pin = $card->getPin();   // zvenku $promenna->...
$newCard = Card::issue(); // zvenku Třída::...

Konstanty

  • přístup pomocí scope resolution operator ::, zvenku i zevnitř
  • uvedení datového typu není povinné, ale silně doporučené
    • v opačném případě může potomek do konstanty uložit hodnotu i jiného datového typu
    • typované konstanty podporovány od PHP 8.3
  • pro pevně daný seznam alternativ (např. typ karty) použijte výčtový typ, ne sadu konstant
class Card {
  public const int NUMBER_LENGTH = 16;
  public const string DEFAULT_CURRENCY = "CZK";

  public static function isValidNumber(string $number): bool {
    return strlen($number) === self::NUMBER_LENGTH;
  }
}

Viditelnost

  • private - přístup pouze zevnitř třídy
  • protected - přístup z třídy a jejích potomků
  • public (výchozí) - bez omezení

Měnitelnost

  • readonly - do vlastnosti lze zapsat jen v rámci konstruktoru
  • od PHP 8.5 je readonly vlastnosti možné změnit při klonování skrz funkci clone

Konstruktor

  • inicializace objektu, nepovinná
  • magická metoda __construct

    class Order {
      protected static int $sequence = 0;
      protected int $id;
      protected string $name;
      public function __construct(string $name) {
        $this->id = ++static::$sequence;
        $this->name = $name;
      }
    }
  • Od php 8.0 může být atribut deklarován v konstruktoru (pomocí slov private, protected nebo public)

    class Order {
      protected static int $sequence = 0;
      protected int $id;
      public function __construct(protected string $name) {
        $this->id = ++static::$sequence;
      }
    }

Destruktor

  • uvolnění objektu (deinicializace), používá se zřídka
  • magická metoda __destruct
  • volán garbage collectorem
  • bez argumentů

Dědičnost

  • vztah mezi třídami
  • specializace (rodič → potomek)
  • generalizace (potomek → rodič)

Příklad dědičnosti

class Card {
  protected $number;
}

class DebitCard extends Card {
  protected $limit;
}

class CreditCard extends Card {
  protected $dueDay;
}

Přístup k rodiči

  • explicitně: klíčové slovo parent a scope resolution operator

    parent::$number  // přístup ke statickému atributu
    parent::lock()   // přístup k metodě
  • pokud není atribut nebo metoda překryta v potomkovi, může přistupovat i přes potomka

    $child->number   // přístup k atributu
    $child->lock()   // přístup k metodě

Konstruktor

  • obvykle voláme rodičovský konstruktor na začátku konstruktoru potomka
  • pokud není v potomkovi implementovaný, volá se rodičovský automaticky

    function __construct() {
      parent::__construct(); // lze předat argumenty
      $this->dueDay = 20;
    }

Destruktor

  • obvykle voláme na konci, po provedení operací pro potomka

    function __destruct() {
      // uvolnění dat potomka apod.
      parent::__destruct();
    }

Výčtové typy

  • od PHP 8.1, klíčové slovo enum (referenční dokumentace)
  • obdoba enum class z C++
  • pevně daný seznam hodnot (case); každá hodnota je jediná instance (singleton), nelze vytvořit pomocí new
  • nahrazuje dřívější řešení sadou konstant (Card::TYPE_VISA, Card::TYPE_MASTERCARD, …​)
  • lze použít jako typ atributu, argumentu i návratové hodnoty
  • nemůže mít atributy (kromě value), konstruktor ani destruktor; nelze z něj dědit
enum CardType {
  case Visa;
  case MasterCard;
}

class Card {
  public function __construct(
    protected string $number,
    protected CardType $type,
  ) {}

  public function getType(): CardType {
    return $this->type;
  }

  public function getFee(): int {
    return match ($this->type) {
      CardType::Visa => 10,
      CardType::MasterCard => 20,
    };
  }
}

$card = new Card('1234567812345678', CardType::Visa);
var_dump($card->getType() === CardType::Visa); // bool(true)

Výčtové typy s hodnotou (backed enum)

  • každé hodnotě je přiřazena hodnota typu int nebo string (např. pro uložení do databáze či JSON)
  • přístup k hodnotě přes →value
  • převod z hodnoty: ::from() (vyhodí ValueError) nebo ::tryFrom() (vrátí null)
  • seznam všech hodnot: ::cases()
enum CardType: string {
  case Visa = 'visa';
  case MasterCard = 'mc';
}

echo CardType::Visa->value;        // visa
$type = CardType::from('mc');      // CardType::MasterCard
$type = CardType::tryFrom('amex'); // null
$type = CardType::from('amex');    // ValueError
CardType::cases();                 // [CardType::Visa, CardType::MasterCard]

Užitečné třídy přímo v PHP