czwartek, 8 grudnia 2011

Jak programować obiektowo cz. 5 - metody cz III

let's do something cool:)

W końcu udało mi się napisać mniej więcej (mam nadzieję, że więcej:) wszystko na temat tego, co warto wiedzieć o metodach w teorii. Pora, więc na zobaczenie jak to wszystko sprawdza się w praktyce.

Tak więc mamy nasz przykład z drugiego wpisu:
Tworzymy system dla firmy transportowej. Jednym z głównych części składowych ma być baza kontrahentów. Przy dodawaniu kontrahenta użytkownik musi określić jego NIP oraz unikalną nazwę. Musi istnieć możliwość wystawiania i przesyłania drogą mailową faktur za usługi firmy oraz musi być możliwość wykonania przelewu na konto kontrahenta, z którego usług korzystała firma. Firma jest międzynarodowa, więc musi istnieć możliwość obsługi różnych walut. Niektórzy kontrahenci mają kilka osób, do których chcą aby były wysyłane faktury na maila. Dodatkowo powinna istnieć możliwość określenia adresu siedziby kontrahenta.

czwartek, 1 grudnia 2011

Pusty interfejs?

Czym są interfejsy? Cytując za Wikipedią:
"In object-oriented languages the term "interface" is often used to define an abstract type that contains no data, but exposes behaviors defined as methods."
Istotnym fragmentem tego zdania jest: '[...] exposes behaviors defined as methods.', czyli interfejs definiuje pewne zachowania w postaci metod, których ciało należy zaimplementować w każdej klasie, która implementuje dany interfejs.
Dzięki temu możemy określić jakie zachowanie mają posiadać klasy.

poniedziałek, 28 listopada 2011

Programistyka na Google+ i Facebooku

social networks, I'm coming:)

Witam, dzisiaj krótki wpis całkowicie nie powiązany z programowaniem obiektowym.

Chciałem zaprosić wszystkich zainteresowanych programowaniem obiektowym do obserwowania mojej strony na facebook'u lub/oraz na google+.

piątek, 18 listopada 2011

Jak programować obiektowo cz. 4 - metody klas II

ciągle o tym samym

W dzisiejszym wpisie kontynuuje wątek z poprzedniego wpisu, a więc nadal będzie o metodach:)
Dzisiaj zbiór wszelkich różnych rzeczy dotyczących metod, czyli trochę informacji, a trochę dobrych praktyk, które warto przestrzegać, aby z kodem przyjemniej się pracowało:)

czwartek, 10 listopada 2011

Jak programować obiektowo cz. 3 - metody klas I

po długiej przerwie

Trochę to potrwało zanim znalazłem chwilę czasu, żeby coś napisać, ale mam nadzieję, że warto było czekać:) Postaram się, żeby kolejne wpisy z tej serii pojawiały się częściej, a że w najbliższym czasie nie planuję już żadnych długoterminowych prac, myślę, że jest to założenie, które uda mi się zrealizować.
Dobra, bez dłuższego przynudzania, zaczynam. Dzisiaj kilka słów o metodach, czyli:

sobota, 8 października 2011

Jak programować obiektowo cz. 2 - atrybuty klasy

wstęp

Na potrzeby wszystkich wpisów z tej serii zdecydowałem się, w miarę możliwości, skupić je wokół jednego przykładu. Oczywiście nie będę przeprowadzał żadnej analizy wymagań, ani specjalnie rozwodził się nad całą strukturą i pełną funkcjonalnością, będę poruszał jedynie te aspekty aplikacji, które będą bezpośrednio powiązane z danym wpisem. Dzięki takiemu praktycznemu przykładowi mam nadzieję, że uda mi się przedstawić myślenie, które kryje się za większością decyzji projektowych oraz pozbędę się zbędnego teoretyzowania i przykładów, które nijak nie pokrywają się z rzeczywistością.
Dzisiaj chciałbym skupić się na atrybutach klasy, więc na początek trochę teorii.

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.

