Obiekty i klasy
Podstawowe pojęcia
• Abstrakcja
• Dziedziczenie
• Polimorfizm
• Kapsułowanie
(hermetyzacja)
• Wysyłanie komunikatów
• Powiązanie
• Agregacja
Definiowanie klasy oraz
tworzenie obiektu
<?php
class
nazwa_klasy {
//właściwości i metody klasy
}
$obiekt_klasy =
new
nazwa_klasy();
?>
Uwaga: właściwość = atrybut
Przykład klasy
<?php
class
Klasa {
public
$a = 12;
}
$obj =
new
Klasa();
$b = $obj -> a;
?>
Składowe klasy
•Klasy posiadają tzw.
składowe:
– Właściwości czyli
atrybuty
klasy
– Metody
czyli realizację
operacji
klasy
Definiowanie atrybutu
klasy
<?php
class
nazwa_klasy {
public
|
protected
|
private
$nazwa_atrybutu;
}
?>
•
public
– oznacza dostęp publiczny do atrybutu
•
protected
– oznacza dostęp ze wszystkich klas
dziedziczących dla danej klasy
•
private
– oznaczają dostęp do atrybutu jedynie
z wnętrz danej klasy.
Definiowanie metody klasy
<?php
class
Klasa {
[
final
|
abstract
] [
public
|
protected
|
private
] [
static
]
function
metoda([$arg1, $arg2, ..., $ardN])
{ciało_metody}
}
?>
Przykład odwołania się do metody klasy:
<?php
class
A {
function
dorwijmnie( ) {
return
$this
;
}
}
$objA =
new
A( );
if ($objB === $objA ->dorwijmnie( ))
echo
(‘Mam cię’);
?>
Prywatne metoda i atrybut
<?php
class
Klasa {
// atrybut zadeklarowany jako prywatny
private
$atrybut
// metoda zadeklarowana jako prywatna
private function
metoda() {
$
this->
atrybut = ’’To jest przykład atrybutu’’;
}
}
$obj =
new
Klasa();
$obj->metoda; //Błąd – próba odwołania się do ametody prywatnej
echo
($obj->atrybut);
//Błąd – próba odwołania się do atrybutu prywatnego
?>
Publiczna metoda i prywatny
atrybut
<?php
private
$atrybut; //atrybut zadeklarowany jako
prywatny
private function
metoda() {
$
this->
atrybut = ’’To jest atrybut’’;
}
//metoda zadeklarowana jako prywatna
public function
ustal_wartosc() {
$
this->
metoda();
}
// metoda zadeklarowana jako publiczna
public function
odczytaj_wartosc() {
return
$
this->
atrybut;
}
// metoda zadeklarowana jako publiczna
}
?>
Przykład klasy z atrybutami i
metodą
<?php
class ShopProduct {
public $title = ’’bez tytułu’’;
public $producerMainName = ’’nazwisko’’;
public $producerFirstName = ’’imię’’;
public $price = 0;
function getProducer( ) {
return ’’{$this->producerMainName}’’.
’’{$this-> producerFirstName}’’;
}
}
?>
Metoda konstrukcji obiektu
<?php
class ShopProduct {
public $title = ’’bez tytułu’’;
public $producerMainName = ’’nazwisko’’;
public $producerFirstName = ’’imię’’;
public $price = 0;
function __construct($title, $firstName,
$mainName, $price) {
$this->title
=
$title;
$this->producerFirstName = $firstName;
$this->producerMainName =
$mainName;
$this->price = $price;
}
function getProducer( ) {
return ’’{$this->producerMainName}’’.
’’{$this-> producerFirstName}’’;
}
}
?>
new ShopProduct(‘Alicja w krainie czarów’, ‘Ion’.
‘Charlston’, 15.99);
Metody i atrybuty o
różnym poziomie
dostępności I
<?php
//klasa Osoba jest klasą bazową dla różnych klas
class
Osoba {
protected
$imie;
protected
$nazwisko;
protected
$adres;
protected
$telefon;
protected
$email;
protected function __construct
($imie, $nazwisko, $adres,
$telefon, $email) {
$
this->
imie = $imie;
$
this->
nazwisko = $nazwisko;
$
this->
adres = $adres;
$
this->
telefon = $telefon;
$
this->
email = $email;
}
}
?>
Metody i atrybuty o
różnym poziomie
dostępności II
<?php
//klasa Pracownik dziedziczy wszystkie chronione
atrybuty klasy Osoba
class
Pracownik
extends
Osoba {
private
$stanowisko;
private
$dzial;
public function
__construct
($imie, $nazwisko,
$adres,
$telefon, $email, $stanowisko, $dzial) {
parent::__construct
($imie, $nazwisko, $adres,
$telefon,
$email);
$
this->
stanowisko = $stanowisko;
$
this->
dzial = $dzial;
}
?>
Metody i atrybuty o
różnym poziomie
dostępności III
<?php
//klasa Kierownik dziedziczy wszystkie chronione
atrybuty klasy Osoba
class
Kierownik
extends
Osoba {
private
$dzial;
public function
__construct
($imie, $nazwisko,
$adres,
$telefon, $email, $dzial) {
parent::__construct
($imie, $nazwisko, $adres,
$telefon,
$email);
$
this->
dzial = $dzial;
}
?>
Metody i atrybuty o
różnym poziomie
dostępności IV
<?php
//tworzenie obiektu klasy Pracownik
$prac =
new
Pracownik(‘Jan’, ‘Kowalski’, ‘Ulica 0/0’,
‘0-000-000-0000’, ‘jankowal@ect.pl’, ‘asystent’, ‘marketing’);
//tworzenie obiektu klasy Kierownik
$kier =
new
Kierownik(‘Jerzy’, ‘Nowak’, ‘gdzieś’, ‘0-111-111-0000’,
‘jerzynowak@ect.pl’, ‘marketing’);
?>
Stosowanie dziedziczenia
<?php
class BookProduct extends ShopProduct {
function getNumberOfPages( ) {
return $this->numPages;
}
function getSummaryLine( ) {
$base = ’’{$this->title} ( {$this-
>producerMainName}. ’’;
$base .= ’’{$this->producerFirstName} )’’;
$base .= ’’: liczba stron – {$this-
>numPages}’’;
return $base;
}
}
?>
Klasy i metody
abstrakcyjne
<?php
//klasa ShopProductWriter jest klasą abstrakcyjną, czyli
nie może //tworzyć obiektów
abstract class
ShopProductWriter {
protected
$products = array( );
//zadeklarowanie metody abstrakcyjnej wymusza jej
definicje w klasach
//potomnych
abstract public function
addProduct(ShopProduct
$shopProduct) {
$
this->
products[ ] = $shopProduct;
}
}
?>
Metody typu final
• Metody definiowane w klasach (zwykłych lub
abstrakcyjnych) mogą zostać zdefiniowane
jako metody niemodyfikowalne.
• Oznacza to, że definicja metody jest
ostateczna i nie może ulec zmianie w
klasach potomnych.
• Metody niemodyfikowalne definiowane są z
wykorzystaniem słowa kluczowego
final
.
Interfejsy
•
Interfejs (
interface
) można z pewnym przybliżeniem
potraktować jak klasę abstrakcyjną.
•
Jednak w przeciwieństwie do klasy abstrakcyjnej interfejs nie
może zawierać definicji atrybutów ani metod
nieabstrakcyjnych.
<?php
intrface Chargeable {
public function getPrice( );
}
class ShopProduct implements Chargeable {
// ...
public function getPrice( ) {
return ($this->price - $this->discount);
}
// ...
}
?>
Przykład użycia interfejsu
<?php
class Consultancy extends TimeService implements
Bookable, Chargeable {
// ...
}
?>
Narzędzia obiektowe
• Pakiety
– wcześniejsze wersje PHP5 nie obsługują
pakietów wprost, ale również dla wcześniejszych
wersji można organizować kod w pakieto-
podobnych strukturach.
• Włączanie kodu
– z naciskiem na ustanowienie
centralnie dostępnej lokalizacji kodu
bibliotecznego.
• Funkcje pomocnicze względem klas i obiektów
–
służące do testowania obiektów, klas, składowych
i metod.
• Interfejs Reflection API
– bezprecedensowy zestaw
wbudowanych klas pozwalających realizację
dynamicznych odwołań do informacji o klasach.
Pakiety
• Pakiet to zbiór powiązanych ze sobą klas i
innych plików.
• Pakiety służą do wyodrębniania i rozdzielania
poszczególnych części systemu.
• We współczesnych językach programowania
obsługa pakietów (modułów) jest
sformalizowana – jak np. w C++ gdzie pakiety
dysponują własnymi przestrzeniami nazw.
Packeges
business
util
WebTools.php
Customer.php
Invoice.php
Przykład organizacji
pakietu w konwencji
organizacji plików
Funkcja
require_once()
• Funkcja
require_once()
przyjmuje argumentem
wywołanie ścieżki do pliku, który to plik wstawiamy
do bieżącego skryptu. Włączenie to odbędzie się
tylko wtedy, kiedy plik określony argumentem nie
zostanie jeszcze włączony do skryptu w innym
miejscu.
• Powyższej właściwości jednokrotnego włącznia
pliku do kodu nie posiadają funkcje
require()
oraz
include()
.
• Programista ma swobodę wyboru pomiędzy
funkcjami:
require(), require_once()
oraz
include(),
include_once ().
Zaleca się jednak korzystanie z
pierwszej grupy funkcji, a to dla tego, że błąd w
pliku odczytywanym - powoduje przerwanie
wykonywania programu. Przykład getCD.php.
Konwencja PEAR
• PEAR to skrót od PHP Extension and Application
Repository. Adres PEAR to
• Nazwa każdego skryptu jest odzwierciedleniem
ścieżki dostępu, nazwy poszczególnych
podkatalogów są w nazwie skryptu (np. klasy)
rozdzielone znakiem podkreślenia.
• Np.
business_User
, czy też
XML_RPC_Server
.
Ścieżki przeszukiwania
•
W prezentowanych przykładach stosowaliśmy
ścieżki względne
require_once(”business/User.php”)
•
Jeśli już stosować w wywołaniach włączających kod
biblioteczny ścieżki względne, lepiej, aby miały one
postać:
require_once(” ../../projectlib/business/User.php”)
•
Można też stosować ścieżki bezwzblędne:
require_once(”/home/john/projectlib/business/User.php”)
•
Żadne rozwiązanie nie jest jednak idealne, bo
określając ścieżkę zbyt szczegółowo, zamrażamy
niejako położenie pliku bibliotecznego.
Ścieżki przeszukiwania
• Stosując ścieżki względne, ustalamy położenie
plików bibliotecznych względem bieżącego
katalogu roboczego, przez co utrudniamy
współużytkowanie plików bibliotecznych w
różnych projektach.
• Aby ułatwić odwołania do plików bibliotecznych,
rozdzielamy kod wywołujący od konkretnego
położenia plików bibliotecznych, tak aby ścieżkę:
business/User.php
można było wykorzystywać w dowolnym miejscu
systemu i w każdym z nich odnosiła się ona do
tego samego pakietu.
• Można to osiągnąć, umieszczając pakiet w jednym
z katalogów do których odwołuje się parametr
include_path
. Parametr ten jest ustawiany w pliku
php.ini
– pliku konfiguracyjnym PHP5.
Ścieżki przeszukiwania
• Parametr
include_path
definiuje listę ścieżek
dostępu wymienionych po dwukropkach (albo
średnikowych)
include_path = ”.:/usr/local/lib/php-libraries”
• W wywołaniach funkcji systemowych, jak
fopen()
,
czy
require()
, z względnymi ścieżkami dostępu,
których nie udaje się dopasować w kontekście
bieżącego katalogu roboczego, inicjowanie jest
ścieżek wskazanych w parametrze
include_path,
w kolejności ich definiowania w parametrze.
• Umieszczając katalog pakietu na liście ścieżek
przeszukiwanych, możemy w wywołaniach
włączających kod możemy zrezygnować z
samodzielnego określania ścieżek dostęppu.
Ścieżki przeszukiwania
•
W przypadku nie posiadania uprawnień do modyfikacji
pliku
php.ini
, można modyfikować
include_path
z
poziomu skryptu. Służy do tego funkcja
set_include_path()
.
•
Zazwyczaj parametr
include_path
zdefiniowany w
pliku php.ini zawiera już przydatne ścieżki
przeszukania, więc zamiast go zmazywać, można go
odczytać i dołączyć własne ścieżki do bieżącej
wartości parametru. Np. listing05.03.php
•
Odczyt parametru umożliwia funkcja
get_include_path()
.
•
Operacja rozszerzania listy ścieżek przeszukiwania
wygląda więc następująco:
set_include_path(get_include_path(),”:/home/john/php
lib/”)
Automatyczne wczytywanie
kodu
•
Niekiedy dobrym pomysłem jest definiowanie każdej klasy
występującej w pakiecie w oddzielnym pliku. Każdy taki plik
może zostać opatrzony nazwą pochodzącą od nazwy klasy.
Np. klasę
ShopProduct
umieścimy w pliku
ShopProduct.php
. Jednak samą klasę powinniśmy nazwać
zgodnie z konwencją PEAR w oparciu o jej przynależność do
pakietu:
business_ShopProduct.php. Co redukuje możliwość kolizji
nazw.
•
W PHP5 pojawiła się funkcja
__autoload()
. Która ma postać:
function __autoload($classname) {
include_once(”$classname.php”);
}
$product = new ShopProduct(‘The Darkening’, ‘Harry’,
‘Hunter’, 22.99);
•
Np. listing05.04.php i listing05.05.php
Szukanie klasy
• Programista może uzyskać tablicę wszystkich
zdefiniowanych dotąd klas z pomocą funkcji
get_declared_classes().
print_r(get_declared_classes())
• Wykonanie
• Np. listing05.08.php
Badanie obiektów i klas
• Np. listing05.09.php
Badanie metod
• Np. listing05.12.php, listing05.13.php,
listing05.14.php, listing05.15.php,
listing05.16.php
Badanie składowych
• listing05.17.php
Badanie związków
dziedziczenia
• Np. listing05.18.php, listing05.19.php
Badanie wywołań metod
• listing05.20.php, listing05.21.php,
listing05.22.php, listing05.23.php
Reflection API
• listing05.24.php, listing05.25.php,
listing05.26.php, listing05.27.php,
listing05.28.php
Obiekty zaczynają działać
•
Przyjrzyjmy się prostej rzeczywistej aplikacji
obiektowej - po zapoznaniu się z podstawami
podejścia, w językach:
– UML
– PHP5
•
Przykładem jest program zarządzający
informacjami kontaktowymi pozwalającym
użytkownikom wyszukiwać, dodawać i edytować
dane.
•
Program (aplikacja) zarządzania kontaktami
pozwala na gromadzenie informacji kontaktowych o
osobach i zależność między jednostkami.
Wymagania na program
•
Aplikacja ma przechowywać informacje o kontaktach
(osobach i organizacjach) w bazie danych i
wyświetlać je na stronie WWW.
•
Kontakty mogą mieć zero lub więcej adresów
pocztowych, adresów e-mailowych i numerów
telefonów.
•
Osoba może mieć tylko jednego pracodawcę
(organizację).
•
Organizacja może mieć zero lub więcej pracowników
(osób).
Klasy: Address, Email,
PhoneNumber
• Rozpoczynamy od utworzenia diagramu
klas posługując się programem Visual
Paradigm for UML.
• Najpierw tworzymy trzy klasy zawierające
odpowiednio informacje:
– Adresowe
– Mailowe
– Numery telefoniczne
Klasy dla trzech różnych
informacji
Dodanie klasy Individual
• Do diagramu należy dołączyć klasę
Individual (osoba).
• Osoba ma przypisane:
– Imię
– Nazwisko
– Unikalny identyfikator (pole id w bazie danych)
– Listę adresową (adres pocztowy, adres e-
mailowy, numer telefonu)
– Pracodawcę
– Stanowisko pracy
– Metody (addAddress, addEmail, addPhone).
Diagram z klasą Individual
Dodanie klasy
Organization
• Do diagramu należy dołączyć klasę
Organization (organizacja).
• Organizacja ma przypisaną:
– Nazwę
– Unikalny identyfikator
– Listy adresowe
– Metody służące do dołączania obiektów
– Listę pracowników.
Diagram z klasą
Organization
Dodanie klasy Entity
• Klasy Individual i Organization mają wiele
wspólnych atrybutów i metod.
• To sugeruje, że dzięki użyciu dziedziczenia
można zaoszczędzić wiele pracy, zwiększając
jednocześnie elastyczność aplikacji
• Należy utworzyć nową klasę Entity, która zawiera
wspólne cechy klas Individual i Organization.
• Na diagramie UML zapisuje się wyłącznie te
atrybuty i metody, które dana klasa
bezpośrednio implementuje.
Diagram z klasą Entity
Określenie dalszych związków
klas
•
Związek klas pokazany na diagramie zawiera
jeszcze jeden typ związków obok dziedziczenia.
•
Związek ten nazywamy kompozycją (
composite
aggregation
), zawierającą krotności
występowania obiektów biorących udział w
związku.
•
Dzięki zapisaniu związku na diagramie, łatwo
można stwierdzić, na które części aplikacji
wpłynie modyfikacja jednego z elementów.
•
Zmiany w klasach
,
Address
,
PhoneNumber
będą miały wpływ na klasy
Entity
,
Individual
oraz
Organization
.
Diagram ze związkami
Dodanie dalszych
elementów
•
Stworzyliśmy więc wstępną architekturę aplikacji.
•
Warto przypomnieć, że dobrą praktyką jest ochrona
danych w prywatnych zmiennych i operowanie na
nich wyłącznie za pomocą tzw. metod dostępowych.
•
W tym celu posługujemy się metodami __get ( )
oraz __set ( ) upraszczając ten proces.
•
Wykorzystując mechanizm dziedziczenia, można
przenieść część funkcjonalności z klasy Entity do
nowej klasy, którą być może wykorzystamy w
przyszłości w innych aplikacjach.
•
Taką klasą rodzicem będzie PropertyObject (obiekt
własności).
•
Uzupełniamy aplikację warstwą odpowiedzialną za
dane.
Kompletn
y
diagram
klas
przykładu