wtorek, 20 września 2011

Jak programować obiektowo cz.1 - wstęp

trochę przydługi wstęp...

Jest szereg tutoriali pokazujących jak wykorzystać techniki obiektowe, jak je zastosować w kodzie, ale w większości z nich brak jednej ważnej informacji - OOP to przede wszystkim obiektowe myślenie. Techniki i sposób ich implementacji w danym języku programowania są jak język mówiony i gramatyka, są czymś zmiennym. Natomiast sposób myślenia obiektowego, umiejętność podejmowania decyzji w kwestiach relacji między klasami jest czymś stałym. Nie ważne, czy piszesz kod w PHP, Javie, C++ itp., ponieważ, jeżeli potrafisz poprawnie zaprojektować strukturę, to będzie ona uniwersalna, zmieni się jedynie sposób przetłumaczenia tego na język zrozumiały dla interpretera/kompilatora.

A więc o czym to będzie?
Właściwie to o tym samym, co wszędzie, czyli podstawy takie, jak: dziedziczenie, interfejsy, abstrakcje, hermetyzacja itd. Z tym, że oprócz przykładu użycia postaram się jeszcze przedstawić tok myślenia, który się za tym kryje. Tak, więc np. w dziedziczeniu nie chodzi tylko o to, że mamy dwie klasy Samochód i Samolot, które dziedziczą po abstrakcyjnej klasie Pojazd:)
Postaram się zaprezentować przykłady, kiedy dane rozwiązanie jest niekoniecznie dobre lub całkiem złe.
Tak naprawdę najwięcej będzie o myśleniu, bo w gruncie rzeczy do tego OOP się sprowadza. Ostatnim etapem dobrego programowania obiektowego jest pisanie kodu.

programowanie obiektowe - wtf?

Programowanie obiektowe jest to przedstawienie świata rzeczywistego i relacji w nim zachodzących, za pomocą obiektów. Najważniejszymi cechami programowania obiektowego są:
  • Abstrakcja
  • Enkapsulacja
  • Dziedziczenie
  • Polimorfizm
Wszystkie te pojęcia rozwinę w kolejnych wpisach.
Dzisiaj skupię się na kwestiach językowych, które są niezbędne do rozpoczęcia zabawy z OOP:

klasa

Klasa jest to typ zmiennej z punktu widzenia programowania, natomiast w ujęciu projektowym jest to ogólna definicja pewnej grupy powiązanych ze sobą obiektów. Klasa definiuje metody, czyli funkcjonalność, które są dostarczane przez obiekty. Poza tym definiuje również atrybuty, które są indywidualne (nie zawsze, ale do tego tematu jeszcze wrócę:) dla konkretnych obiektów. Czym jest obiekt? Jest to instancja danej klasy, czyli konkretna zmienna danego typu. Prosty przykład:
Janek, Ania, Zosia to obiekty klasy Człowiek. Każde z nich może spać, jeść, poruszać się i to są właśnie metody zdefiniowane w klasie Człowiek. Oprócz tego każdy człowiek posiada imię oraz datę urodzenia, jednak są one indywidualne dla każdego obiektu, czyli nie są bezpośrednio powiązane z klasą, a z obiektem danej klasy
A jak coś takiego napisać?
<?php
class Human
{
  private $_name;
  private $_birthDate;
  
  public function __construct($name, $birthDate) {/*...*/}
  public function eat() {/*...*/}
  public function sleep() {/*...*/}
  public function move() {/*...*/}
}

$janek = new Human('Janek', '1982-01-09');
$anna = new Human('Anna', '1950-02-19');
$zosia = new Human('Zosia', '1999-11-17');
Z informacji dotyczących klasy warto przeczytać jeszcze o operatorach widoczności.

abstrakcja i dziedziczenie

Klasa abstrakcyjna różni się od zwykłej klasy tym, że nie można utworzyć jej instancji. Może ona również dostarczać metod abstrakcyjnych, czyli metod, które zostały zadeklarowane i muszą zostać zdefiniowane w klasach potomnych. Aby to rozjaśnić rozwinę przykład z poprzedniego akapitu:
Pomimo iż Janek, Ania i Zosia są obiektami klasy Człowiek, to są pewne różnice charakterystyczne dla grup np. Ani i Zosia mogą urodzić dziecko, ponieważ są Kobietami, natomiast Janek nie doświadczy tego przywileju gdyż jest Mężczyzną. Dodatkowo różni ich też sposób sikania, pomimo tego, że i Kobiety i Mężczyźni to robią. Więc tak naprawdę żadne z nich nie jest tylko Człowiekiem, ale jest albo Kobietą, albo Mężczyzną
i kod:
<?php
abstract class Human
{
  private $_name;
  private $_birthDate;
  
  public function __construct($name, $birthDate) {/*...*/}
  public function eat() {/*...*/}
  public function sleep() {/*...*/}
  public function move() {/*...*/}
  abstract public function pee();
}

class Woman extends Human
{
   public function bornChild() {/*...*/}
   public function pee() {/*...*/}
}

class Man extends Human
{
   public function pee() {/*...*/}
}

$janek = new Man('Janek', '1982-01-09');
$anna = new Woman('Anna', '1950-02-19');
$zosia = new Woman('Zosia', '1999-11-17');

interfejs i realizacja

Specyficzną formą abstrakcji jest interfejs. Od klasy abstrakcyjnej odróżniają go dwie podstawowe rzeczy:
  • nie może definiować żadnych atrybutów, jedynie metody i stałe
  • wszystkie metody muszą być abstrakcyjne i publiczne
Interfejs służy do zapewnienia pewnej pożądanej funkcjonalności, bez jej implementacji.
Tak więc, gdyby Człowieka ograniczyć jedynie do sikania, to nasz kod mógłby wyglądać tak:
<?php
interface Human
{
   public function pee();
}

class Woman implements Human
{
   public function pee() {/*...*/}
}

class Man implements Human
{
   public function pee() {/*...*/}
}
Więcej o klasach abstrakcyjnych i interfejsach tutaj. Dodatkowo polecam przeczytanie artykułu o referencjach wykorzystywanych przy dziedziczeniu.

czy to wszystko?

Więcej w związku z kwestiami językowymi, które warto wykorzystać przy programowaniu obiektowym można znaleźć tutaj. Zdaję sobie również sprawę, że jest jeszcze kilka rzeczy, które warto poruszyć (np. metody/atrybuty statyczne), pominąłem je jednak celowo, ponieważ szerzej te zagadnienia poruszę w kolejnych wpisach. Również wiem, że żaden temat nie został wyczerpany w zadowalającym stopniu, ale chciałem się skupić na przedstawieniu struktur językowych, które są potrzebne do rozpoczęcia całej przygody z OOP, a nie na logice, która się za tym kryje. Do tego dojdę przy następnych wpisach:)

Proszę o komentarze, jeżeli ktoś zauważy coś niejasnego bądź błędnego. Zdaję sobie sprawę, że OOP, to na tyle trudny temat, że mogą się wkraść jakieś przekłamania.