fb pixel

Jak programować obiektowo? cz.1 – wstęp

|

Kiedy zaczynałem swoją przygodę z programowaniem obiektowym szybko trafiłem na tutoriale i wszelkiego rodzaju inne kursy, które pokazywały jak wykorzystywać struktury obiektowe w kodzie. Niestety większość z nich demonstrowało jedynie, jak napisać swoją pierwszą klasę, interfejs, czy też stworzyć obiekt. Wszystko sprowadzało się do „gramatyki” języka, czyli słów kluczy, sposobu tworzenia tych struktur, itp. Gdzieś po drodze „gubiła się” jednak informacja skąd takie, a nie inne decyzje, dlaczego np. interfejs ma same metody abstrakcyjne? Do tego publiczne? Po co te widoczności? I tak dalej.

Dlatego też postaram się oderwać Was trochę od kodu (choć oczywiście postaram się o wystarczającą ilość odpowiednich przykładów), a postaram się skupić na tym, co jest najistotniejsze - na obiektowym myśleniu. Bo wbrew temu co można sądzić, wszystko na co możecie sobie pozwolić i czego Wam nie wolno pisząc obiektowo, ma swoje uzasadnienie.

To, jak implementujecie obiektowe rozwiązania w danym języku, jest czymś zmiennym, stałe pozostają wybierane rozwiązania, sposób rozumowania, proces podejmowania decyzji. Nie ważne, czy piszesz kod w PHP, Javie, C++ itp., ponieważ, jeżeli potrafisz go poprawnie zaprojektować, to ten projekt będzie uniwersalny, zmieni się jedynie sposób przetłumaczenia go na język zrozumiały dla interpretera/kompilatora danego języka.

Dobra, ale żeby nie przedłużać już i tak wystarczająco długiego wstępu, przejdźmy do rzeczy. Dzisiaj będzie trochę podstaw. Od czegoś w końcu trzeba zacząć.

Programowanie obiektowe - co to jest?

Programowanie obiektowe jest próbą przedstawienia świata rzeczywistego i relacji w nim zachodzących, za pomocą obiektów. Najważniejszymi cechami programowania obiektowego są:

  • Abstrakcja
  • Enkapsulacja
  • Dziedziczenie
  • Polimorfizm

Wszystkimi tymi pojęciami zajmiemy się w kolejnych wpisach. Dzisiaj skupimy się przede wszystkim na języku i na rzeczach, które są niezbędne do rozpoczęcia zabawy z OOP.

Klasa

Klasa, z punktu widzenia programowania, jest to typ zmiennej. Natomiast w ujęciu projektowym jest to ogólna definicja pewnej grupy powiązanych ze sobą obiektów, które różnią się tożsamością. 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 wrócimy w przyszłości) dla konkretnych obiektów. Czym jest obiekt? Jest to instancja danej klasy, czyli konkretna zmienna danego typu.

Prosty przykład dla rozjaśnienia sprawy:

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 jej instancją (obiektem).

A jak coś takiego wyrazić za pomocą kodu?

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');

Abstrakcja i dziedziczenie

Klasa abstrakcyjna różni się od zwykłej klasy tym, że nie można utworzyć jej instancji. Może dostarczać zarówno metody zdefiniowane jak i abstrakcyjne, czyli takie, które zostały zadeklarowane i muszą zostać zdefiniowane w klasach potomnych.

Dodajmy coś więcej do przykładu z wcześniejszego akapitu:

Oczywiście Janek, Ania, Zosia to obiekty klasy Człowiek. Niemniej jednak, oprócz tego wszyscy są ssakami, tak jak Pies Janka - Reksio.

Z powyższego wyłania nam się następujący kod:

abstract class Mammal
{
    // code
    public function drinkMilk() {/*...*/}
}

class Human extends Mammal
{
    // code
}

class Dog extends Mammal
{
    // code
}

Dlaczego Mammal jest klasą abstrakcyjną? Ponieważ pomimo tego, że ssakiem jest zarówno pies jak i człowiek, to nie jesteśmy w stanie utworzyć ssaka (nie urodzi się taki), który nie będzie przynależał do jakiegoś konkretnego gatunku. Nie zmienia to jednak faktu, że chcemy tą wiedzę (o tym, co ssakiem jest) posiadać.

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

Interfejsy służą do zapewnienia pewnej pożądanej funkcjonalności, są pewnego rodzaju kontraktem pomiędzy klasami, które interfejs wykorzystują, a tymi, które go implementują. Interfejsy są po to, aby zagwarantować nam, że jakaś metoda istnieje, a nie jak się zachowuje.

I wracając do naszych przyjaciół ze wcześniejszych przykładów:

Ssaki, jak wszystkie inne Zwierzęta, poruszają się i jedzą. Niemniej jednak inaczej przemieszczają się ryby, inaczej ptaki, a inaczej ludzie.
To samo tyczy się jedzenia.

I tak rodzi nam się kod przedstawiony poniżej:

interface Animal
{
    public function eat();
    public function move();
}

abstract class Mammal implements Animal
{
    // code
}

abstract class Bird implements Animal
{
    // code
}

Jeżeli chcielibyście sie dowiedzieć więcej o różnicach pomiędzy interfejsem, a klasą abstrakcyjną to zapraszam tutaj.

Czy to wszystko?

To, co napisałem dzisiaj jest jedynie wstępem i miało na celu wprowadzenie Was do tematu. Jest jeszcze wiele kwestii przed nami, które warto poruszyć, więc uzbrójcie się w cierpliwość - stopniowo przebrniemy przez wszystkie.

PS. Seria o tym samym tytule pojawiła się również u mnie na blogu kilka lat temu. Niemniej jednak, dzięki możliwości opublikowania jej tutaj, mam sposobność skonfrontowania tego, co napisałem wtedy, z tym, co wiem teraz. Dlatego też z pewnością tutaj pojawi się ulepszona i przeredagowana jej wersja.

Pozostałe artykuły z cyklu

Dodaj na LinkedIn
Sebastian Malaca
Jestem fanatykiem obiektowego programowania i nieustannie pogłębiam swoją wiedzę we wszelkich tematach z nim związanych. Wszystko czego się dowiem konfrontuję z rzeczywistością, ponieważ teoria, która nie ma odzwierciedlenia w praktyce, traci swój sens tam, gdzie zaczyna się praca programisty :)