Autor wpisu: sokzzuka, dodany: 22.06.2010 00:29, tagi: php
Do napisania tego postu zainspirował mnie artykuł na 97 rzeczy pt. Stosuj zasady programowania funkcjonalnego. Każdemu polecam przeczytanie go aby dowiedział się dlaczego należy stosować te zasady. Ja w tym poście będę chciał pokrótce przedstawić jak rady zawarte w artykule można odnieść do kodu w PHP i jakie korzyści odniesiemy stosując refaktoring poszczególnych rodzajów kodu. Duchem całego programowania funkcjonalnego jest przezroczystość referencyjna – funkcja dla tych samych danych zwraca konsekwentnie ten sam wynik, aby tak było kod musi spełniać kilka zasad:
Pierwsza zasadą jest unikanie globalnego stanu. Często można natknąć się w skryptach na następujące konstrukcje „popełniane” przez początkujących:
$conn = mysql_connect(); function fetch_users(){ global $conn; /** reszta kodu **/ }
Problem z tego rodzaju kodem jest taki, że jest on całkowicie nieprzewidywalny. Jeżeli zamkniemy gdzieś w naszym skrypcie zamkniemy połączenie do bazy, a potem wywołamy funkcję fetch_users, po prostu dostaniemy błąd. Oprócz tego, osoba postronna czytająca kod, w zasadzie nie jest w stanie wywnioskować co powinno znaleźć się w zmiennej $conn.
Są dwa rozwiązania refaktoringu takiego kodu, pierwszy:
$conn = mysql_connect(); function fetch_users($connection){ /** kod */ }
Drugi:
class DbService { protected $_conn; public function __construct(){ $this->_conn = mysql_connect(); } public function fetch_users(){ /** kod **/ } public function __destruct(){ mysql_close($this->_conn); } }
Dzięki refaktoringowi nr.1 zyskaliśmy większą przewidywalność funkcji i czytelność. Dzięki refaktoringowi nr.2 uzyskaliśmy przewidywalność, abstrakcje i enkapsulacje.
Przykładem pogwałcenia tej zasady jest również wzorzec singleton (namiętnie używany w ZF) i rejestr.
class FooController extends Zend_Controller_Action { function barAction(){ $baz = Zend_Registry->getInstance()->baz; // czym jest baz ??? } }
Jak widzimy powyżej, gdybyśmy recenzowali taki kod, nie jesteśmy bez uruchamiania skryptu w stanie stwierdzić, jakiego typu jest zmienna $baz, co więcej nie jesteśmy w stanie stwierdzić czy rejestr przechowuje coś pod tą zmienną. Refaktoring:
class FooController extends Zend_Controller_Action { function barAction(ModelUser $baz){ $aList = $baz->getList(); // baz jest instancją ModelUser, wiemy jakie ma metody i musi istnieć żeby ta metoda mogła być //wywołana } }
Oczywiście osobną kwestią jest jak wstrzyknąć zależności do kontrolera, ja bym zastosował do tego kontener IOC, ale to osobna bajka.
Drugą zasadą jest nieprzekazywanie przez referencję do funkcji: