Programowanie obiektowe – OOP (object-oriented programming) – pozwala na przedstawienie rzeczywistości i relacji w niej zachodzących za pomocą obiektów.
Po co nam programowanie obiektowe?
Programowanie obiektowe (ang. Object-Oriented Programming, OOP) wprowadza strukturę i organizację do kodu, co ułatwia jego tworzenie, rozwijanie i utrzymanie. Poniżej znajdziesz wyjaśnienie, po co stosować programowanie obiektowe, z przykładami jego głównych zalet:
1. Organizacja i modularność
- OOP pozwala dzielić kod na mniejsze, logiczne fragmenty (klasy), które odpowiadają rzeczywistym obiektom lub pojęciom.
- Dzięki temu kod jest bardziej przejrzysty i łatwiejszy w zarządzaniu.
Przykład: W grze komputerowej można mieć klasy Gracz, Przeciwnik, Broń. Każda z nich zawiera tylko dane i funkcje związane z tym obiektem.
2. Reużywalność kodu
- Możesz tworzyć klasy i wykorzystywać je wielokrotnie w różnych projektach lub częściach programu.
- Dziedziczenie umożliwia tworzenie nowych klas na podstawie istniejących, dzięki czemu kod jest bardziej elastyczny.
Przykład: Klasa Pojazd może mieć podklasy Samochód, Motocykl, które dziedziczą podstawowe cechy pojazdu (np. prędkość, masa), ale dodają swoje specyficzne funkcje.
3. Łatwiejsze rozwijanie aplikacji
- W OOP zmiany są łatwiejsze do wprowadzenia, ponieważ każda klasa działa jako oddzielny moduł.
- Jeśli potrzebujesz zmienić zachowanie jednego elementu, modyfikujesz tylko odpowiednią klasę, bez wpływu na inne części programu.
Przykład: Dodanie nowej broni w grze wymaga jedynie stworzenia nowej klasy na podstawie istniejącego schematu, np. Broń.
4. Ukrywanie szczegółów (enkapsulacja)
- OOP pozwala ukrywać szczegóły implementacji, dzięki czemu kod jest bardziej bezpieczny i trudniejszy do przypadkowego uszkodzenia.
- Dane w klasach są chronione, a dostęp do nich odbywa się przez ściśle określone metody.
Przykład: W klasie KontoBankowe saldo konta jest ukryte, a użytkownik może je zmieniać tylko za pomocą metod wpłać() i wypłać().
5. Naturalne odwzorowanie rzeczywistości
- OOP naśladuje sposób, w jaki myślimy o świecie, opierając się na obiektach, ich cechach i zachowaniach.
- Dzięki temu kod staje się bardziej intuicyjny i łatwiejszy do zrozumienia.
Przykład: Klasa Kot ma cechy takie jak kolor futra (atrybuty) i umiejętności jak miauczenie (metody).
6. Łatwość debugowania i testowania
- Dzięki temu, że klasy są niezależne, błędy w jednym obiekcie nie wpływają na inne.
- Testowanie poszczególnych modułów (klas) jest prostsze niż testowanie całości kodu.
7. Polimorfizm – elastyczność w działaniu
- Polimorfizm umożliwia różnym obiektom używanie tej samej metody, ale w różny sposób.
- Pozwala to na pisanie bardziej uniwersalnego i elastycznego kodu.
Przykład: Metoda atakuj() może działać inaczej w klasie Rycerz i Mag, mimo że jest wywoływana w ten sam sposób.
8. Ułatwienie pracy zespołowej
- W dużych projektach, podział kodu na klasy ułatwia współpracę, ponieważ każdy programista może pracować nad innymi obiektami, nie wchodząc sobie w drogę.
Podsumowanie
Programowanie obiektowe jest użyteczne, gdy:
- Tworzysz duże, złożone systemy.
- Chcesz pisać kod, który łatwo rozwijać, utrzymywać i testować.
- Potrzebujesz odzwierciedlić rzeczywiste pojęcia lub obiekty w swoim kodzie.
Jeśli jednak tworzysz mały, prosty skrypt, nie zawsze konieczne jest stosowanie OOP – wtedy wystarczą inne podejścia, jak programowanie proceduralne.
Klasa i obiekt
Kiedy mówimy o klasie musimy wyobrazić ją sobie jak ogólny zarys/opis jakiegoś obiektu, zbiór cech wspólnych dla np. człowieka. Gdybyśmy mieli klasę człowiek moglibyśmy określić ogólne cechy jakie definiują ludzi, czyli np. to że śpi, że ma głos, oczy, włosy, ręce, nogi itd.
Obiekt jest instancją danej klasy, czyli konkretnym człowiekiem np. Janem Kowalskim, który ma brązowe włosy, jest typem „skowronka” i ma niebieskie oczy. Te cechy nie są bezpośrednio związane z klasą, a z obiektem (instancją/wystąpieniem klasy).
Można powiedzieć, że Jan Kowalski jest zmienną typu klasy, czyli obiektem typu człowiek.
Przykład utworzenia klasy – Streamer na Twitchu
<?php
// Tworzymy klasę Streamer
class Streamer {
public $nick;
public $subskrybcje;
public $followers;
public $kategoria;
}
?>
Mając tak zdefiniowana klasę możemy otworzyć konkretny obiekt:
<?php
$streamer1 = new Streamer(); // Tworzymy nowy obiekt klasy Streamer
// Nadajemy konkretne cechy
$streamer1->nick = 'GamingLegend';
$streamer1->subskrybcje = 1520;
$streamer1->followers = 45000;
$streamer1->kategoria = 'League of Legends';
// Wyświetlamy rezultaty
echo 'Streamer: ' . $streamer1->nick . '<br>';
echo 'Subskrybcje: ' . $streamer1->subskrybcje . '<br>';
echo 'Followersi: ' . $streamer1->followers . '<br>';
echo 'Gra w: ' . $streamer1->kategoria . '<br>';
/*
Wyświetli się:
Streamer: GamingLegend
Subskrybcje: 1520
Followersi: 45000
Gra w: League of Legends
*/
?>
Wyświetlenie stworzonego obiektu
Możemy wyświetlić sobie stworzony obiekt:
<?php
echo '<pre>';
print_r($streamer1);
echo '</pre>';
/*
Wyświetli się:
Streamer Object
(
[nick] => GamingLegend
[subskrybcje] => 1520
[followers] => 45000
[kategoria] => League of Legends
)
*/
?>
Wartości domyślne w klasie
Klasie możemy nadać domyślne wartości, które zostaną nadane w momencie tworzenia obiektu:
<?php
class Streamer {
public $nick;
public $subskrybcje = 0; // Wartość domyślna
public $followers = 0; // Wartość domyślna
public $kategoria = 'Just Chatting'; // Wartość domyślna
}
$streamer1 = new Streamer(); // Tworzymy nowy obiekt
// Nadajemy tylko niektóre cechy
$streamer1->nick = 'NewbieStreamer';
$streamer1->followers = 150;
// Wyświetlamy rezultaty
echo 'Streamer: ' . $streamer1->nick . '<br>';
echo 'Subskrybcje: ' . $streamer1->subskrybcje . '<br>';
echo 'Followersi: ' . $streamer1->followers . '<br>';
echo 'Kategoria: ' . $streamer1->kategoria . '<br>';
/*
Wyświetli się:
Streamer: NewbieStreamer
Subskrybcje: 0
Followersi: 150
Kategoria: Just Chatting
*/
?>
Już potrafisz:
- stworzyć definicję obiektu w postaci klasy
- dodać publiczne pola do klasy i nadawać im wartości domyślne
- przypisywać im wartości
- odczytywać ich wartość
- korzystać z „->”, by dostać się do pola klasy
Metody
Klasa może definiować metody, czyli nic innego jak funkcje z podejścia proceduralnego. Kiedy tworzymy obiekt nadajemy mu dostęp do metod danej klasy.
Trochę praktyki – Discord Bot
<?php
class DiscordBot {
public $nazwa;
public $serwery = 0; // Liczba serwerów
public $uzytkownicy = 0; // Liczba użytkowników
public $wersja = '1.0'; // Wersja bota
// Teraz tworzymy metodę
public function pobierzStatus() {
return "Bot {$this->nazwa} jest aktywny na {$this->serwery} serwerach.";
}
}
$bot1 = new DiscordBot();
$bot1->nazwa = 'MusicBot';
$bot1->serwery = 350;
$bot2 = new DiscordBot();
$bot2->nazwa = 'ModerationBot';
$bot2->serwery = 1200;
$bot3 = new DiscordBot();
$bot3->nazwa = 'GamesBot';
echo '<pre>';
print_r($bot1);
echo '</pre>';
echo $bot1->pobierzStatus() . '<br>';
echo '<pre>';
print_r($bot2);
echo '</pre>';
echo $bot2->pobierzStatus() . '<br>';
echo '<pre>';
print_r($bot3);
echo '</pre>';
echo $bot3->pobierzStatus() . '<br>';
?>
Zostanie wyświetlone:
DiscordBot Object
(
[nazwa] => MusicBot
[serwery] => 350
[uzytkownicy] => 0
[wersja] => 1.0
)
Bot MusicBot jest aktywny na 350 serwerach.
DiscordBot Object
(
[nazwa] => ModerationBot
[serwery] => 1200
[uzytkownicy] => 0
[wersja] => 1.0
)
Bot ModerationBot jest aktywny na 1200 serwerach.
DiscordBot Object
(
[nazwa] => GamesBot
[serwery] => 0
[uzytkownicy] => 0
[wersja] => 1.0
)
Bot GamesBot jest aktywny na 0 serwerach.
Zmienna $this
Aby dostać się do konkretnego pola w obiekcie, korzystamy ze zmiennej $this.
W powyższym przykładzie nadawaliśmy wartości odnosząc się bezpośrednio do pola klasy. Nie jest to dobra metoda, ponieważ użytkownik może wpisać co chce w dane pole i niestety skrypt może się „wysypać”.
Aby tego uniknąć dodajemy dane za pomocą metody, którą możemy łatwo zabezpieczyć przed niechcianym kodem.
Bezpieczne metody – setter i getter
Powyższy kod powinien wyglądać następująco:
<?php
class DiscordBot {
public $nazwa;
public $serwery = 0;
public $uzytkownicy = 0;
public $wersja = '1.0';
// Metoda pobierająca status (getter)
public function pobierzStatus() {
return "Bot {$this->nazwa} jest aktywny na {$this->serwery} serwerach.";
}
// Metoda przypisująca liczbę serwerów (setter)
public function przypiszSerwery($ilosc) {
// Można dodać tutaj sprawdzanie czy wprowadzona wartość jest int
// i mieści się w określonym przedziale
$this->serwery = $ilosc;
}
}
$bot1 = new DiscordBot();
$bot1->nazwa = 'MusicBot';
$bot1->przypiszSerwery(350); // Bezpieczne przypisanie
$bot2 = new DiscordBot();
$bot2->nazwa = 'ModerationBot';
$bot2->przypiszSerwery(1200);
$bot3 = new DiscordBot();
$bot3->nazwa = 'GamesBot';
echo '<pre>';
print_r($bot1);
echo '</pre>';
echo $bot1->pobierzStatus() . '<br>';
echo '<pre>';
print_r($bot2);
echo '</pre>';
echo $bot2->pobierzStatus() . '<br>';
echo '<pre>';
print_r($bot3);
echo '</pre>';
echo $bot3->pobierzStatus() . '<br>';
?>
Walidacja danych w metodach
A teraz przykład z walidacją liczby serwerów:
<?php
class DiscordBot {
public $nazwa;
public $serwery = 'brak danych'; // Wartość domyślna
public $uzytkownicy = 0;
public $wersja = '1.0';
// Metoda pobierająca status
public function pobierzStatus() {
return "Bot {$this->nazwa} jest aktywny na {$this->serwery} serwerach.";
}
// Metoda przypisująca liczbę serwerów Z WALIDACJĄ
public function przypiszSerwery($ilosc) {
// Sprawdzanie czy wprowadzona wartość jest int i mieści się w przedziale
$ilosc = intval($ilosc);
if ($ilosc > 0 && $ilosc < 1000000) {
$this->serwery = $ilosc;
}
}
}
$bot1 = new DiscordBot();
$bot1->nazwa = 'MusicBot';
$bot1->przypiszSerwery('tekst'); // Nieprawidłowa wartość - zostanie odrzucona
$bot2 = new DiscordBot();
$bot2->nazwa = 'ModerationBot';
$bot2->przypiszSerwery(1200); // Prawidłowa wartość
$bot3 = new DiscordBot();
$bot3->nazwa = 'GamesBot';
echo '<pre>';
print_r($bot1);
echo '</pre>';
echo $bot1->pobierzStatus() . '<br>';
echo '<pre>';
print_r($bot2);
echo '</pre>';
echo $bot2->pobierzStatus() . '<br>';
echo '<pre>';
print_r($bot3);
echo '</pre>';
echo $bot3->pobierzStatus() . '<br>';
?>
Wyświetli się:
DiscordBot Object
(
[nazwa] => MusicBot
[serwery] => brak danych
[uzytkownicy] => 0
[wersja] => 1.0
)
Bot MusicBot jest aktywny na brak danych serwerach.
DiscordBot Object
(
[nazwa] => ModerationBot
[serwery] => 1200
[uzytkownicy] => 0
[wersja] => 1.0
)
Bot ModerationBot jest aktywny na 1200 serwerach.
DiscordBot Object
(
[nazwa] => GamesBot
[serwery] => brak danych
[uzytkownicy] => 0
[wersja] => 1.0
)
Bot GamesBot jest aktywny na brak danych serwerach.
Już potrafisz:
- stworzyć metodę klasy
- zwrócić wartość i przekazać argumenty
- korzystać ze zmiennej $this
- zabezpieczyć dane
Modyfikatory dostępu
Do tej pory wykonywaliśmy przykłady w oparciu o publiczną dostępność danego elementu, czyli public. Taka metoda lub właściwość jest publiczna, więc widoczna wszędzie wewnątrz obiektu i poza nim. Dostęp do pola jest nieograniczony i można je odczytywać i zmieniać dowolnie.
Właściwość lub metoda może być prywatna, czyli private, wówczas może zostać użyta tylko wewnątrz obiektu.
Właściwość lub metoda protected może być użyta tylko wewnątrz obiektu, ale może być również dziedziczona.
Zaleca się w programowaniu i w życiu, nie udostępniać zbyt wiele informacji na zewnątrz, więc tego się trzymajmy – korzystamy z modyfikatorów private.
Przykład public – Serwer gry online
<?php
class SerwerGry {
public $czyOnline = true;
public function wlacz() {
$this->czyOnline = true;
}
public function wylacz() {
$this->czyOnline = false;
}
public function sprawdzStatus() {
return $this->czyOnline;
}
}
$serwer = new SerwerGry();
$serwer->wylacz();
$serwer->czyOnline = true; // Zmieniamy wartość parametru bezpośrednio!
echo $serwer->sprawdzStatus() ? 'Serwer online' : 'Serwer offline';
// Wyświetli: Serwer online
?>
W powyższym przykładzie należy zwrócić uwagę na to, że pomimo iż ostatnią operacją na obiekcie było wyłączenie serwera, wprowadzenie bezpośredniej zmiany pola $serwer->czyOnline na true, sprawiło że otrzymujemy informację, że serwer jest online.
Możemy tego uniknąć blokując dostęp do pól i metod klasy, czyli zmieniając ich modyfikatory z publicznych na prywatne.
Ustawiamy wartość prywatną:
<?php
class SerwerGry {
private $czyOnline = true; // Modyfikator prywatny, brak dostępu z zewnątrz
public function wlacz() {
$this->czyOnline = true;
}
public function wylacz() {
$this->czyOnline = false;
}
public function sprawdzStatus() {
return $this->czyOnline;
}
}
$serwer = new SerwerGry();
$serwer->wylacz();
$serwer->czyOnline = true; // Chcemy zmienić wartość parametru
// Tutaj interpreter zwróci błąd, gdyż nie ma dostępu do zmiennej
echo $serwer->sprawdzStatus() ? 'Serwer online' : 'Serwer offline';
?>
Przykład powyższy generuje błąd, ponieważ nie ma dostępu do prywatnej wartości pola klasy.
Ustawimy teraz metody na prywatne i publiczne:
<?php
class StreamConfig {
private $bitrate = 6000;
private function obliczJakosc() {
return $this->bitrate / 1000 . 'Mbps';
}
public function czyMoznaStreamowac($upload) {
return $this->bitrate < $upload ? 'TAK' : 'NIE';
}
}
$config = new StreamConfig();
echo '<pre>';
print_r($config);
echo '</pre>';
echo $config->czyMoznaStreamowac(8000); // Wyświetli: TAK
echo $config->czyMoznaStreamowac(3000); // Wyświetli: NIE
// echo $config->obliczJakosc(); // Tutaj interpreter zwróci błąd,
// gdyż nie ma dostępu do metody
?>
O modyfikatorze protected, trochę później – przy temacie dziedziczenie.
Już potrafisz:
- ograniczać dostęp do pól klasy i metod
- pozwalać klasie bazowej oraz klasom dziedziczącym na modyfikację pola lub metody
Dziedziczenie
Tak jak w życiu – jako potomkowie dziedziczymy po naszych przodkach pewne cechy (tak naprawdę to 50% od matki i 50% od ojca), tak samo klasy dziedziczą pola i metody, ale mogą zostać uzupełnione o inne elementy, których przodek nie posiada.
Jeżeli klasa przodka posiada pola i metody, które nie są prywatne, to klasa potomna również będzie je posiadać.
Klasa może dziedziczyć tylko po jednej klasie (rozszerzać tylko jedną klasę bazową )
Aby klasa dziedziczyła po innej należy użyć słówka extends i podać nazwę klasy przodka:
Przykład – Postać w grze RPG
<?php
class Postac {
private $nick = 'Gracz123';
protected $poziom = 1;
protected $hp = 100;
public $imie;
public function czyMartwy() {
return $this->hp <= 0;
}
public function otrzymajObrazenia($obrazenia) {
return $this->zadajObrazenia($obrazenia);
}
protected function zadajObrazenia($ilosc) {
$this->hp -= $ilosc;
if ($this->hp < 0) {
$this->hp = 0;
}
return $this->hp;
}
}
// Następuje dziedziczenie
class Wojownik extends Postac {
private $pancerz = 50;
public function mocnyAtak() {
return $this->atakujZSila(30);
}
private function atakujZSila($ilosc) {
if ($this->poziom > 0) { // Mamy dostęp do pola poziom, gdyż jest protected
return $this->otrzymajObrazenia($ilosc); // Metoda otrzymajObrazenia jest public
}
return $this->pancerz += $ilosc;
}
}
$bohater = new Wojownik();
$bohater->imie = 'Conan';
$bohater->otrzymajObrazenia(25);
$bohater->mocnyAtak();
if ($bohater->czyMartwy()) {
echo $bohater->imie . ' nie żyje!';
} else {
echo $bohater->imie . ' walczy dalej!';
}
echo '<pre>';
print_r($bohater);
echo '</pre>';
// $hp = $bohater->hp; // Ta linijka wygeneruje błąd,
// gdyż z zewnątrz nie mamy dostępu do pola protected
// $bohater->zadajObrazenia(10); // Ta linijka podobnie, jak wyżej
?>
Final, a nadpisywanie
Wiemy już doskonale, że zadeklarowanie w kodzie zmiennej o nazwie, która już istnieje, spowoduje nadpisanie jej wartości. Tak samo dzieje się z metodami. Jeżeli klasa dziedzicząca będzie posiadała metodę o nazwie istniejącej w klasie bazowej, to jej funkcjonalność zostanie nadpisana:
<?php
class ProfilBazowy {
public function wyswietlNazwe() {
echo "Nazywam się: Użytkownik";
}
}
class ProfilPremium extends ProfilBazowy {
public function wyswietlNazwe() {
echo "Nazywam się: Premium User";
}
}
$profil = new ProfilPremium();
$profil->wyswietlNazwe();
// Wyświetli: Nazywam się: Premium User
?>
W powyższym kodzie widzimy, że metoda wyswietlNazwe() została nadpisana i zamiast wyświetlić „Nazywam się: Użytkownik” otrzymaliśmy „Nazywam się: Premium User”.
Słowo kluczowe final
Jeżeli chcemy zabronić klasie potomnej nadpisywania metod w klasie bazowej, używamy słowa final przed deklaracją metody:
<?php
class ProfilBazowy {
final public function wyswietlNazwe() {
echo "Nazywam się: Użytkownik";
}
}
class ProfilPremium extends ProfilBazowy {
/*
public function wyswietlNazwe() {
// Ta metoda wygeneruje błąd:
// Fatal error: Cannot override final method...
}
*/
}
$profil = new ProfilPremium();
$profil->wyswietlNazwe();
?>
Modyfikatora final możemy użyć przed nazwą klasy, wówczas ta klasa nie będzie mogła być dziedziczona.
Już potrafisz:
- stworzyć klasę rozszerzającą inną klasę
- korzystać z pól i metod klasy bazowej
- nadpisywać metody klasy bazowej
- blokować nadpisywanie przy pomocy słowa final
Metody magiczne
Nazwa tych metod wzięła się stąd, że wywoływane są automatycznie w momencie tworzenia obiektu i nie wymagają dodatkowych akcji. Metody te występują tylko w kontekście klas, a ich nazwy są poprzedzone 2 znakami „podłogi” (podkreślnika) __ .
Konstruktor – __construct
Dzięki konstruktorowi możemy przesłać określone wartości do obiektu w momencie jego tworzenia.
Teoretycznie PHP umożliwia stworzenie tylko 1go konstruktora, ale po to mamy kreatywność żeby móc sobie z tym poradzić Zapraszam do zbadania tej ewentualności na stronie: https://kursphp.com/programowanie-obiektowe-php/konstruktor/
<?php
// PHP ma TYLKO JEDEN konstruktor, ale możemy symulować wiele:
class Uzytkownik {
private string $imie;
private string $email;
// Jedyny prawdziwy konstruktor
private function __construct(string $imie, string $email) {
$this->imie = $imie;
$this->email = $email;
}
// Statyczne metody fabrykujące (factory methods) - symulacja wielu konstruktorów
public static function zLoginu(string $login): self {
// Pobierz dane z bazy po loginie
return new self($login, "$login@example.com");
}
public static function zEmail(string $email): self {
// Pobierz dane z bazy po email
$login = explode('@', $email)[0];
return new self($login, $email);
}
}
// Użycie - wygląda jak różne konstruktory:
$user1 = Uzytkownik::zLoginu('jan123');
$user2 = Uzytkownik::zEmail('anna@example.com');
Destruktor – __destruct
Destruktor wykonuje się w momencie niszczenia obiektu.
Przykład użycia konstruktora i destruktora:
<?php
class GraczOnline {
private $nick;
private $poziom;
public function __construct(string $nick, int $poziom) {
$this->nick = $nick;
$this->poziom = $poziom;
echo "Gracz {$this->nick} dołączył do gry!<br>";
}
public function getNick(): string {
return $this->nick;
}
public function getPoziom(): int {
return $this->poziom;
}
public function __destruct() {
echo "Gracz {$this->nick} opuścił grę!<br>";
}
}
$gracz = new GraczOnline('ProGamer99', 45);
echo 'Nick: ' . $gracz->getNick() . ', Poziom: ' . $gracz->getPoziom() . '<br>';
unset($gracz); // Usuwanie obiektu
// Wynik: Gracz ProGamer99 opuścił grę!
?>
Inne popularne metody magiczne
// __call() - wywoływana gdy metoda nie istnieje
public function __call($name, $arguments) {
echo "Próba wywołania nieistniejącej metody: $name";
}
// __isset() - sprawdzanie czy właściwość istnieje
public function __isset($name) {
return isset($this->data[$name]);
}
// __unset() - usuwanie właściwości
public function __unset($name) {
unset($this->data[$name]);
}
// __clone() - klonowanie obiektu
public function __clone() {
echo "Obiekt został sklonowany";
}
Więcej informacji na temat metod magicznych znajdziesz w dokumentacji: http://php.net/manual/en/language.oop5.magic.php
Stałe
Stała to właściwość klasy tylko do odczytu. Jak wiemy z języka PHP, stałe deklarujemy drukowanymi literami, są dostępne public i nie można ich modyfikować w kodzie.
Aby zadeklarować stałą, używaliśmy dotychczas konstrukcji define(), możemy to też zrobić używając modyfikatora const. Pamiętać należy, że jedna i druga konstrukcja nie są sobie równe. Za pomocą const definiujemy stałe w klasach i tylko w nich.
Aby się do niej odwołać wewnątrz klasy, musimy użyć modyfikatora self::
Aby wywołać właściwość poza klasą używamy modyfikatora nazwaKlasy::
<?php
class KonfiguracjaGry {
const MAX_GRACZY = 100;
const MIN_POZIOM = 1;
const MAX_POZIOM = 100;
private $liczbaGraczy;
public function __construct($liczbaGraczy) {
$this->liczbaGraczy = $liczbaGraczy;
}
public function mozeDolczyc() {
return $this->liczbaGraczy < self::MAX_GRACZY;
}
}
// KonfiguracjaGry::MAX_GRACZY = 200; // Nie można modyfikować
$gra = new KonfiguracjaGry(85);
echo 'Czy można dołączyć: ' . ($gra->mozeDolczyc() ? 'TAK' : 'NIE') . '<br>';
echo 'Maksymalna liczba graczy: ' . KonfiguracjaGry::MAX_GRACZY . '<br>';
echo 'Maksymalny poziom: ' . KonfiguracjaGry::MAX_POZIOM . '<br>';
?>
Metody i właściwości statyczne
Metody i właściwości statyczne to takie, które nie wymagają utworzenia obiektu, aby móc się do nich odwołać. Nadawane są dla całej klasy. Tworzymy je przy pomocy modyfikatora static.
Z racji tego, że właściwość ta nadawana jest dla klasy (nie dla obiektu), aby się do niej odwołać wewnątrz klasy, musimy użyć modyfikatora self::, nie korzystamy w tym przypadku z $this. Aby wywołać właściwość poza klasą używamy modyfikatora nazwaKlasy::
<?php
class LicznikGraczy {
public static $iloscGraczy = 0;
public function __construct() {
self::$iloscGraczy++;
}
}
echo 'Graczy online: ' . LicznikGraczy::$iloscGraczy . '<br>'; // Wartość początkowa: 0
$gracz1 = new LicznikGraczy();
$gracz2 = new LicznikGraczy();
$gracz3 = new LicznikGraczy();
echo 'Graczy online: ' . LicznikGraczy::$iloscGraczy . '<br>'; // Wartość po dodaniu: 3
?>
Klasy i metody abstrakcyjne
Klasa abstrakcyjna definiuje pewien model i zachowanie obiektu.
Taka klasa nie posiada definicji tych metod, jedynie ich deklaracje, że gdzieś dalej w kodzie muszą się pojawić. Czyli do czasu rozszerzenia ich o funkcjonalności w klasach dziedziczących, te metody nie istnieją (są abstrakcją).
Metody abstrakcyjne definiujemy w klasie bazowej klauzulą abstract. Wszystkie tego typu zadeklarowane metody będą musiały pojawić się w klasach dziedziczących. Jeśli nie pojawią się, to PHP wygeneruje nam błąd.
Nie można utworzyć obiektu klasy abstrakcyjnej. Klasa ta jest jedynie „bazą” dla klas potomnych.
Pamiętamy, że klasę dziedziczymy i możemy zrobić to tylko raz.
<?php
abstract class PostacGry {
abstract public function getNazwa();
abstract public function atak();
}
class Mag extends PostacGry {
public function getNazwa() {
return 'Mag';
}
public function atak() {
return 'Rzuca zaklęcie!';
}
}
class Wojownik extends PostacGry {
public function getNazwa() {
return 'Wojownik';
}
public function atak() {
return 'Uderza mieczem!';
}
}
$mag = new Mag();
echo $mag->getNazwa() . ': ' . $mag->atak() . '<br>';
$wojownik = new Wojownik();
echo $wojownik->getNazwa() . ': ' . $wojownik->atak() . '<br>';
?>
Interfejsy
Interfejs jest zbiorem metod jakie wymagamy by posiadała klasa, która go implementuje.
Możemy implementować wiele interfejsów dla jednej klasy.
Wszystkie metody określane w interfejsie są abstrakcyjne.
<?php
interface PostacInterface {
public function getNazwa(): string;
public function getPoziom(): int;
}
interface WalkaInterface {
public function atak(): string;
public function obrona(): string;
}
class Bohater implements PostacInterface, WalkaInterface {
private $nazwa;
private $poziom;
public function __construct(string $nazwa, int $poziom) {
$this->nazwa = $nazwa;
$this->poziom = $poziom;
}
public function getNazwa(): string {
return $this->nazwa;
}
public function getPoziom(): int {
return $this->poziom;
}
public function atak(): string {
return "{$this->nazwa} atakuje!";
}
public function obrona(): string {
return "{$this->nazwa} broni się!";
}
}
$bohater = new Bohater('Aragorn', 50);
echo $bohater->getNazwa() . '<br>';
echo 'Poziom: ' . $bohater->getPoziom() . '<br>';
echo $bohater->atak() . '<br>';
echo $bohater->obrona() . '<br>';
?>
Różnice między interfejsem, a klasą abstrakcyjną
- klasa może implementować wiele interfejsów, ale rozszerzać tylko jednego rodzica
- metody w interfejsie są publiczne, w klasie publiczne lub chronione
- interfejs może zawierać tylko deklaracje metod, a klasa też metody zdefiniowane
- klasy abstrakcyjne mogą zawierać atrybuty(pola), interfejsy nie
- w interfejsach wszystkie metody są abstrakcyjne, natomiast w klasie abstrakcyjnej można stworzyć metody posiadające ciało, jak i abstrakcyjne
- klasa abstrakcyjna zazwyczaj jest ściśle związana z klasami dziedziczącymi w sensie logicznym, czyli np. tworzymy klasę abstrakcyjną Planeta po której dziedziczą konkretne klasy planet (np. Ziemia, Mars). Interfejs natomiast nie musi być już tak mocno związany z daną klasą, on określa jej cechy, np możesz stworzyć interfejs Zniszczalny, który mówi że dany obiekt może zostać zniszczony. Taki interfejs możesz nadać zarówno klasą Planeta, Gwiazda, Budynek itp.
- interfejs może dziedziczyć jedynie po innych interfejsach, a klasa abstrakcyjna może dziedziczyć po klasach abstrakcyjnych, interfejsach a nawet zwykłych klasach
- interfejs nie może mieć konstruktora, w klasie abstrakcyjnej możemy dostarczyć implementację konstruktora domyślnego
<?php
// PHP 8.0+ - interfejsy mogą mieć metody z implementacją (rzadko używane)
interface Logger {
public function log(string $message): void;
// PHP 8.0+ - metoda z implementacją w interfejsie
public function logError(string $message): void {
$this->log("ERROR: " . $message);
}
}
Przestrzenie nazw
Przestrzenie nazw pozwalają na zadeklarowanie wielu klas lub funkcji o tej samej nazwie.
W programowaniu jak w życiu, tak samo jak nie mogą w jednym folderze istnieć 2 pliki o tej samej nazwie i rozszerzeniu, tak samo w kodzie PHP dwie tak samo nazwane funkcje lub klasy generują błąd.
Przestrzenie nazw PHP umożliwiają grupowanie powiązanych klas, interfejsów, funkcji i stałych.
Zalecane jest tworzenie jednej przestrzeni nazw w jednym pliku.
<?php
// Przestrzenie nazw pozwalają na organizację kodu i unikanie konfliktów nazw
// Przykład problemu BEZ przestrzeni nazw:
// Jeśli masz dwie biblioteki, które mają klasę o nazwie User, będzie konflikt:
// biblioteka1.php
class User { }
// biblioteka2.php
class User { } // BŁĄD! Klasa User już istnieje
// --------------------------------------------------
// Rozwiązanie Z przestrzeniami nazw:
// biblioteka1.php
namespace Biblioteka1;
class User { }
// biblioteka2.php
namespace Biblioteka2;
class User { }
// Teraz możesz używać obu:
use Biblioteka1\User as User1;
use Biblioteka2\User as User2;
$user1 = new User1();
$user2 = new User2();
Przestrzenie nazw działają jak foldery w systemie plików
Zalecane jest: jedna przestrzeń nazw = jeden plik
Konwencja: odpowiadają strukturze folderów (PSR-4)
PHP 8 – nowości
1. Typed Properties (właściwości typowane)
PHP 8 pozwala na określenie typu dla właściwości klasy:
<?php
class GraczOnline {
public string $nick;
public int $poziom;
public float $hp;
public bool $premium;
public function __construct(string $nick, int $poziom) {
$this->nick = $nick;
$this->poziom = $poziom;
$this->hp = 100.0;
$this->premium = false;
}
}
$gracz = new GraczOnline('ProGamer', 45);
// $gracz->poziom = 'tekst'; // Błąd: Typ musi być int
?>
2. Constructor Property Promotion
PHP 8 pozwala na zwięzłe deklarowanie właściwości bezpośrednio w konstruktorze:
<?php
// PHP 7 - długie
class GraczStary {
private string $nick;
private int $poziom;
public function __construct(string $nick, int $poziom) {
$this->nick = $nick;
$this->poziom = $poziom;
}
}
// PHP 8 - zwięzłe
class GraczNowy {
public function __construct(
private string $nick,
private int $poziom,
private float $hp = 100.0
) {}
}
$gracz = new GraczNowy('ProGamer', 45);
?>
3. Named Arguments
PHP 8 pozwala na przekazywanie argumentów po nazwie:
<?php
class PostacGry {
public function __construct(
private string $imie,
private int $poziom = 1,
private int $hp = 100,
private int $mana = 50
) {}
}
// PHP 7 - musimy podać wszystkie
$postac1 = new PostacGry('Gandalf', 1, 100, 200);
// PHP 8 - możemy pominąć środkowe
$postac2 = new PostacGry(
imie: 'Gandalf',
mana: 200,
poziom: 50
);
?>
4. Match Expression
PHP 8 wprowadza match jako lepszą alternatywę dla switch:
<?php
class SystemRang {
public function getRanga(int $punkty): string {
// PHP 7 - switch
switch (true) {
case $punkty < 1000:
return 'Brązowy';
case $punkty < 2000:
return 'Srebrny';
case $punkty < 3000:
return 'Złoty';
default:
return 'Diamentowy';
}
// PHP 8 - match (zwięzłe i bezpieczniejsze)
return match (true) {
$punkty < 1000 => 'Brązowy',
$punkty < 2000 => 'Srebrny',
$punkty < 3000 => 'Złoty',
default => 'Diamentowy'
};
}
}
?>
5. Nullsafe Operator
PHP 8 wprowadza operator ?-> który zapobiega błędom przy null:
<?php
class Ekwipunek {
public ?Bron $bron = null;
}
class Bron {
public string $nazwa;
}
$gracz = new stdClass();
$gracz->ekwipunek = new Ekwipunek();
// PHP 7 - długie sprawdzanie
$nazwaBroni = null;
if ($gracz->ekwipunek !== null && $gracz->ekwipunek->bron !== null) {
$nazwaBroni = $gracz->ekwipunek->bron->nazwa;
}
// PHP 8 - nullsafe operator
$nazwaBroni = $gracz->ekwipunek?->bron?->nazwa;
?>
6. Union Types
PHP 8 pozwala na określenie wielu typów dla jednego parametru:
<?php
class Gracz {
// Może być int lub string
public function setId(int|string $id): void {
// ...
}
// Może zwrócić PostacGry lub null
public function znajdzPostac(string $nazwa): PostacGry|null {
// ...
}
}
?>
Przykłady
Przykład 1: System Streamingu na Twitchu – przykład z obszernym komentarzem w kodzie
<?php
/*
* ==========================================
* KLASA STREAMER - System Streamingu Twitch
* ==========================================
*
* Ta klasa reprezentuje streamera na platformie Twitch.
* Każdy streamer ma:
* - pseudonim (nick)
* - liczbę subskrybcji
* - liczbę followerów
* - status online/offline
*/
class Streamer {
/*
* KONSTRUKTOR - metoda automatycznie wywoływana przy tworzeniu obiektu
*
* PHP 8 - Constructor Property Promotion:
* Parametry konstruktora automatycznie stają się właściwościami klasy.
* To skrót, który oszczędza pisanie kodu.
*
* Tradycyjny sposób (PHP 7):
* -------------------------
* private string $nick;
* public function __construct(string $nick) {
* $this->nick = $nick;
* }
*
* Nowy sposób (PHP 8):
* --------------------
* public function __construct(private string $nick) {}
*
* Oba robią to samo, ale PHP 8 jest krócej!
*/
public function __construct(
private string $nick, // Pseudonim streamera (WYMAGANE - brak wartości domyślnej)
private int $subskrybcje = 0, // Liczba subskrybcji (domyślnie 0)
private int $followers = 0, // Liczba followerów (domyślnie 0)
private bool $czyOnline = false // Status online (domyślnie false = offline)
) {
/*
* Ciało konstruktora jest PUSTE {}, ponieważ PHP 8 automatycznie:
* 1. Tworzy prywatne właściwości: $nick, $subskrybcje, $followers, $czyOnline
* 2. Przypisuje im wartości przekazane przy tworzeniu obiektu
*
* Gdybyśmy chcieli coś dodatkowego zrobić przy tworzeniu obiektu,
* moglibyśmy napisać tu dodatkowy kod, np:
* echo "Utworzono konto dla {$this->nick}!";
*/
}
/*
* METODA: rozpocznijStream()
* --------------------------
* Uruchamia stream - zmienia status streamera na "online"
*
* public - metoda jest dostępna z zewnątrz klasy
* void - metoda nie zwraca żadnej wartości (tylko wykonuje akcję)
*/
public function rozpocznijStream(): void {
// Zmieniamy właściwość $czyOnline na true
// $this-> oznacza "tego obiektu" (tego konkretnego streamera)
$this->czyOnline = true;
// Wyświetlamy komunikat
// {$this->nick} wstawia wartość właściwości $nick do tekstu
echo "{$this->nick} rozpoczął stream!<br>";
}
/*
* METODA: zakonczStream()
* -----------------------
* Kończy stream - zmienia status streamera na "offline"
*/
public function zakonczStream(): void {
// Ustawiamy status na offline
$this->czyOnline = false;
// Wyświetlamy komunikat
echo "{$this->nick} zakończył stream!<br>";
}
/*
* METODA: dodajSuba()
* -------------------
* Dodaje jedną subskrybcję do konta streamera
*
* Dlaczego metoda zamiast bezpośredniego dostępu?
* ------------------------------------------------
* Właściwość $subskrybcje jest PRIVATE, więc nie można jej zmienić z zewnątrz:
*
* ŹLE (nie zadziała):
* $streamer->subskrybcje = 999; // BŁĄD! Właściwość jest private
*
* DOBRZE (zadziała):
* $streamer->dodajSuba(); // OK! Używamy publicznej metody
*
* Zalety tego podejścia:
* - Kontrola: możemy dodać walidację (np. sprawdzić czy sub jest ważny)
* - Bezpieczeństwo: nikt nie ustawi liczby subów na -100
* - Logika biznesowa: możemy dodać dodatkowe akcje (np. powiadomienia)
*/
public function dodajSuba(): void {
// Operator ++ zwiększa wartość o 1
// Równoznaczne z: $this->subskrybcje = $this->subskrybcje + 1;
$this->subskrybcje++;
// Wyświetlamy komunikat z aktualną liczbą subów
echo "Nowy sub! Razem: {$this->subskrybcje}<br>";
/*
* MIEJSCE NA ROZBUDOWĘ:
* Tutaj moglibyśmy dodać dodatkową logikę, np:
*
* if ($this->subskrybcje % 100 == 0) {
* echo "MILESTONE! {$this->subskrybcje} subów!<br>";
* }
*
* Albo:
* if ($this->subskrybcje == 1000) {
* echo "Gratulacje! 1000 subskrypcji!<br>";
* }
*/
}
/*
* METODA: wyswietlStatus()
* ------------------------
* Wyświetla pełny status streamera:
* - pseudonim
* - czy jest online/offline
* - liczba subskrybcji
* - liczba followerów
*/
public function wyswietlStatus(): void {
/*
* OPERATOR TRÓJARGUMENTOWY (ternary operator):
* -------------------------------------------
* Składnia: warunek ? wartość_jeśli_prawda : wartość_jeśli_fałsz
*
* Długa wersja (identyczne działanie):
* if ($this->czyOnline == true) {
* $status = 'LIVE';
* } else {
* $status = 'OFFLINE';
* }
*
* Krótka wersja:
* $status = $this->czyOnline ? 'LIVE' : 'OFFLINE';
*/
$status = $this->czyOnline ? 'LIVE' : 'OFFLINE';
// Wyświetlamy nagłówek z pseudonimem i statusem
echo "--- {$this->nick} [{$status}] ---<br>";
// Wyświetlamy szczegóły
echo "Subskrybcje: {$this->subskrybcje}<br>";
echo "Followersi: {$this->followers}<br>";
}
}
/*
* ==========================================
* UŻYCIE KLASY - Tworzenie obiektu i praca z nim
* ==========================================
*/
/*
* TWORZENIE OBIEKTU (instancji klasy):
* ------------------------------------
* Słowo kluczowe 'new' tworzy nowy obiekt na podstawie klasy Streamer
*
* new Streamer(...) wywołuje konstruktor i przekazuje do niego argumenty:
* - 'GamingLegend' → przypisane do $nick
* - 150 → przypisane do $subskrybcje
* - 5000 → przypisane do $followers
* - $czyOnline nie podane, więc dostaje wartość domyślną: false
*
* Obiekt zapisujemy w zmiennej $streamer
*/
$streamer = new Streamer('GamingLegend', 150, 5000);
/*
* Co się stało w pamięci?
* -----------------------
* Utworzono obiekt $streamer z następującymi wartościami:
*
* $streamer:
* ├─ $nick = 'GamingLegend'
* ├─ $subskrybcje = 150
* ├─ $followers = 5000
* └─ $czyOnline = false
*/
// WYWOŁANIE METODY: wyswietlStatus()
// ----------------------------------
// Operator -> służy do wywoływania metod i dostępu do właściwości obiektu
// Wywołujemy metodę wyswietlStatus() na obiekcie $streamer
$streamer->wyswietlStatus();
/*
* Wyświetli:
* --- GamingLegend [OFFLINE] ---
* Subskrybcje: 150
* Followersi: 5000
*/
echo "<br>"; // Odstęp dla czytelności
// WYWOŁANIE METODY: rozpocznijStream()
// ------------------------------------
// Zmieniamy status streamera na "online"
$streamer->rozpocznijStream();
/*
* Dzieje się:
* 1. $czyOnline zmienia się na true
* 2. Wyświetla się: GamingLegend rozpoczął stream!
*/
// WYWOŁANIE METODY: dodajSuba() - pierwsze wywołanie
// --------------------------------------------------
// Dodajemy pierwszą subskrybcję
$streamer->dodajSuba();
/*
* Dzieje się:
* 1. $subskrybcje zwiększa się z 150 do 151
* 2. Wyświetla się: Nowy sub! Razem: 151
*/
// WYWOŁANIE METODY: dodajSuba() - drugie wywołanie
// ------------------------------------------------
// Dodajemy kolejną subskrybcję
$streamer->dodajSuba();
/*
* Dzieje się:
* 1. $subskrybcje zwiększa się z 151 do 152
* 2. Wyświetla się: Nowy sub! Razem: 152
*/
// WYWOŁANIE METODY: zakonczStream()
// ---------------------------------
// Kończymy stream
$streamer->zakonczStream();
/*
* Dzieje się:
* 1. $czyOnline zmienia się na false
* 2. Wyświetla się: GamingLegend zakończył stream!
*/
echo "<br>"; // Odstęp dla czytelności
// WYWOŁANIE METODY: wyswietlStatus() - ponownie
// ---------------------------------------------
// Wyświetlamy status po zakończeniu streamu
$streamer->wyswietlStatus();
/*
* Wyświetli:
* --- GamingLegend [OFFLINE] ---
* Subskrybcje: 152 <-- Zauważ: zwiększyło się z 150 do 152
* Followersi: 5000
*
* Status zmienił się z LIVE na OFFLINE
*/
/*
* ==========================================
* PEŁNY OUTPUT PROGRAMU:
* ==========================================
*
* --- GamingLegend [OFFLINE] ---
* Subskrybcje: 150
* Followersi: 5000
*
* GamingLegend rozpoczął stream!
* Nowy sub! Razem: 151
* Nowy sub! Razem: 152
* GamingLegend zakończył stream!
*
* --- GamingLegend [OFFLINE] ---
* Subskrybcje: 152
* Followersi: 5000
*/
/*
* ==========================================
* DODATKOWE PRZYKŁADY - Tworzenie wielu streamerów
* ==========================================
*/
echo "<hr><h3>Dodatkowe przykłady:</h3>";
// Przykład 1: Nowy streamer z minimalnymi danymi
// ----------------------------------------------
// Podajemy tylko nick (reszta dostanie wartości domyślne)
$nowyStreamer = new Streamer('RookiePlayer');
/*
* Wartości:
* - $nick = 'RookiePlayer'
* - $subskrybcje = 0 (domyślna)
* - $followers = 0 (domyślna)
* - $czyOnline = false (domyślna)
*/
$nowyStreamer->wyswietlStatus();
/*
* Wyświetli:
* --- RookiePlayer [OFFLINE] ---
* Subskrybcje: 0
* Followersi: 0
*/
echo "<br>";
// Przykład 2: Wielki streamer
// ---------------------------
$megaStreamer = new Streamer('xQcOW', 85000, 1500000);
$megaStreamer->rozpocznijStream();
$megaStreamer->wyswietlStatus();
/*
* Wyświetli:
* xQcOW rozpoczął stream!
* --- xQcOW [LIVE] ---
* Subskrybcje: 85000
* Followersi: 1500000
*/
echo "<br>";
// Przykład 3: Porównanie dwóch streamerów
// ----------------------------------------
echo "<h4>Porównanie streamerów:</h4>";
$streamer1 = new Streamer('Ninja', 75000, 180000);
$streamer2 = new Streamer('Shroud', 50000, 100000);
// Obaj zaczynają stream
$streamer1->rozpocznijStream();
$streamer2->rozpocznijStream();
echo "<br>";
// Wyświetlamy ich statusy
echo "Status Ninja:<br>";
$streamer1->wyswietlStatus();
echo "<br>";
echo "Status Shroud:<br>";
$streamer2->wyswietlStatus();
/*
* WAŻNA OBSERWACJA:
* -----------------
* Każdy obiekt ($streamer1, $streamer2) ma WŁASNE wartości właściwości.
* Zmiana wartości w jednym obiekcie NIE WPŁYWA na drugi obiekt.
*
* To tak jakby mieć dwa oddzielne konta na Twitchu - każde jest niezależne.
*/
/*
* ==========================================
* KLUCZOWE POJĘCIA - PODSUMOWANIE:
* ==========================================
*
* 1. KLASA (class):
* - Szablon/przepis na tworzenie obiektów
* - Definiuje jakie właściwości i metody będą miały obiekty
*
* 2. OBIEKT (object/instancja):
* - Konkretny "egzemplarz" utworzony na podstawie klasy
* - Tworzy się słowem kluczowym 'new'
*
* 3. WŁAŚCIWOŚCI (properties):
* - Zmienne należące do obiektu
* - W naszym przykładzie: $nick, $subskrybcje, $followers, $czyOnline
*
* 4. METODY (methods):
* - Funkcje należące do klasy
* - Operują na właściwościach obiektu
* - W naszym przykładzie: rozpocznijStream(), dodajSuba(), wyswietlStatus()
*
* 5. $this:
* - Odnosi się do "bieżącego obiektu"
* - Używamy go w metodach aby dostać się do właściwości obiektu
*
* 6. MODYFIKATORY DOSTĘPU:
* - private: dostęp tylko wewnątrz klasy
* - public: dostęp z każdego miejsca
*
* 7. KONSTRUKTOR (__construct):
* - Specjalna metoda wywoływana automatycznie przy tworzeniu obiektu
* - Służy do inicjalizacji (ustawienia początkowych wartości)
*
* 8. OPERATOR -> :
* - Służy do dostępu do właściwości i metod obiektu
* - Składnia: $obiekt->metoda() lub $obiekt->właściwość
*/
?>
Przykład z wizualizacją stanu obiektów
<?php
// Wizualizacja jak zmieniają się dane w obiekcie
echo "<h3>Śledzenie zmian w obiekcie krok po kroku:</h3>";
// Krok 1: Tworzenie obiektu
echo "<strong>KROK 1: Tworzenie obiektu</strong><br>";
$streamer = new Streamer('TestStreamer', 10, 100);
echo "Utworzono streamera. Stan obiektu:<br>";
$streamer->wyswietlStatus();
/*
* Stan obiektu:
* $nick = 'TestStreamer'
* $subskrybcje = 10
* $followers = 100
* $czyOnline = false
*/
echo "<br><hr>";
// Krok 2: Rozpoczęcie streamu
echo "<strong>KROK 2: Rozpoczęcie streamu</strong><br>";
$streamer->rozpocznijStream();
echo "Stan obiektu po rozpoczęciu:<br>";
$streamer->wyswietlStatus();
/*
* Stan obiektu:
* $nick = 'TestStreamer'
* $subskrybcje = 10
* $followers = 100
* $czyOnline = true <-- ZMIANA!
*/
echo "<br><hr>";
// Krok 3: Dodanie 3 subskrybcji
echo "<strong>KROK 3: Dodanie 3 subskrybcji</strong><br>";
$streamer->dodajSuba();
$streamer->dodajSuba();
$streamer->dodajSuba();
echo "Stan obiektu po dodaniu subów:<br>";
$streamer->wyswietlStatus();
/*
* Stan obiektu:
* $nick = 'TestStreamer'
* $subskrybcje = 13 <-- ZMIANA! (było 10, teraz 13)
* $followers = 100
* $czyOnline = true
*/
echo "<br><hr>";
// Krok 4: Zakończenie streamu
echo "<strong>KROK 4: Zakończenie streamu</strong><br>";
$streamer->zakonczStream();
echo "Stan obiektu po zakończeniu:<br>";
$streamer->wyswietlStatus();
/*
* Stan obiektu:
* $nick = 'TestStreamer'
* $subskrybcje = 13
* $followers = 100
* $czyOnline = false <-- ZMIANA!
*/
echo "<br><hr>";
// Podsumowanie
echo "<strong>PODSUMOWANIE ZMIAN:</strong><br>";
echo "Początek: 10 subskrybcji, OFFLINE<br>";
echo "Koniec: 13 subskrybcji, OFFLINE<br>";
echo "Różnica: +3 subskrybcje<br>";
?>
Przykład 2: OOP obsługujące fabrykę samochodów
<?php
// Klasa bazowa dla fabryki Chevrolet
class ChevroletFactory {
// Stała reprezentująca maksymalną pojemność produkcji
const MAX_CAPACITY = 500;
// Właściwości klasy
protected $brand = "Chevrolet";
protected $producedCars = 0;
// Konstruktor inicjalizuje fabrykę
public function __construct() {
echo "Fabryka marki {$this->brand} została otwarta.<br>";
}
// Metoda do produkcji samochodów
public function produceCar($quantity) {
if ($this->producedCars + $quantity > self::MAX_CAPACITY) {
echo "Nie można wyprodukować $quantity samochodów. Przekroczono maksymalną pojemność produkcyjną.<br>";
} else {
$this->producedCars += $quantity;
echo "Wyprodukowano $quantity samochodów. Łączna produkcja: {$this->producedCars}.<br>";
}
}
// Metoda do wyświetlenia statusu produkcji
public function factoryStatus() {
echo "Fabryka marki {$this->brand} wyprodukowała {$this->producedCars} samochodów.<br>";
}
// Magiczna metoda __get. Obsługuje odczyt nieistniejących właściwości.
public function __get($name) {
echo "Próba dostępu do właściwości '{$name}', która nie istnieje.<br>";
return null;
}
// Magiczna metoda __set. Obsługuje próbę ustawienia niedozwolonych właściwości.
public function __set($name, $value) {
echo "Próba ustawienia właściwości '{$name}' na wartość '{$value}'. Operacja niedozwolona!<br>";
}
// Magiczna metoda __toString, która pozwoli na łatwą reprezentację obiektu jako tekst
public function __toString() {
return "Fabryka marki {$this->brand}: wyprodukowano {$this->producedCars} samochodów. Maksymalna pojemność: " . self::MAX_CAPACITY . ".<br>";
}
// Destruktor wyświetla komunikat o zamknięciu fabryki i wyprodukowanej liczbie samochodów.
// Destruktor - wywoływany automatycznie gdy:
// 1. Kończy się skrypt
// 2. Obiekt jest usuwany przez unset()
// 3. Obiekt jest nadpisywany przez nowy
public function __destruct() {
echo "Fabryka marki {$this->brand} została zamknięta. Łączna produkcja: {$this->producedCars} samochodów.<br>";
}
}
// Klasa pochodna dla modelu Camaro
class Camaro extends ChevroletFactory {
public $modelName = "Camaro";
private $engineType;
// Konstruktor klasy pochodnej
public function __construct($engineType) {
parent::__construct(); // Wywołanie konstruktora klasy bazowej
$this->engineType = $engineType;
echo "Rozpoczęto produkcję modelu {$this->modelName} z silnikiem {$this->engineType}.<br>";
}
}
// Klasa pochodna dla modelu Corvette
class Corvette extends ChevroletFactory {
public $modelName = "Corvette";
private $color;
// Konstruktor klasy pochodnej
public function __construct($color) {
parent::__construct(); // Wywołanie konstruktora klasy bazowej
$this->color = $color;
echo "Rozpoczęto produkcję modelu {$this->modelName} w kolorze {$this->color}.<br>";
}
}
// Przykład użycia
echo "==== Fabryka Chevrolet ====<br>";
// Tworzenie fabryki dla Camaro
$camaroFactory = new Camaro("V8");
$camaroFactory->produceCar(100); // Produkcja 100 samochodów
$camaroFactory->produceCar(450); // Przekroczenie pojemności
echo $camaroFactory; // Wywołanie __toString
echo $camaroFactory->nonExistentProperty; // Magiczne __get
$camaroFactory->nonExistentProperty = "Test"; // Magiczne __set
$camaroFactory->factoryStatus(); // Status fabryki
echo "<br>==== Fabryka Corvette ====<br>";
// Tworzenie fabryki dla Corvette
$corvetteFactory = new Corvette("Czerwony");
$corvetteFactory->produceCar(200); // Produkcja 200 samochodów
echo $corvetteFactory; // Wywołanie __toString
$corvetteFactory->factoryStatus(); // Status fabryki
// Destruktory wywołają się automatycznie po zakończeniu skryptu
?>
Klasa ChevroletFactory jest klasą bazową, która reprezentuje fabrykę samochodów marki Chevrolet. W tej klasie definiujemy:
-
Stałą
MAX_CAPACITY:
StałaMAX_CAPACITYokreśla maksymalną liczbę samochodów, które mogą być wyprodukowane przez fabrykę. Wartość ta jest stała i niezmienna, wynosi 500 w naszym przypadku. - Właściwości
$brandi$producedCars:$brandto nazwa marki fabryki, w tym przypadku „Chevrolet”.$producedCarsto liczba wyprodukowanych samochodów przez fabrykę, początkowo ustawiona na 0.
- Konstruktor: Konstruktor jest wywoływany, gdy tworzony jest nowy obiekt klasy
ChevroletFactory. W momencie tworzenia fabryki, wyświetlany jest komunikat o jej otwarciu. -
Metoda
produceCar($quantity): Ta metoda służy do produkcji określonej liczby samochodów. Jeśli liczba samochodów przekroczyłabyMAX_CAPACITY, metoda wyświetla komunikat o błędzie. W przeciwnym razie dodaje liczbę wyprodukowanych samochodów do właściwości$producedCars. -
Metoda
__toString(): Jest to metoda magiczna, która pozwala na „przekształcenie” obiektu na ciąg tekstowy. Gdy próbujemy wydrukować obiekt, np.echo $obiekt, PHP wywołuje__toString()i wyświetla zwrócony tekst. W naszym przypadku metoda ta zwraca szczegóły dotyczące fabryki, czyli liczbę wyprodukowanych samochodów i maksymalną pojemność. -
Destruktor: Destruktor jest wywoływany, gdy obiekt jest usuwany z pamięci, co zwykle następuje, gdy skrypt kończy działanie. W naszym przypadku, destruktor wyświetla komunikat o zamknięciu fabryki i liczbie wyprodukowanych samochodów.
-
Magic Methods
__geti__set:__get($name): Została dodana po to, aby obsługiwać przypadki, gdy próbujemy uzyskać dostęp do właściwości, która nie istnieje. W takim przypadku wyświetlamy komunikat informujący o próbie dostępu do nieistniejącej właściwości.__set($name, $value): Została dodana, aby obsługiwać próbę ustawienia wartości właściwości, która nie istnieje, lub która nie jest dozwolona do ustawiania.
Przykładowo, jeśli spróbujemy przypisać wartość do nieistniejącej właściwości, metoda
__setzostanie wywołana i wyświetli komunikat.
W naszym przykładzie mamy dwie klasy pochodne:
-
Camaro: Reprezentuje model samochodu Camaro. Klasa ta dziedziczy po klasieChevroletFactoryi dodaje specyficzną właściwość$engineType(typ silnika).Konstruktor klasy
Camarowywołuje konstruktor klasyChevroletFactoryza pomocąparent::__construct(), a następnie ustawia właściwość$engineTypei wyświetla komunikat o rozpoczęciu produkcji. -
Corvette: Reprezentuje model samochodu Corvette. Podobnie jak w przypadkuCamaro, dziedziczy po klasieChevroletFactory, ale dodaje właściwość$color(kolor samochodu).Konstruktor klasy
Corvetterównież wywołuje konstruktor klasyChevroletFactory, ustawia kolor i wyświetla komunikat o rozpoczęciu produkcji.
W sekcji przykładu użycia tworzymy obiekty klasy Camaro i Corvette i wywołujemy na nich różne metody:
-
Tworzymy fabrykę Camaro:
- Tworzymy obiekt
Camaro, który przyjmuje typ silnika jako argument w konstruktorze. - Wywołujemy metodę
produceCar(), aby wyprodukować samochody, a potem wyświetlamy status fabryki przy pomocyechoi metody__toString().
- Tworzymy obiekt
-
Tworzymy fabrykę Corvette:
- Tworzymy obiekt
Corvette, który przyjmuje kolor jako argument w konstruktorze. - Wywołujemy metodę
produceCar()i ponownie wyświetlamy status fabryki przy pomocyechoi metody__toString().
- Tworzymy obiekt
Podsumowanie:
- Klasa
ChevroletFactoryreprezentuje fabrykę samochodów i zawiera metody do produkcji, statusu fabryki oraz metody magiczne do zarządzania właściwościami obiektów. - Klasy pochodne (
CamaroiCorvette) reprezentują konkretne modele samochodów i dziedziczą właściwości i metody klasy bazowej, dodając własne specyficzne właściwości (np. typ silnika, kolor). - Metoda
__toString()umożliwia łatwe reprezentowanie obiektu w formie tekstowej.
Każdy obiekt fabryki może produkować samochody, wyświetlać stan fabryki oraz korzystać z funkcji związanych z magią PHP (__get, __set).
Przykład 3: Postaci w grze RPG
Poniżej przedstawiam przykład wykorzystania OOP do tworzenia postaci w grze i symulowania ich walki.
<?php
// Klasa główna Postac. To podstawowa klasa, z której dziedziczą inne postacie. Ma podstawowe cechy: imię, poziom i zdrowie.
class Postac {
// Własności
protected $imie;
protected $poziom;
protected $zdrowie;
// Konstruktor
public function __construct($imie, $poziom = 1) {
$this->imie = $imie;
$this->poziom = $poziom;
$this->zdrowie = 100;
}
// Metoda, która zwiększa poziom
public function awans() {
$this->poziom++;
$this->zdrowie += 20;
echo "{$this->imie} awansował na poziom {$this->poziom}! Zdrowie wynosi teraz {$this->zdrowie}.\n";
}
// Metoda magiczna __toString
public function __toString() {
return "Postać: $this->imie, Poziom: $this->poziom, Zdrowie: $this->zdrowie.";
}
}
// Klasa Wojownik, dziedziczy po klasie Postac
class Wojownik extends Postac {
private $sila;
// Konstruktor
public function __construct($imie, $poziom = 1) {
parent::__construct($imie, $poziom);
$this->sila = 10 + $poziom * 2;
}
// Atak
public function atak() {
echo "{$this->imie} wykonuje potężny atak mieczem, zadając {$this->sila} obrażeń!\n";
}
// Nadpisanie metody awans
public function awans() {
parent::awans();
$this->sila += 5;
echo "{$this->imie} zwiększył siłę do {$this->sila}.\n";
}
}
// Klasa Mag, dziedziczy po klasie Postac
class Mag extends Postac {
private $mana;
// Konstruktor
public function __construct($imie, $poziom = 1) {
parent::__construct($imie, $poziom);
$this->mana = 50 + $poziom * 10;
}
// Rzucenie zaklęcia
public function rzucZaklecie() {
if ($this->mana >= 10) {
$this->mana -= 10;
echo "{$this->imie} rzuca zaklęcie, zadając 30 obrażeń! Pozostała mana: {$this->mana}.\n";
} else {
echo "{$this->imie} nie ma wystarczającej ilości many!\n";
}
}
// Nadpisanie metody awans
public function awans() {
parent::awans();
$this->mana += 20;
echo "{$this->imie} zwiększył manę do {$this->mana}.\n";
}
}
// Tworzenie postaci
$wojownik = new Wojownik("Thor");
$mag = new Mag("Gandalf");
// Wypisanie informacji o postaciach
echo $wojownik . "\n"; // Wyświetla: Postać: Thor, Poziom: 1, Zdrowie: 100.
echo $mag . "\n"; // Wyświetla: Postać: Gandalf, Poziom: 1, Zdrowie: 100.
// Akcje postaci
$wojownik->atak(); // Thor wykonuje potężny atak mieczem, zadając 12 obrażeń!
$mag->rzucZaklecie(); // Gandalf rzuca zaklęcie, zadając 30 obrażeń! Pozostała mana: 50.
// Awansowanie postaci
$wojownik->awans(); // Thor awansował na poziom 2! Zdrowie wynosi teraz 120. Zwiększył siłę do 17.
$mag->awans(); // Gandalf awansował na poziom 2! Zdrowie wynosi teraz 120. Zwiększył manę do 80.
echo $wojownik . "\n"; // Wyświetla: Postać: Thor, Poziom: 2, Zdrowie: 120.
echo $mag . "\n"; // Wyświetla: Postać: Gandalf, Poziom: 2, Zdrowie: 120.
Wyjaśnienie kodu:
Dziedziczenie: Klasy Wojownik i Mag dziedziczą po Postac i dodają swoje unikalne właściwości (sila, mana) oraz metody (atak, rzucZaklecie).
Konstruktor: Każda klasa ma swój konstruktor, który inicjalizuje własności. Wojownik i Mag wywołują konstruktor z klasy nadrzędnej za pomocą parent::__construct().
Metoda magiczna __toString: Pozwala reprezentować obiekt jako ciąg znaków, np. przy wywołaniu echo $obiekt.
Polimorfizm: Metoda awans() jest nadpisywana w klasach Wojownik i Mag, aby uwzględnić specyficzne zmiany.
Zadanie: rozszerz kod o kolejną postać np. łucznik
Zadanie 1
Jesteś praktykantem w firmie zajmującej się tworzeniem witryn i aplikacji internetowych. Otrzymałeś zadanie polegające na stworzeniu aplikacji w języku PHP.
W aplikacji ma być utworzona klasa trójkąt, która zawiera dwa publiczne pola, takie jak: wysokość i podstawa oraz konstruktor, który przypisze im losowo wygenerowane wartości.
Ponadto w klasie powinna być zadeklarowana metoda obliczająca pole trójkąta.
W aplikacji należy utworzyć dwa obiekty klasy trójkąt.
Wynikiem działania aplikacji ma być wyświetlona wartość wysokości, podstawy i pola powierzchni obu trójkątów oraz informacja, który z trójkątów ma większą powierzchnię.
Zadanie 2
Jesteś praktykantem w firmie zajmującej się tworzeniem witryn i aplikacji internetowych. Otrzymałeś zadanie polegające na stworzeniu aplikacji w języku PHP.
W aplikacji ma być utworzona klasa odcinek zawierająca cztery publiczne pola, określające współrzędne początku i końca odcinka we współrzędnych x, y. W klasie odcinek należy utworzyć konstruktor, który współrzędnym przypisze podane przez użytkownika (za pomocą formularza) wartości.
Ponadto w klasie powinna być zadeklarowana metoda obliczająca długość odcinka.
W aplikacji należy utworzyć dwa obiekty klasy odcinek.
Wynikiem działania aplikacji ma być wyświetlona wartość długości obu odcinków oraz informacja, który z nich jest dłuższy.
Zadanie 3: System Zarządzania Gildią Graczy
Opis zadania:
Wyobraź sobie, że zarządzasz gildią w grze MMORPG. Twoim celem jest stworzenie systemu, który pozwala dodawać graczy do gildii, zarządzać ich rangami, a także organizować misje. Każdy gracz należy do gildii i może współpracować z innymi w ramach drużyny.
Wymagania funkcjonalne:
Klasa Player:
Atrybuty:
- name – imię gracza.
- rank – ranga w gildii (np. „Rekrut”, „Członek”, „Oficer”).
- level – poziom doświadczenia gracza.
- role – rola w drużynie (np. „Tank”, „DPS”, „Healer”).
Metody:
- promote() – zwiększa rangę gracza (np. z „Rekruta” na „Członka”).
- demote() – obniża rangę gracza (np. z „Oficera” na „Członka”).
Klasa Guild:
Atrybuty:
- name – nazwa gildii.
- members – lista graczy w gildii (obiekty klasy Player).
Metody:
- add_member(player) – dodaje nowego gracza do gildii.
- remove_member(player) – usuwa gracza z gildii.
- list_members() – wyświetla listę wszystkich graczy z ich szczegółami.
- assign_team(players) – przypisuje wybranych graczy do drużyny (np. na misję).
Klasa Mission:
Atrybuty:
- name – nazwa misji.
- difficulty – poziom trudności (np. „Łatwy”, „Średni”, „Trudny”).
- required_roles – lista wymaganych ról (np. [„Tank”, „DPS”, „Healer”]).
Metody:
- is_team_eligible(team) – sprawdza, czy drużyna spełnia wymagania roli.
Rozszerzenia:
- Dodaj system zdobywania punktów doświadczenia i automatycznego awansu na wyższy poziom.
- Wprowadź ograniczenie maksymalnej liczby graczy w gildii.
- Stwórz mechanizm losowania nagród za ukończone misje.
Zadanie 4: Gra Wirtualnych Walk Robotów
Opis zadania:
Stwórz symulację walk robotów, w której każdy robot ma różne parametry, broń i strategie walki.
Wymagania:
Klasa Robot:
Atrybuty:
- name – nazwa robota.
- health – punkty życia.
- attack_power – siła ataku.
- defense – poziom obrony.
- weapon – broń robota (np. „Laser”, „Rakiety”).
Metody:
- attack(opponent) – zmniejsza punkty życia przeciwnika na podstawie siły ataku i broni.
- defend(damage) – redukuje otrzymane obrażenia na podstawie obrony.
- status() – wyświetla aktualne zdrowie robota.
Symulator walki:
Wybierz dwóch robotów i pozwól im walczyć w rundach, aż jeden z nich przegra (zdrowie spadnie do 0). W każdej rundzie roboty na przemian atakują, a wynik walki jest wyświetlany po każdej turze.
Dodatkowe funkcjonalności:
Moduł ulepszeń:
- Dodaj klasę Upgrade, która pozwala ulepszać roboty (np. zwiększać zdrowie, siłę ataku, obronę).
- Przykład: „Dodatkowe Pancerze” zwiększają defense, a „Turbo Silnik” zwiększa szybkość ataku.
Różne typy broni:
- Każda broń może mieć dodatkowe efekty, np. laser ignoruje obronę przeciwnika, a rakiety zadają obrażenia obszarowe.
- Dodaj klasę Weapon z atrybutami damage i special_effect.
Tryb turniejowy:
- Dodaj system turniejów, w którym roboty walczą w eliminacjach, aż pozostanie jeden zwycięzca.
Losowe zdarzenia:
- Wprowadź zdarzenia w trakcie walki, np. „Awaria systemu” lub „Podwójne obrażenia”.
Zadanie 5: System Kart Kolekcjonerskich
Opis zadania:
Stwórz system kart kolekcjonerskich, które mogą być zbierane, wymieniane i używane w pojedynkach.
Wymagania:
Klasa Card:
Atrybuty:
- name – nazwa karty.
- attack – siła ataku karty.
- defense – poziom obrony karty.
- rarity – rzadkość karty (np. „Zwykła”, „Rzadka”, „Legendarna”).
Metody:
- compare(card) – porównuje siłę ataku dwóch kart i zwraca zwycięzcę.
Klasa Player:
Atrybuty:
- name – imię gracza.
- deck – talia kart (lista obiektów klasy Card).
Metody:
- add_card(card) – dodaje kartę do talii gracza.
- remove_card(card) – usuwa kartę z talii.
- battle(opponent) – wybiera jedną kartę z talii i walczy z wybraną kartą przeciwnika.
Rozgrywka:
Gracze losują karty z talii i walczą w turach. Wygrywa gracz, którego karty przetrwają najdłużej.
Dodatkowe funkcjonalności:
Tworzenie własnych kart:
- Dodaj system, w którym gracz może stworzyć własną kartę, przypisując jej atak, obronę i rzadkość w ramach określonych limitów.
Efekty specjalne kart:
- Wprowadź karty z efektami, np. „Ognisty Smok” podwaja atak podczas pierwszej tury, a „Magiczna Bariera” neguje pierwszy atak przeciwnika.
Mechanizm wymiany:
- Gracze mogą wymieniać karty między sobą.
- Dodaj opcję targowania się lub ustalania cen wirtualnych.
Rozgrywka wieloosobowa:
- Wprowadź tryb, w którym gracze mogą walczyć z innymi graczami w turniejach online (symulowanych w grze).
Zadanie 6: Gra Słowna z Przeciwnikiem AI
Opis zadania:
Stwórz grę, w której gracz i przeciwnik komputerowy rywalizują, tworząc jak najdłuższe słowa z losowego zestawu liter.
Wymagania:
Klasa WordGame:
Atrybuty:
- letters – lista liter, z których można tworzyć słowa.
- player_score – wynik gracza.
- ai_score – wynik przeciwnika.
Metody:
- generate_letters() – losuje zestaw liter.
- validate_word(word) – sprawdza, czy słowo jest poprawne (np. istnieje w słowniku).
- ai_move() – komputer tworzy losowe poprawne słowo z dostępnych liter.
Rozgrywka:
Gracz wprowadza słowo, a następnie przeciwnik generuje swoje słowo. Punktacja zależy od długości słowa. Gra kończy się po określonej liczbie tur.
Dodatkowe funkcjonalności:
Tryb rywalizacji:
- Dodaj możliwość gry na czas – gracz i AI mają ograniczoną liczbę sekund na wymyślenie słowa.
Poziomy trudności AI:
- Łatwy: AI wybiera krótkie słowa.
- Średni: AI stara się tworzyć średniej długości słowa.
- Trudny: AI generuje najdłuższe możliwe słowa.
System bonusów:
- Dodaj bonusy za użycie trudnych liter (np. „Q” lub „X”) lub za stworzenie słowa dłuższego niż 7 liter.
Ulepszenie liter:
- Gracz może wymienić kilka liter na nowe za stratę punktów.
Zadanie 7: Fantasy Liga e-Sportowa
Opis zadania:
Zaprojektuj system zarządzania drużyną wirtualnych graczy e-sportowych, gdzie każda drużyna rywalizuje w turnieju.
Wymagania:
Klasa Player:
Atrybuty:
- nickname – pseudonim gracza.
- role – rola w grze (np. „Support”, „Carry”).
- skill_level – poziom umiejętności gracza.
Metody:
- train() – zwiększa poziom umiejętności gracza.
- play_match() – zmniejsza zmęczenie gracza (opcjonalnie wprowadź mechanikę kondycji).
Klasa Team:
Atrybuty:
- name – nazwa drużyny.
- players – lista obiektów klasy Player.
Metody:
- add_player(player) – dodaje gracza do drużyny.
- remove_player(player) – usuwa gracza z drużyny.
- team_power() – oblicza łączny poziom umiejętności drużyny.
Turniej:
Stwórz symulację, w której drużyny rywalizują w turnieju, a wyniki zależą od poziomu umiejętności ich graczy. Wyświetlaj wyniki każdej rundy i ogłoś zwycięzcę.
Dodatkowe funkcjonalności:
Personalizacja graczy:
- Dodaj system awatarów, imion drużyn i personalizowanych logo.
Zdarzenia losowe:
- Przykład: „Kontuzja gracza” – gracz musi zostać wymieniony przed kolejnym meczem.
- „Eksplozja formy” – tymczasowy wzrost umiejętności jednego gracza.
Ekonomia drużyny:
- Wprowadź budżet, który pozwala na zatrudnianie lepszych graczy lub inwestowanie w trening.
Symulacja pełnych rozgrywek:
- Stwórz harmonogram meczów między drużynami, a na koniec wyświetlaj tabelę wyników.
Rozgrywka interaktywna:
- Gracz może aktywnie zarządzać strategią podczas meczu (np. wybór taktyki, zmiany w drużynie).
Zadania 8: System Handlu w Grze MMORPG
Opis zadania:
Stwórz symulację handlu w grze, gdzie gracze mogą sprzedawać i kupować przedmioty od siebie.
Wymagania:
Klasa Item:
Atrybuty:
- name – nazwa przedmiotu.
- type – rodzaj przedmiotu (np. „Broń”, „Zbroja”, „Eliksir”).
- price – cena przedmiotu.
Metody:
- discount(percent) – obniża cenę przedmiotu o podany procent.
Klasa Player:
Atrybuty:
- name – imię gracza.
- inventory – lista posiadanych przedmiotów.
- gold – ilość złota gracza.
Metody:
- buy(item) – kupuje przedmiot, o ile gracz ma wystarczająco złota.
- sell(item) – sprzedaje przedmiot i zwiększa ilość złota.
Klasa Marketplace:
Atrybuty:
- items – lista dostępnych przedmiotów.
Metody:
- list_items() – wyświetla dostępne przedmioty na rynku.
- trade(buyer, seller, item) – przeprowadza transakcję między graczami.
Rozgrywka:
Gracze mogą kupować i sprzedawać przedmioty na rynku. Ceny przedmiotów mogą się zmieniać w zależności od podaży i popytu (opcjonalne).
Dodatkowe funkcjonalności:
Dynamiczna ekonomia:
- Ceny przedmiotów zmieniają się w zależności od podaży i popytu na rynku.
Dom aukcyjny:
- Gracze mogą wystawiać przedmioty na aukcje, a inni licytować je w określonym czasie.
System reputacji:
- Gracze mogą zdobywać lub tracić reputację na podstawie uczciwości w handlu.
Rzemiosło:
- Wprowadź możliwość tworzenia nowych przedmiotów z surowców, które gracze mogą kupować, sprzedawać lub zdobywać podczas misji.
Mechanizm kradzieży:
- Dodaj opcję prób kradzieży przedmiotów od innych graczy, ale z ryzykiem utraty reputacji.