20 07 2010

Tworzenie biblioteki Underscore

W niniejszym artykule opiszę co będzie składać się na bibliotekę (bibliotekę a nie frameworka!). Przedstawię założenia, projekt budowy, oraz poszczególne rozwiązania. Dokładne opisy klas składających się na bibliotekę znajdą się w późniejszych artykułach.

kategoria: Tworzymy bibliotekę autor: Michał Juszczak

Artykuł podzielony na kilka części tematycznych. Zachęcam do dopisywania komentarzy, może część będzie na tyle konstruktywna, że uwzględnię je w projekcie. Aktualnie biblioteka jest w fazie ukończenia wersji 0.6. Biblioteka jest tworzona, poprawiana i wykorzystywana w innych projektach od kilku lat. Aktualnie postanowiłem przebudować i zmniejszyć objętościowo cały rdzeń biblioteki.

Budowa - szybkość ma znaczenie

Część osób wie, że: funkcje są szybsze od klas oraz metody statyczne są szybsze od metod wywołanych z obiektów. Różnice te są praktycznie niezauważalne przy pojedynczym wywołaniu. Jednakże przy dużym natężeniu ruchu na stronie możemy stracić/zyskać wiele zasobów sprzętowych w zależności od budowy aplikacji. Najlepszym rozwiązaniem pod względem szybkości jest stworzenie biblioteki funkcji, jednakże wówczas nie jesteśmy w stanie ukryć części rozwiązań. Metody klas posiadają parametr private lub protected, dzięki temu ukrywamy to, czego nie chcemy by programista używał. Tak więc obieramy kierunek na funkcje w klasach.

Wybór pada na klasy które posiadają metody statyczne. Dlaczego? Moja odpowiedź brzmi: Po co tworzyć obiekt by przetworzyć string i skrócić go do x wyrazów. Nie tylko uzyskamy na szybkości wywołania, co również na objętości kodu. Użycie w kilku/kilkunastu miejscach:


$string = transform::wordCount($string, 10);

Jest krótsze o 31 znaków niż poniższe:


$transform = new transform();
$string = $transform->wordCount($string, 10);

I wydaje mi się że kod jest czytelniejszy.

Decyzja podjęta – wszystko klasy będą miały metody statyczne (w końcu będą tylko zbiorem funkcji), poza klasą bazy danych, ale wyjaśnienie opiszę później.

Budowa - główne założenia

Funkcje o podobnym działaniu będą pogrupowane w odpowiednie klasy – tak więc, zmiany ciągu znaków będą się znajdować w klasie np. Format; sprawdzanie poprawności danych w klasie Validation; działania na szablonach w Template; itp. itd.
Klasy jak w najmniejszym stopniu zależne od siebie – i nie chodzi wyłącznie o możliwość ustalania w parametrach powiązań dopisywania klas pomocniczych, ale o całkowitą enkapsulację. Klasa ma być niezależna, ma nie odwoływać się do innych klas, wszystko zawarte w jednym. Niestety, nie da się tego rozwiązać w 100%. Powstaną min.2 klasy, które będą wymagane przez inne. Będą to klasy Exception, czyli przechwytywanie wyjątków wraz z logowaniem błędów oraz Core(Base) zawierającą metody wspólne dla wszystkich klas, np. sprawdzanie istnienia pliku.
Każda klasa powinna mieć możliwość zmiany ustawień – najlepiej żeby można było je przekazać jako metodę zmiany ustawień, oraz możliwość przechowywania ustawień w pliku.
Jak najmniejsze pliki – tego nie muszę tłumaczyć.
Wszystko ładnie upakowane w paczki – możliwość tworzenia i obsługi archiwów PHP - PHAR. Ułatwi to tworzenie aktualizacji, uporządkuje miejsce na dysku. Łatwiej wgrać pojedynczy pliki klient.phar zawierający pliki php, html, xml; niż kilka plików do odpowiednich katalogów.
Przejrzysta struktura katalogów – Trzy podstawowe katalogi: biblioteka, dane publiczne (pobierane bezpośrednio przez przeglądarkę pliki css, js, grafika), oraz dane strony. Ostatni katalog dzielący się na kilka: konfiguracja, baza danych, pliki html/php, cache, logi. Struktura wyglądająca następująco:


        ├───    _               (biblioteka)
        ├───    public          (dane js, css itp)
        └───    private         (dane strony)
                ├───    cache   (cache)
                ├───    conf    (konfiguracja)
                ├───    db      (baza danych – sqLite)
                ├───    data    (pliki php/html – przetwarzane)
                └───    logs    (logi)
    

Podział na klasy

Poniżej, bez zbędnego opisywania przedstawiam proponowany (i wstępny podział na klasy), a także strukturę katalogów. Jedyną nie statyczną klasą jest klasa bazy danych, natomiast częściowo statyczną (logi i błędy) klasa przechwytywania wyjątków.
Docelowo każdy katalog będzie paczką PHAR, którą będzie łatwiej aktualizować.


        ├───    _               (core)
        │       ├───    _.php   (podstawowe funkcje)
        │       ├───    _C.php  (kontroler)
        │       ├───    _D.php  (obsługa bazy danych)
        │       ├───    _E.php  (wyjątki i błędy)
        │       ├───    _F.php  (formatowanie tekstu)
        │       ├───    _Q.php  (generowanie zapytań SQL)
        │       ├───    _R.php  (generowanie RSS)
        │       ├───    _S.php  (sesje)
        │       ├───    _T.php  (szablony)
        │       ├───    _U.php  (utf8)
        │       └───    _V.php  (walidacja)
        │
        └───    _H              (helpery)
                ├───    _H_C    (captcha)
                ├───    _H_H    (helpery ogólne)
                ├───    _H_L    (generator Lorem)
                ├───    _H_I    (instalacja i konfiguracja)
                ├───    _H_P    (zarządzanie użytkownikami)
                ├───    _H_R    (wyrażenia regularne)
                └───    _H_T    (testowanie klas - unit test)
    

