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
- třídní OOP (na rozdíl od prototypové v plain JS)
- viditelnost -
private,protected,public abstractafinaltřídy a metody- magické metody
- rozhraní, traity (viz další část cvičení)
- výčtové typy
- type hinting
Definice třídy
klíčové slovo
classclass 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
- třídní atributy - sdílené pro všechny instance (modifikátor
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í
selfnebostatic- pouze zevnitř- upozornění: tyto výrazy nejsou ekvivalentní, více viz další část cvičení.
- k členům se přistupuje pomocí scope resolution operator
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::$accountsMetody
- 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řídyprotected- 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
readonlyvlastnosti možné změnit při klonování skrz funkciclone
Konstruktor
- inicializace objektu, nepovinná
magická metoda
__constructclass 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,protectednebopublic)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
parenta scope resolution operatorparent::$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 classz 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
intnebostring(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]