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:
<?php
//tworzymy klasę człowiek
class Czlowiek {
public $kolorOczu;
public $kolorWlosow;
public $wiek;
}
?>
Mając tak zdefiniowana klasę możemy otworzyć konkretny obiekt:
<?php
$czlowiek_1 = new Czlowiek(); //tworzymy nowy obiekt klasy Czlowiek
//nadajemy konkretne cechy
$czlowiek_1 -> kolorOczu = 'brązowe';
$czlowiek_1 -> kolorWlosow = 'brązowe';
$czlowiek_1 -> wiek = 17;
//wyświetlamy rezultaty
echo 'Osoba 1 ma '.$czlowiek_1 -> kolorOczu . ' oczy, ' . $czlowiek_1 -> kolorWlosow . ', włosy i ma lat ' . $czlowiek_1 -> wiek;
// Osoba 1 ma brązowe oczy, brązowe, włosy i ma lat 17
?>
Możemy wyświetlić sobie stworzony obiekt:
<?php
echo '<pre>';
print_r($czlowiek_1);
echo '</pre>';
/*wyświetli się:
Czlowiek Object
(
[kolorOczu] => brązowe
[kolorWlosow] => brązowe
[wiek] => 17 )
*/
?>
Klasie możemy nadać domyślne wartości, które zostaną nadane w momencie tworzenia obiektu:
<?php
class Czlowiek {
public $kolorOczu;
public $kolorWlosow;
public $wiek=17; //nadajemy wartość domyślną
}
$czlowiek_1 = new Czlowiek(); //tworzymy nowy obiekt klasy Czlowiek
//nadajemy konkretne cechy
$czlowiek_1 -> kolorOczu = 'brązowe';
$czlowiek_1 -> kolorWlosow = 'brązowe';
//wyświetlamy rezultaty
echo 'Osoba 1 ma '.$czlowiek_1 -> kolorOczu . ' oczy, ' . $czlowiek_1 -> kolorWlosow . ', włosy i ma lat ' . $czlowiek_1 -> wiek;
// Osoba 1 ma brązowe oczy, brązowe, włosy i ma lat 17
?>
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:
<?php
class Czlowiek {
public $kolorOczu;
public $kolorWlosow;
public $wiek=17; //nadajemy wartość domyślną
//teraz tworzymy metodę
public function pobierzWiek() {
return $this->wiek;
}
}
$czlowiek_1 = new Czlowiek();
$czlowiek_1 -> wiek=16;
$czlowiek_2 = new Czlowiek();
$czlowiek_2 -> wiek=18;
$czlowiek_3 = new Czlowiek();
echo '<pre>';
print_r($czlowiek_1);
echo '</pre>';
echo 'Pobrany wiek to: '.$czlowiek_1 -> pobierzWiek().'<br>';
echo '<pre>';
print_r($czlowiek_2);
echo '</pre>';
echo 'Pobrany wiek to: '.$czlowiek_2 -> pobierzWiek().'<br>';
echo '<pre>';
print_r($czlowiek_3);
echo '</pre>';
echo 'Pobrany wiek to: '.$czlowiek_3 -> pobierzWiek().'<br>';
?>
Zostanie wyświetlone:
Czlowiek Object
(
[kolorOczu] =>
[kolorWlosow] =>
[wiek] => 16
)
Pobrany wiek to: 16
Czlowiek Object
(
[kolorOczu] =>
[kolorWlosow] =>
[wiek] => 18
)
Pobrany wiek to: 18
Czlowiek Object
(
[kolorOczu] =>
[kolorWlosow] =>
[wiek] => 17
)
Pobrany wiek to: 17
Aby dostać się do konkretnego pola w obiekcie, korzystamy ze zmiennej $this.
W powyższym przykładzie nadawaliśmy wiek 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.
Powyższy kod powinien wyglądać następująco:
<?php
class Czlowiek {
public $kolorOczu;
public $kolorWlosow;
public $wiek=17; //nadajemy wartość domyślną
//teraz tworzymy metodę
public function pobierzWiek() {
return $this->wiek;
}
public function przypiszWiek($wiek) {
$this->wiek = $wiek; //można dodać tutaj sprawdzanie czy wprowadzona wartość jest int i mieści się w określonym przedziale
}
}
$czlowiek_1 = new Czlowiek();
$czlowiek_1->przypiszWiek(16);
$czlowiek_2 = new Czlowiek();
$czlowiek_2->przypiszWiek(18);
$czlowiek_3 = new Czlowiek();
echo '<pre>';
print_r($czlowiek_1);
echo '</pre>';
echo 'Pobrany wiek to: '.$czlowiek_1 -> pobierzWiek().'<br>';
echo '<pre>';
print_r($czlowiek_2);
echo '</pre>';
echo 'Pobrany wiek to: '.$czlowiek_2 -> pobierzWiek().'<br>';
echo '<pre>';
print_r($czlowiek_3);
echo '</pre>';
echo 'Pobrany wiek to: '.$czlowiek_3 -> pobierzWiek().'<br>';
?>
A teraz przykład z walidacją wieku:
<?php
class Czlowiek_przyklad {
public $kolorOczu;
public $kolorWlosow;
public $wiek='wiek nieznany'; //nadajemy wartość domyślną
//teraz tworzymy metodę
public function pobierzWiek() {
return $this->wiek;
}
public function przypiszWiek($wiek) {
$wiek = intval($wiek);//sprawdzanie czy wprowadzona wartość jest int i mieści się w określonym przedziale
if ($wiek > 0 && $wiek < 120)
$this->wiek = $wiek;
}
}
$czlowiek_1 = new Czlowiek_przyklad();
$czlowiek_1->przypiszWiek('sdf');
$czlowiek_2 = new Czlowiek_przyklad();
$czlowiek_2->przypiszWiek(18);
$czlowiek_3 = new Czlowiek_przyklad();
echo '<pre>';
print_r($czlowiek_1);
echo '</pre>';
echo 'Pobrany wiek to: '.$czlowiek_1 -> pobierzWiek().'<br>';
echo '<pre>';
print_r($czlowiek_2);
echo '</pre>';
echo 'Pobrany wiek to: '.$czlowiek_2 -> pobierzWiek().'<br>';
echo '<pre>';
print_r($czlowiek_3);
echo '</pre>';
echo 'Pobrany wiek to: '.$czlowiek_3 -> pobierzWiek().'<br>';
?>
Wyświetli się:
Czlowiek_przyklad5 Object
(
[kolorOczu] =>
[kolorWlosow] =>
[wiek] => wiek nieznany
)
Pobrany wiek to: wiek nieznany
Czlowiek_przyklad5 Object
(
[kolorOczu] =>
[kolorWlosow] =>
[wiek] => 18
)
Pobrany wiek to: 18
Czlowiek_przyklad5 Object
(
[kolorOczu] =>
[kolorWlosow] =>
[wiek] => wiek nieznany
)
Pobrany wiek to: wiek nieznany
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.
Ale trochę praktyki:
<?php
class Szkola {
public $czyZamknieta = true;
public function otworz() {
$this->czyZamknieta = false;
}
public function zamknij() {
$this->czyZamknieta = true;
}
public function sprawdzCzyZamknieta() {
return $this->czyZamknieta;
}
}
$szkola = new Szkola();
$szkola->zamknij();
$szkola->czyZamknieta = false; //zmieniamy wartość parametru
echo $szkola->sprawdzCzyZamknieta() ? 'szkoła zamknięta' : 'szkoła otwarta';
?>
//wyświetli szkoła otwarta
W powyższym przykładzie należy zwrócić uwagę na to, że pomimo iż ostatnią operacją na obiekcie było zamknięcie szkoły, wprowadzenie bezpośredniej zmiany pola $szkola->czyZamknieta na false, sprawiło że otrzymujemy informację, że szkoła jest otwarta. 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ą:
class Szkola_przyklad2 {
private $czyZamknieta = true; //modyfikator prywatny, brak dostępu z zewnątrz
public function otworz() {
$this->czyZamknieta = false;
}
public function zamknij() {
$this->czyZamknieta = true;
}
public function sprawdzCzyZamknieta() {
return $this->czyZamknieta;
}
}
$szkola = new Szkola_przyklad2();
$szkola->zamknij();
$szkola->czyZamknieta = false; //chcemy zmienić wartość parametru
//tutaj interpreter zwróci błąd, gdyż nie ma dostępu do zmiennej
echo $szkola->sprawdzCzyZamknieta() ? 'szkoła zakmnięta' : 'szkoła otwarta';
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 Kolo {
private $promien=5;
private function obliczSrednice() {
return $this->promien * 2;
}
public function czySieZmiesci($patyk) {
return $this->obliczSrednice() > $patyk ? 'TAK' : 'NIE';
}
}
$csz = new Kolo();
echo '<pre>';
print_r($csz);
echo '</pre>';
echo $csz->czySieZmiesci(5);
//wyświetli TAK
echo $csz->czySieZmiesci(40);
//wyświetli NIE
echo $csz->obliczSrednice(); //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:
<?php
class Choroba {
private $pesel='12345678900';
protected $stan_zdrowia=5;
public $imie;
public function czyIscDoLekarza(){
return $this->stan_zdrowia<2;
}
public function katar(){
return $this->choruj(2);
}
protected function choruj($iloscChorob) {
return $this->stan_zdrowia -= $iloscChorob;
if ($this->stan_zdrowia < 0)
return $this->stan_zdrowia = 0;
}
}
//następuje dziedziczenie
class Choroba1 extends Choroba{
private $goraczka=36.6;
public function goraczkaIkatar() {
return $this->temperaturaIkatar(2);
}
private function temperaturaIkatar($iloscChorob) {
if ($this->stan_zdrowia > 0) // tutaj mamy dostęp do pola stan_zdrowia, gdyż jest protected
return $this->katar($iloscChorob); // oraz do metody katar, gdyż jest public
return $this->goraczka += $iloscChorob;
}
}
$czlowiek = new Choroba1();
$czlowiek->imie = 'Jan';
$czlowiek->katar();
$czlowiek->goraczkaIkatar();
if ($czlowiek->czyIscDoLekarza())
echo $czlowiek->imie.' idziemy do lekarza!';
echo '<pre>';
print_r($czlowiek);
echo '</pre>';
//$stan_zdrowia = $czlowiek->stan_zdrowia; // ta linijka wygeneruje błąd, gdyż z zewnątrz nie mamy dostępu do pola
//$stan_zdrowia->choruj(1); // 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:
class Ja {
public function wyswietlImie(){
echo "Nazywam się: Jan Kowalski";
}
}
class Ty extends Ja {
public function wyswietlImie(){
echo "Nazywam się: John Smith";
}
}
$nazwa = new Ty();
$nazwa->wyswietlImie();
//wyświetli: Nazywam się: John Smith
W powyższym kodzie widzimy, że metoda wyswietlImie() została nadpisana i zamiast wyświetlić „Nazywam się: Jan Kowalski” otrzymaliśmy „Nazywam się: John Smith”.
Jeżeli chcemy zabronić klasie potomnej nadpisywania metod w klasie bazowej, używamy słowa final przed deklaracja metody:
class Ja {
final public function wyswietlImie(){
echo "Nazywam się: Jan Kowalski";
}
}
class Ty extends Ja {
/*public function wyswietlImie(){
ta metoda wygeneruje błąd: Fatal error: Cannot declare class Ty, because the name is already in use...
}*/
}
$nazwa = new Ty();
$nazwa->wyswietlImie();
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/
Destruktor – __destruct
Destruktor wykonuje się w momencie niszczenia obiektu.
Przykład użycia konstruktora i destruktora:
class Czlowiek_przyklad6
{
private $imie;
private $wiek;
public function __construct(string $imie, int $wiek)
{
$this->imie = $imie;
$this->wiek = $wiek;
}
public function getImie(): string
{
return $this->imie;
}
public function getWiek(): int
{
return $this->wiek;
}
public function __destruct()
{
echo '<br>Obiekt usunięty!';
}
}
$czlowiek = new Czlowiek_przyklad6('Jan Kowalski', 17);
echo 'imie: ' . $czlowiek->getImie() . ', wiek: ' . $czlowiek->getWiek(); // result: imie: Jan Kowalski, wiek: 17
unset($czlowiek); // usuwanie obiektu, result: Obiekt usunięty!
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::
class Okrag{
const PI = 3.14;
private $promien;
public function __construct($promien){
$this->promien = $promien;
}
public function obwod(){
return 2*$this->promien*self::PI;
}
}
// Okrag::PI = 40; // nie mozna modyfikowac
$dane=new Okrag(2);
echo 'Obwód wynosi: '.$dane->obwod().'<br>';
echo 'Liczba Pi wynosi: '.Okrag::PI.'<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::
class Licznik{
public static $ilosc = 0;
public function __construct(){
self::$ilosc++;
}
}
echo '$ilosc ='.Licznik::$ilosc.'<br />'; //wartość początkowa
$pierwsza = new Licznik;
$druga = new Licznik;
$trzecia = new Licznik;
echo '$ilosc = '.Licznik::$ilosc; //wartość po kilkukrotnym wykorzystaniu konstruktora
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.
abstract class Osoba {
abstract public function getName();
}
class Dziecko extends Osoba
{
public function getName()
{
return 'imię';
}
}
echo (new Dziecko())->getName();
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.
interface PersonInterface
{
public function getName(): string;
public function getAddress(): string;
}
interface UserInterface
{
public function getLogin(): string;
}
class Person implements PersonInterface, UserInterface
{
public function getName(): string
{
return 'Jan Kowalski<br>';
}
public function getAddress(): string
{
return 'Długa 1, Warszawa<br>';
}
public function getLogin(): string
{
return 'jkowalski<br>';
}
}
$osoba=new Person();
echo $osoba->getName();
echo $osoba->getAddress();
echo $osoba->getLogin();
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
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.
Przydatne linki:
http://php.net/manual/en/language.oop5.php
https://webmastah.pl/jak-programowac-obiektowo-cz-1-wstep/
https://kursphp.com/programowanie-obiektowe-php/
https://www.phpdevs.pl/programowanie-obiektowe
http://phpmajster.blogspot.com/2017/01/php-abstrakcja-interfejsy-i-factory.html
http://zasoby.open.agh.edu.pl/~09sdczerner/strona/page/programowanie_obiektowe.html
Przykład
Napisz klasę odcinek w przestrzeni dwuwymiarowej (x,y). Klasa ta ma zawierać:
Punkt początkowy i punkt końcowy o zakresie prywatnym.
Dostęp do punktów zrealizowany ma być za pomocą publicznych metod.
Metodę zwracającą długość odcinka.
class Odcinek{
private $pocz = array('x'=> 0, 'y' => 0);
private $kon = array('x'=> 0, 'y' => 0);
public function ustawWspolrzedne($pocz_x, $pocz_y, $kon_x, $kon_y)
{
$this->pocz['x'] = $pocz_x;
$this->pocz['y'] = $pocz_y;
$this->kon['x'] = $kon_x;
$this->kon['y'] = $kon_y;
}
public function wypiszWspolrzedne(){
echo "wektor ma wsporzedne :
[{$this->pocz['x']},{$this->pocz['y']}]
->[{$this->kon['x']},{$this->kon['y']}]";
}
public function zwrocDlugosc(){
return sqrt(($this->kon['x'] - $this->pocz['x'])**2 +($this->kon['y'] - $this->pocz['y'])**2);
}
}
$odc = new Odcinek();
$odc->ustawWspolrzedne(1,1,4,2);
$odc->wypiszWspolrzedne();
echo 'Długość odcinka to: '.$odc->zwrocDlugosc();
echo '<pre>';
print_r($odc);
echo '</pre>';
echo '<br><br>';
Przykład
OOP obsługujące fabrykę Chevroleta
<?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.
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
<?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.- 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 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.
1. 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”).
- Atrybuty:
-
Klasa
Guild:- Atrybuty:
name– nazwa gildii.members– lista graczy w gildii (obiekty klasyPlayer).
- 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ę).
- Atrybuty:
-
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.
- Atrybuty:
2. 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.
- Atrybuty:
-
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.
- Dodaj klasę
-
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ę
Weaponz atrybutamidamageispecial_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ę.
- Atrybuty:
-
Klasa
Player:- Atrybuty:
name– imię gracza.deck– talia kart (lista obiektów klasyCard).
- 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.
- Atrybuty:
-
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 Smoczydłowy” 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.
- Atrybuty:
-
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).
- Atrybuty:
-
Klasa
Team:- Atrybuty:
name– nazwa drużyny.players– lista obiektów klasyPlayer.
- 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.
- Atrybuty:
-
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.
- Atrybuty:
-
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.
- Atrybuty:
-
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.
- Atrybuty:
-
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.
-