To jest wstępna budowa. Główny katalog (core) prawdopodobnie się nie zmieni. Jedynie nad czym się zastanawiam to wycięcie _Q.php_R.php i przeniesienie do Helperów jako klas nie będących tak często w używaniu. Brakuje mi również jakiegoś pliku bootstrap.php konfigurującego wstępnie bibliotekę. No i helpery, nie wiem czy nie rozbić ich na mniejsze.

Aktualnie biblioteka (w fazie produkcji) zajmuje około 400kiB, po usunięciu komentarzy, wcięć itp pozostaje około 120kiB kodu. Plik PHAR zajmuje ok 60kiB. Tak więc pracuję i skracam dalej.

Dodaj komentarz:

Komentarze podlegają moderacji, nie dopuszczam komentarzy spamujących, z wyzwiskami, wulgaryzmami, oszczerstwami. Rozumiem przez to również nie akceptowanie komentarzy, których treść jest prawnie zabroniona. Pozostałe komentarze, nawet takie które będą sprzeczne z moimi poglądami są akceptowane. Należy czekać na akceptację, każdy komentarz zostanie sprawdzony przeze mnie a następnie ukaże się na stronie.

Komentarze:

1
2010-10-26 by Zyx (grrrr, mój nick jest trzyliterowy!)

Nie wiem, że metody statyczne są szybsze i nie wiem, skąd bierzesz takie rewelacje, bo odkąd pamiętam, zawsze było na odwrót. Nawet przed chwilą zrobiłem szybki benchmark i nic się nie zmieniło w tym względzie. Ponadto z tego, co piszesz (zwłaszcza o krótkości kodu), wydaje mi się, że nie rozumiesz sensu programowania obiektowego. Robienie metod typu wordCount() to głupota do kwadratu - przecież po to się robi obiekty, by te metody pracowały w ich kontekście i wykonywały coś na nich, a nie działały tak sobie w próżni. O wiele lepsze wyniki wydajnościowe osiągniesz dobrymi algorytmami i odpowiednią architekturą, a nie robieniem sobie pseudoobiektowego śmietnika. Ponadto co z takimi kwestiami, jak niezawodność i elastyczność?

Ostatnie wiadomości:

Ostatnie komentarze:

  • (2010-10-26) Nie wiem, że metody statyczne są szybsze i nie wiem, skąd bierzesz tak...
    NEWS Tworzenie biblioteki Underscore: Nie wiem, że metody statyczne są szybsze i nie wiem, skąd bierzesz takie rewelacje, bo odkąd pamiętam, zawsze było na odwrót. Nawet przed chwilą zrobiłem szybki benchmark i nic się nie zmieniło w tym względzie. Ponadto z tego, co piszesz (zwłaszcza o krótkości kodu), wydaje mi się, że nie rozumiesz sensu programowania obiektowego. Robienie metod typu wordCount() to głupota do kwadratu - przecież po to się robi obiekty, by te metody pracowały w ich kontekście i wykonywały coś na nich, a nie działały tak sobie w próżni. O wiele lepsze wyniki wydajnościowe osiągniesz dobrymi algorytmami i odpowiednią architekturą, a nie robieniem sobie pseudoobiektowego śmietnika. Ponadto co z takimi kwestiami, jak niezawodność i elastyczność? Zyx (grrrr, mój nick jest trzyliterowy!)
  • (2010-10-26) Niestety, takie rozwiązanie jest wybitnie niepraktyczne. Już to przera...
    NEWS Konfiguracja klas i obiektów - metoda settings: Niestety, takie rozwiązanie jest wybitnie niepraktyczne. Już to przerabiałem spory kawałek czasu temu przy okazji tworzenia Open Power Libs 2.0 i do dziś odbija mi się to czkawką. Po pierwsze, używasz do tego elementów statycznych, czyli już na wejściu utrudniasz sobie testowanie i wprowadzasz elementy niedeterminizmu, które mogą być źródłem podobnych problemów, jakie powoduje "global". Po drugie, Twoja funkcja do ładowania konfiguracji też jest niepraktyczna, bowiem wymaga uprzedniego załadowania wszystkich możliwych klas nawet, jeśli ich nie używamy, gdyż inaczej oberwiemy wyjątkiem. Dobrym, sprawdzonym rozwiązaniem jest idea wstrzykiwania zależności. PS. Masz u mnie minusa za nałożenie limitu min. 5 znaków na długość nicka, który jest idiotyczny, jako że jest całkiem sporo osób, które mają czteroliterowe nicki, a trzyliterowe takie, jak mój, też się trafiają. Zyx (grrrr, mój nick jest trzyliterowa!!!)

Kategorie:

Tworzenie stron internetowych Tworzymy bibliotekę Zarządzenie przedsiębiorstwem Zarządzanie jakością Blog