poniedziałek, 19 września 2011

self, parent i static

W PHPie istnieją trzy przydatne słowa kluczowe będące referencją do metod oraz atrybutów klas. Wykorzystuje się je przy dziedziczeniu i są to: self, parent, static.

piątek, 12 sierpnia 2011

public, protected czy private? Oto jest pytanie!


O tym czym różnią się operatory widoczności można przeczytać tutaj.
Ostatnio zauważyłem dziwną tendencję do używania jedynie dwóch z nich, a mianowicie protected i public. Publiczne są metody (ewentualnie dane), które mają być dostępne na zewnątrz, a wszystko inne jest chronione. W myśl zasady: może kiedyś będę tą klasę rozszerzał. Osobiście nie zgadzam się z takim podejściem. Projektując klasę wiemy, co chcemy aby robiła, a takie wybieganie w przyszłość 'bo może coś się przyda' powoduje niepotrzebny śmietnik.

Moim zdaniem głównymi operatorami, na których powinien się opierać programista projektując klasę, to private i public, a dopiero w dalszej kolejności protected.

Decyzja o tym, którego operatora należy użyć będzie łatwiejsza o ile będzie się pamiętało o kilku rzeczach:
  • Enkapsulacja, czyli atrybuty klas powinny być prywatne. Dlaczego? Krótko i rzeczowo jest to wyjaśnione tutaj.
  • Gdy już mamy atrybuty klasy, trzeba zastanowić się do czego ta klasa będzie wykorzystywana i na tej podstawie należy zdefiniować metody publiczne, czyli te, które będą wykorzystywane przez inne klasy.
  • Jeżeli nie planujesz w obecnej chwili, aby klasa była rozszerzana, to wszystkie metody, które nie są wykorzystywane poza klasą powinny być prywatne. Jeżeli zakładasz, że "może w przyszłości", to również zostaw je jako prywatne. Kod nie jest stałym bytem, może ewoluować, a więc jeżeli zajdzie potrzeba, to zawsze możesz go rozszerzyć lub zmienić.
  • Jeżeli klasa będzie rozszerzana, to metodami chronionymi powinny być jedynie te, które są potrzebne w klasie pochodnej, a nie wszystkie niepubliczne.
  • Jeżeli klasa pochodna powinna mieć dostęp do atrybutów klasy, do których nie ma akcesorów, to nie oznacza, że należy zmienić widoczność atrybutów na protected. To oznacza, że można dodać chronione settery i/lub gettery.
  • Zastanów się trzy razy zanim podejmiesz decyzję o zmianie operatora private na protected w metodach, dziesięć razy jeżeli chcesz to zrobić z atrybutem.
  • Gruntownie przemyśl decyzję o wyciągnieciu metody na zewnątrz, czyli zmianie operatora na public. Rób to tylko wtedy, gdy metoda jest rzeczywiście potrzebna poza klasą.
  • Rzadko istnieje rzeczywista potrzeba zmiany operatora widoczności atrybutu z private na mniej restrykcyjny - protected. Jeszcze rzadziej zachodzi potrzeba upublicznienia atrybutu.

Mam nadzieje, że tych kilka wskazówek się przyda:)

wtorek, 9 sierpnia 2011

Zmienne globalne - a dlaczego nie?


Mimo rozwoju PHP pod kątem OOP, nadal zdarza się niektórym programistom uparcie tworzyć zmienne globalne. Dlaczego? Powód jest prosty i sensowny - w większości aplikacji występują zmienne, które powinny być dostępne w kilku miejscach. Oczywiście można przekazywać ową zmienną z obiektu do obiektu, ale z dwojga złego decydują się na użycie zmiennych globalnych.

Dlaczego takie rozwiązanie nie jest dobre? Abstrahując od faktu, że nie ma zbyt wiele wspólnego z programowaniem obiektowym, to zawsze istnieje możliwość przypadkowego nadpisania jej w innym miejscu w kodzie. Prawdopodobieństwo to zwiększa się, gdy nad kodem pracuje większa ilość programistów.

Więc jakie mamy alternatywy?

Jeżeli zmienna ma być jakąś stałą wartością, niezmienną w całym kodzie i jest typu podstawowego (int, float, bool, string, null), to można skorzystać z metody define() w celu utworzenia wartości stałej (constant).

Co innego, gdy naszą zmienną jest obiekt. W takim wypadku możemy skorzystać z wzorca Singleton, aby mieć pewność, że obiekt będzie posiadał jedną instancję, do której będziemy mogli się dostać w każdym fragmencie kodu poprzez wywołanie statycznej metody zwracającej tą instancję.

Czasami jednak mamy potrzebę przekazywania większej ilości zmiennych różnych typów. Z pomocą przychodzi wzorzec Property. Jest to pewnego rodzaju kontener na zmienne, do których dostęp powinien być możliwy z każdego miejsca w kodzie.

Wybranie dowolnego z tych rozwiązań powinno być jednak gruntownie przemyślane, ponieważ tego typu obiekty utrudniają testowanie (szczególnie klasy implementujące wzorzec singleton). Poza tym nadużywanie któregokolwiek rozwiązania może przyczynić się do zmniejszenia czytelności kodu.

sobota, 23 lipca 2011

Własne wyjątki - a na co mi to?


O tym, czym są wyjątki można przeczytać w wikipedii lub na php.net. Ogólnie rzecz biorąc wyjątki powinny być wyrzucane wtedy, gdy w jakiś sposób nasza aplikacja podejmie próbę wykonania niedozwolonej operacji lub nie będzie w stanie wykonać operacji.
Od wersji 5 PHP jest dostarczany z bardzo ciekawą biblioteką SPL, która narzuca pewną hierarchię wyjątków i dzieli je na dwie główne grupy:
  • RuntimeException - są to wyjątki, które powinny być wyrzucane, gdy aplikacja z pewnych względów nie może poprawnie działać np. zerwanie połączenia z bazą danych.
  • LogicException - są to wyjątki, które powinny być wyrzucane, gdy aplikacja próbuje wykonać operacje, które są błędne np. wywołanie nieistniejącej metody na przekazanym obiekcie.
Szerszy opis wszystkich wyjątków, które są dostarczone wraz z biblioteką SPL znajdziecie tutaj.

Dobra, po trochę przydługim wstępie, wracam do głównego wątku:) A więc po co tworzyć własne wyjątki? Przecież można z łatwości wpisać odpowiedni message do konstruktora tego bazowego (Exception) i po sprawie. Oczywiście, że tak można, ale tylko do momentu, gdy wyjątki są rzeczywiście tylko i wyłącznie kontenerem na jakąś informację.

Oczywiście powodów stosowanie wyjątków jest kilka, ja jednak pozwolę sobie skupić się na, moim zdaniem najważniejszym, czyli utrzymywaniem spójnej logiki tzn. jeżeli mam klasę Product, Product_Factory, Product_Collection etc. to dobrze jest mieć jeszcze Product_Exception, który będzie wykorzystywany w tych właśnie klasach i tylko w tych. Dzięki temu już po samym typie wyjątku wiem, gdzie się wysypała aplikacja, a do dokładniejszego określenia miejsca załamania powinno się wykorzystywać wartość $message.
Oczywiście można tutaj popaść w pewną skrajność i zacząć tworzyć wyjątki dla wszystkiego i tak dostajemy: Product_Exception, Product_Factory_Exception, Product_Collection_Exception etc., gdzie każdy wyjątek jest wyrzucany jeden, no góra dwa razy w całym kodzie. I po co to? Niestety jest to częsta przypadłość programistów, którzy dopiero odkryli tą cudowną możliwość dziedziczenia po klasie Exception - po prostu zapominają o tym, że wyjątek posiada jeszcze argument $message.

