Pokazywanie postów oznaczonych etykietą PHP. Pokaż wszystkie posty
Pokazywanie postów oznaczonych etykietą PHP. Pokaż wszystkie posty

piątek, 20 września 2013

Enumy w PHPie? Oczywiście, że się da!

Ostatnio, przeglądając jedno z reviewew mój kolega z zespołu rozpoczął rozmowę na temat naszych pseudo-enumów w aplikacji.
Nie odkryliśmy tutaj koła na nowo i nasze "klasy enumeracyjne" wyglądały tak, jak w większości frameworków, bibliotek i aplikacji, które widziałem, a które zostały napisane w PHP:
interface PseudoEnum
{

    const SUCCESS = 1;
    const FAILURE = 2;
}

Dobre rozwiązanie? No cóż, osobiście od dawna bolałem nad tym, że takie konstrukcje nie pozwalają nam na jedną z jakże istotnych funkcjonalności, które w przypadku zwykłych obiektów PHP posiada, a które są bezproblemowe w językach, gdzie coś takiego jak enum istnieje. O czym mowa? Chodzi o typowanie.

Pomimo cudownych i deskryptywnych nazw, te stałe, jakby na to nie patrzyć, w gruncie rzeczy są int'ami i w chwili, gdy któraś metoda mogła przyjąć wartość tylko i wyłącznie taką, jaka została zadeklarowana wcześniej w naszym pseudo-enumie, to musieliśmy tworzyć do tego celu specjalne metody sprawdzające.
Nie mogę powiedzieć, że było to zadowalające rozwiązanie, ale jakoś z tym żyliśmy.

poniedziałek, 29 października 2012

różne typy zwracane?

ach ten pehape...

Jedyną rzeczą, której naprawdę nie mogę przeboleć w PHP, to fakt, że nie posiada on typowania. Oczywiście można "umówić się" z innymi programistami w zespole, że "typujemy" tzn. w komentarzach określamy typ przyjmowany i zwracany i się tego trzymamy, ale jest to jedynie obejście problemu, a nie jego rozwiązanie.
Dobra, niestety świat nie jest idealny i musimy jakoś z tym żyć. W dodatku, jeżeli kod jest rzeczywiście obiektowy, to w większości przypadków typowania rzeczywiście można używać na tyle często, że zapomina się o problemie.

Jednak tylko do czasu...

piątek, 10 sierpnia 2012

Konstruktor w interfejsie i klasie abstrakcyjnej?

wstęp

Abstrahując od tego, na co pozwala nam dany język programowania (np. PHP pozwala niestety na wszystko), to czy w interfejsie jest sens posiadać deklarację konstruktora? A co w przypadku klas abstrakcyjnych? Jak sądzicie, taka funkcjonalność może się przydać, czy raczej jest to coś zbędnego, czego najprawdopodobniej nigdy byście nie wykorzystali?

piątek, 20 lipca 2012

Które metody umieścić w interfejsie?

krótki wstęp, a w nim o tym, czym jest interfejs

Do czego wykorzystuje się interfejsy? Do zapewnienia, że obiekty danej klasy będą posiadały określone metody. Po co chcemy mieć tą pewność? Ponieważ w danym miejscu ich posiadanie jest niezbędne do bezbłędnego wykonania pewnych instrukcji. Dlaczego wszystkie metody interfejsu są abstrakcyjne? Gdyż nie wymagamy określonego ich działania, wymagamy jedynie ich istnienia.

wtorek, 17 lipca 2012

ten pieprzony init()

ten pieprzony init

Zend w wersji 1.x obfitował w klasy, które posiadały deklarację pustej metody init(), która była wywoływana w konstruktorze.

Do czego jest ona wykorzystywana? Twórcy Zenda doszli do wniosku, że jeżeli chcesz np. stworzyć odpowiednio skonfigurowany formularz (np. do dodawania produktów), to idealnym rozwiązaniem będzie rozszerzenie klasy Zend_Form i umieszczenie całej kofiguracji owego formularza w nadpisanej metodzie init(), która wykona się przy tworzeniu nowego obiektu.

Sprytne, no nie? I jeszcze w dodatku można pokusić się o stwierdzenie, że jest to implementacja wzorca template method.

Chciałbym jednak zasmucić wszystkich tych, którzy praktykują takie rozwiązanie. Ani to sprytne nie jest, a użycie wzorca jest niepoprawne i niepotrzebne.

środa, 23 maja 2012

Jak programować obiektowo cz. 9 - klasy abstrakcyjne

klasa abstrakcyjna

Czym jest klasa abstrakcyjna? Nie będę się rozpisywał na temat konstrukcji (jak zwykle:), ponieważ w internecie można znaleźć sporo definicji.
We wstępie chciałbym się natomiast skupić na podziale klas abstrakcyjnych. Co prawda, z tego co się orientuję, żadnego oficjalnego podziału nie ma, ale po jakimś czasie programowania można wyróżnić trzy powtarzające się wzorce (nie w znaczeniu wzorców projektowych) klas abstrakcyjnych:

piątek, 13 kwietnia 2012

Jak programować obiektowo cz. 8 - interfejsy

kilka słów na początek

O interfejsach pisałem już kilka razy (np. tu i tu) i pewnie jeszcze nie raz napiszę, ponieważ są one (wraz z klasami abstrakcyjnymi) jednymi z najistotniejszych elementów projektowania obiektowego. Kiedy już je poznasz, zrozumiesz i się z nimi zaprzyjaźnisz, tak naprawdę dopiero w tym momencie odkrywasz piękno pisania obiektowego, jego elastyczność.

Nie poruszę w tym wpisie wszystkich aspektów dotyczących interfejsów, nawet nie będę się starał, chodzi mi jedynie o podstawowe (i moim zdaniem najważniejsze) cechy interfejsów.

poniedziałek, 12 marca 2012

Jak programować obiektowo cz. 7 - const

na wstępie słów kilka

Znowu dłuższa przerwa w publikowaniu czegokolwiek. Na szczęście dobiegła końca i mam nadzieję, że w ciągu najbliższych kilku-kilkunastu tygodni uda mi się dokończyć wszystkie zaległe wpisy, na które pomysły dojrzewały, lecz z braku czasu nie mogły się doczekać realizacji:(
O czym dzisiaj? O stałych. A czym są te stałe? Kiedy ich używać, a kiedy nie? O tym dalej:)

poniedziałek, 23 stycznia 2012

Jak programować obiektowo cz. 6 - static

wstęp

Część z Was zapewne zauważyła, że w poprzednich wpisach pominąłem kilka istotnych rzeczy dotyczących atrybutów klas, ich metod, czy też samych klas. W kolejnych wpisach postaram się nadrobić tą zaległość.

Na pierwszy ogień idzie słowo kluczowe static. Z tym, że pominę kwestię używania tego słowa jako referencji do klasy, o czym pisałem tutaj.
Co oznacza użycie statycznej metody bądź atrybutu? Jest to równoznaczne z tym, że do jej/jego używania nie potrzebujemy tworzyć instancji danej klasy.

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.

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:)

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ć.