Jednym z najbardziej dla mnie niekomfortowych żeczy w PHPe jest brak możliwości przeciążania czy to funkcji czy też metod w kontekście OO. Jak wiadomo związane jest to z brakiem m.in. ścisłej kontroli typów. No cóż rzeczywistość jest jaka jest i trzeba do niej się dostosować. Najczęściej pojawiającym się zabiegiem w celu implementacji „przeciążania” metod jest użycie magicznych metod. Sposób takiej implementacji można podejrzeć w wpisie „Method Overloading in PHP5” na blogu Caught in a Web. Osobiście nie przepadam za tego rodzaju rozwiązaniami. Tak więc w wolnym czasie zacząłem główkować i kombinować jak to można by był zrobić troszkę inaczej. Z przemyśleń narodził się pomysł aby do „przeciążania” (pseudo przeciążania) wykorzystać namespace (opis dostępny w manualu PHP oraz na stronie z poradnikami IBMa „Create better namespaces in PHP”). Pomysł bazuje na prostych zasadach:
– wykorzystaniu bazowej przestrzeni nazw dla metod podstawowych które będą przeciążane, zaimplementowane lub nadpisane w klasach potomnych. Najlepiej wykorzystać abstrakcje,
– wykorzystaniu predefiniowanych przestrzeni nazw do enkapsulacji przeciążanych klas i ich metod,
– zamknięcie przestrzeni bazowej i predefiniowanej w jednym pliku (daje to swego rodzaju konwencję utrzymania kodu i jego standaryzację w obrębie realizowanego projektu). Ewentualnie można odpowiednio sobie porozbijać klasy na pliki. Opisu tego rozwiązania jednak nie dokonałem i jedynie skupiłem się na podstawowej strukturze – jeden plik.
Przykładową implementację całości rozwiązania bazującego na ww. zasadach przedstawiłem poniżej. Miłego czytania i zapoznawanie się z pomysłem.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 | <!--?php // Converter.php /** * Bazowa (podstawowa) przestrzeń nazw * * @author Marcin M. * */ namespace BaseNamespace { /** * Abstrakcyjna klasa konwertera * * @author Marcin M. * */ abstract class Converter { abstract public function convert( $arg1 ); } } /** * Przestrzeń nazw dla konwertera `String` * * @author Marcin M. * */ namespace StringNamespace { use BaseNamespace; class Converter extends BaseNamespace\Converter { public function convert( $arg1 ) { return (string) $arg1 ; } } } /** * Przestrzeń nazw dla konwertera `Float` * * @author Marcin M. * */ namespace FloatNamespace { use BaseNamespace; class Converter extends BaseNamespace\Converter { public function convert( $arg1 ) { return (float) $arg1 ; } } } /** * Przestrzeń nazw aplikacji * * @author Marcin M. * */ namespace { use StringNamespace as String; use FloatNamespace as Float; $stringConverter = new String\Converter(); $floatConverter = new Float\Converter(); echo 'Konwersja na string ' . $stringConverter --->convert( '123' ); echo "\n" ; echo 'Konwersja na float ' . $floatConverter ->convert( '123.12' ); } ?> |
Oczywiście przedstawiona metoda w żaden sposób nie zastępuje klasycznego przeciążania i wywoływania z obiektu metody w zależności od kontekstu (specyfikacji jej deskryptora). Niemniej stanowi pewne obejście za pomocą którego można zdefiniować jawnie o jaki typ konwersji nam chodzi. Użytkownik (programista) musi sam jawnie w kodzie zadeklarować, w tym przypadku, na jaki typ ma się odbyć konwersja. W pewien sposób poprawia to czytelność kodu i nie musimy dochodzić do tego co gdzie i jak zostało prze-konwertowane dynamicznie przez kompilator/interpreter w przypadku klasycznego przeciążania. Z drugiej strony pociąga to dodatkowy narzut na projektowanie elastycznej i jasnej konwencji przestrzeni nazw. Tak więc jak to w programowaniu i nie tylko w nim bywa, są ciemne jak i jasne strony stosowania pewnych rozwiązań. Zachęcam do eksperymentów i powodzenia.