Niestety złotego środka nie ma i już we własnym zakresie trzeba zdecydować, czy to już powinien być kolejny typ wyjątku, czy jeszcze ten sam, z odpowiednią wiadomością. Granica jest płynna, ważne jest to, aby pamiętać o dwóch rzeczach:
  • Dziedziczyć po klasie Exception jest w PHP dozwolone.
  • Konstruktor klasy Exception posiada możliwość przyjmowania parametrów m.in. $message i $code.

czwartek, 21 lipca 2011

foreach() i for() - subtelna różnica


Pewnie dla wielu osób, które z php mają do czynienia od dłuższego czasu, ten wpis nie będzie niczym odkrywczym.
Czym różni się foreach() od for()? W wielu przypadkach nie ma żadnej różnicy dla logiki aplikacji, której pętli się stosuje i można je stosować zamiennie bez wpływu na jej działanie.

Więc gdzie jest różnica? Otóż foreach() przechodzi po elementach tablicy w kolejności dodawania wartości, natomiast for() jest używany do iterowania tablicy w zdefiniowany przez nas sposób.
Poniższy kod wyjaśnia wszystko:
<?php
$a = array();
$a[3] = 'a';
$a[1] = 'b';
$a[0] = 'c';
$a[2] = 'd';

$arrayCount = count($a);

echo "FOR: \n";
for ($key = 0; $key < $arrayCount; $key++)
    echo 'key: ' . $key . ', value: ' . $a[$key] . "\n\n";

echo "FOREACH: \n";
foreach ($a as $key => $value)
    echo 'key: ' . $key . ', value: ' . $value . "\n";

Wykonanie go daje taki wynik:
FOR:
key: 0, value: c
key: 1, value: b
key: 2, value: d
key: 3, value: a

FOREACH:
key: 3, value: a
key: 1, value: b
key: 0, value: c
key: 2, value: d

czwartek, 14 lipca 2011

Abstrakcja vs Interfejs


Klasy abstrakcyjne i interfejsy są bytami, które nie tylko ułatwiają pisanie kodu, ale także sprawiają, że kod jest czytelniejszy i bardziej przejrzysty, a logika aplikacji prostsza do zrozumienia. Ich poprawne i przemyślane zastosowanie z pewnością opłaca się, gdy aplikacja zaczyna się rozrastać i/lub zmieniać.
O tym co to jest interfejs oraz klasa abstrakcyjna można przeczytać w wikipedii. I choć zrozumienie tego, czym są nie jest wcale takie skomplikowane, to poprawne ich stosowanie nie jest czasami oczywiste.

Nie chcę tytułować się żadnym ekspertem w tej dziedzinie, ale po pewnym czasie spędzonym nad projektowaniem aplikacji i wdrażaniem tych rozwiązań, chciałem się podzielić wnioskami.
Kiedy więc powinno się stosować interfejsy i/lub klasy abstrakcyjne?
Pozwolę sobie to wszystko zaprezentować na prostym przykładzie:
<?php
class Man
{
  public function sleep() {/*...*/}
  public function eat() {/*...*/}
  public function doMenStuff() {/*...*/}
}

class Woman
{
  public function sleep() {/*...*/}
  public function eat() {/*...*/}
  public function doFemaleStuff() {/*...*/}
} 

W powyższym przykładzie mamy dwie klasy (Man i Woman), w których jesteśmy w stanie wyodrębnić pewną część wspólną. W takiej sytuacji warto rozważyć utworzenie nadklasy abstrakcyjnej po której będą dziedziczyły obie klasy. W tym wypadku takie rozwiązanie jest idealne:
<?php
abstract class Human
{
  public function sleep() {/*...*/}
  public function eat() {/*...*/}
}

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

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

W końcu i mężczyźni i kobiety śpią i jedzą w ten sam sposób, przynajmniej w większości :) Jeżeli przyjrzymy się tym klasom bliżej, to możemy dojść do wniosku, że metody doMenStuff() i doFemaleStuff() są logicznie podobne, obie odpowiadają za wykonywanie czynności, jednak ich działanie jest zależne od konkretnej klasy. W takim wypadku można by się pokusić o jeszcze jedno przeprojektowanie kodu:
<?php
abstract class Human
{
  public function sleep() {/*...*/}
  public function eat() {/*...*/}
  abstract public function doStuff();
}

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

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

Teraz na pierwszy rzut oka widzimy, że każda klasa dziedzicząca po Human musi zaimplementować ciało metody doStuff(). Oczywiście klasy mogą posiadać jeszcze atrybuty wspólne i rozłączne (pomijam wszelkiego rodzaju gettery i settery):
<?php
abstract class Human
{
  private $_age;
  public function sleep() {/*...*/}
  public function eat() {/*...*/}
  abstract public function doStuff();
}

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

class Woman extends Human
{
  private $_pregnanciesNumber;
  public function doStuff() {/*...*/}
}


Mamy teraz ładną hierarchię klas, spójną i logiczną. Na podstawie klasy abstrakcyjnej widzimy, że każda klasa pochodna musi posiadać zaimplementowaną metodę doStuff() oraz, że posiada kilka dodatkowych metod, które dziedziczy po klasie bazowej.
Załóżmy jednak, że przyszło nam do głowy dodać do całego kodu klasę Bed:
<?php
class Bed
{
  public function use($user)
  {
    $user->sleep();
  }
}

Oczywiście zakładamy, że z łóżka mogą korzystać tylko obiekty, na których jesteśmy w stanie wykonać metodę sleep(). W takim wypadku możemy kod klasy Bed zmienić na:
<?php
class Bed
{
  public function use(Human $user)
  {
    $user->sleep();
  }
}

Już lepiej, ale czy tak naprawdę parametr powinien być typu Human? Nam przecież zależy, żeby można było na tym obiekcie wykonać metodę sleep(), a w Human jest ich trochę więcej. I tutaj z pomocą przychodzą interfejsy, które powinny być używane wszędzie tam, gdzie należy zadeklarować metody niezbędne dla logiki działania. Oczywiście można do tego celu użyć również klasy abstrakcyjnej z tym, że ma to sens tylko, gdy wszystkie metody publiczne klasy abstrakcyjnej są wykorzystywane w danym miejscu aplikacji, czyli nasza klasa Human musiałaby wyglądać tak:
<?php
abstract class Human
{
  public function sleep() {/*...*/}
}

W innym wypadku (jeżeli zostaniemy przy aktualnym projekcie klasy Human z trzema metodami, w tym jedną abstrakcyjną) programista, który również chciałby napisać klasę korzystającą z obiektów klasy Bed uważałby, że do działania metody use() mogą być niezbędne również metody eat() i doStuff() klasy Human, co oczywiście byłoby błędne. Aby nie doprowadzić do takich nieporozumień stosuje się interfejsy. I tak nasz kod po całej tej analizie wygląda tak:
<?php
interface AbleToSleep
{
  public function sleep();
}

abstract class Human implements AbleToSleep
{
  private $_age;
  public function sleep() {/*...*/}
  public function eat() {/*...*/}
  abstract public function doStuff();
}

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

class Woman extends Human
{
  private $_pregnanciesNumber;
  public function doStuff() {/*...*/}
}

class Bed
{
  public function use(AbleToSleep $user)
  {
    $user->sleep();
  }
}

Teraz już na pierwszy rzut oka widać, że obiekty klasy Human mogą korzystać z instancji klasy Bed oraz jakie metody muszą implementować przyszłe klasy, aby również z nich korzystać.