Przejdź do treści

Domknięcia oraz funkcje anonimowe w przykładach część 2

Domknięcia oraz funkcje anonimowe w przykładach …

A więc czas i na część 2, w której to dalej przyjrzymy się przykładowym zastosowaniom domknięć, w tej części głównie skupimy się na drugim typie callbacków, w poprzedniej części nazwałem je tworzącymi jakiś większy 'zamysł’. Jeśli nie czytałeś poprzedniej części to zapraszam do zapoznania się z nią przed przystąpieniem do poniższego wpisu. Dodatkowo jeśli nie bardzo orientujesz się w domknięciach oraz funkcjach anonimowych to zapraszam do wpisu rozpoczynającego serię. Oto i linki: Wpis rozpoczynający serię o funkcjach anonimowych oraz Domknięcia oraz funkcje anonimowe w przykładach część 1

Skoro wstęp mamy już za sobą, przejdźmy teraz do pierwszego zagadnienia tej części czyli do

Lazy Loading

Do czasu gdy w PHP nie pojawiły się domknięcia, zagadnienia odnośnie lazy loadingu(implementacja oraz rozważania na jego temat) były rozwiązywane wszelkimi dostępnymi obejściami niedoskonałości języka. Na szczęście te czasy minęły i miejmy nadzieję, że już bezpowrotnie. Obecnie możemy się cieszyć z tak potężnego narzędzia jakim niewątpliwie jest lazy loading.

Może zanim przyjrzymy się przykładowi, w skrócie opowiem o idei mechanizmu. A więc, lazy loading jest to przygotowanie obiektu najwcześniej jak to tylko możliwe ale jego użycie (a co za tym idzie, powołanie obiektu do 'życia’) następuje tylko w momencie gdy tak naprawdę go potrzebujemy użyć. Ale po co ? (ktoś może zapytać), no a więc po to (śpieszę z odpowiedzią) aby zminimalizować zużycie zasobów sprzętowych oraz przyśpieszyć naszą aplikację. Bo po co ładować X obiektów skoro do przetworzenia danego żądania wystarczą nam np. 2 (gdzie X > 2).

$lazy = function() {
   $przykladowyObiekt = new \SplFixedArray(1000);
   return $przykladowyObiekt;
};

W powyższym przykładzie opakowujemy tworzenie obiekty SplFixedArray w mechanizm lazy loadingu, teraz gdy będziemy potrzebować tego obiektu, wystarczy odwołać się do domknięcia $lazy(). Bardziej życiowym przykładem jest przygotowywanie obiektów w kontenerze zależności (DI Container) lub w jakimkolwiek mechanizmie utrzymującym stan naszej aplikacji lub obiektów.

Modelowanie Encji

Wyobraźmy sobie, że nie możemy lub nie chcemy w naszym następnym projekcie użyć żadnego ORM’a oraz chcemy mieć ładną czystą nieedytowalną encje, przez nieedytowalną rozumiem bez encje bez setterów co uniemożliwi nam zmianę jej stanu, natomiast reszta systemu może w jakimś stopniu oddziaływać na nią. Ok więc mamy jakiś kawałek kodu który wyciąga nam dane z bazy i zwraca nam je w postaci tablicowej np.

$dane = [ 'name' => 'Jan',
  'age'  => 21
];

Teraz dla przykładu chcielibyśmy aby nasza encja wyglądała mniej więcej tak

class Person {
    private $name = '';
    private $age = 0;

    public function getName()
    { 
        return $this->name;
    }

    public function getAge()
    {
        return $this->age();
    }
}

Dodatkowo encja ta będzie tworzona przez wyspecjalizowaną do tego celu fabrykę, która utworzy ją na podstawie przekazanej tablicy elementów z bazy danych. Początkowy stan encji oczywiście można by ustawić poprzez konstruktor, natomiast dla tego przykładu nie robimy tego, ponieważ w trakcie działania naszej aplikacji stan tej encji może się zmienić lecz nie poprzez nasze działanie ale poprzez jakąś 'ewolucje’ obiektu. Metoda w naszej fabryce może wyglądać mniej więcej tak

public function newEntity($data)
{
    $entity = new Person();
    foreach($data as $key => $value) {
        (function() use ($key, $value) {
            $this->{$key} = $value;
        })->call($entity);
    }
return $entity;
}

Natomiast funkcja modyfikująca encje będzie wyglądać bardzo podobnie

public function modifyEntity($data, $entity)
{
    foreach($data as $key => $value) {
        (function() use ($key, $value) {
            $this->{$key} = $value;
        })->call($entity);
    }
return $entity;
}

Powyższe metody potrafią przypisać odpowiednią wartość do wskazanej własności w klasie. Oczywiście wprawny programista od razu zawoła ejj hejj ! refaktoryzuj z tych dwóch metod zrób trzy, przez co zlikwidujemy dublet kodu… Oczywiście ma racje, natomiast jest to tylko przykład i refaktoryzacja nie jest tematem tego wpisu 😉

Powyższy przykład jest tak naprawdę bardzo prostym rozwinięciem tego tematu, rozwiązań oraz możliwości jest tak dużo, że postanowiłem w jak najprostszy sposób ukazać te zagadnienie.

Prototypowanie Obiektu

Pewnie nie raz miałeś do czynienia z JavaScriptem, więc mechanizm prototypowania nie jest Ci obcy, a gdyby tak spróbować przenieść ten mechanizm do PHP(chociaż spróbować go za symulować), niewątpliwie funkcje anonimowe oraz wcześniej poznana metoda call na pewno nam w tym pomogą.
Na początek stwórzmy sobie podstawową klasę

class Object {
  protected $methods = [];
  
  public function prototype($name, $fn)
  {
    $this->methods[$name] = $fn;
    return $this;
  }
  
  public function __call($name, $args=null)
  {
    return $this->methods[$name]->call($this, $args);
  }
  
  public function __invoke()
  {
    return $this;
  }
}

Będzie ona naszym rodzicem dla każdego kolejnego bytu, teraz wystarczy tylko utworzyć pierwszą prototypową funkcję.

$obj = new Object();
$class = $obj()->prototype('extend', function() {return clone $this;});

OK a więc nasz obiekt obj uzyskał nową 'metodę’ o nazwie extend, cały obiekt zaś przypisaliśmy do zmiennej class gdyż to ona będzie punktem wyjścia dla kolejnych bytów. W taki sposób definiujemy sobie prototypy naszych klas aby później utworzyć z nich już końcowe obiekty.

$helloWorldClass = $class->extend()->prototype('sayHello', function($args){ echo "Hello {$args[0]}"; });

$helloWorldClass->sayHello('world');

Powyższy przykład jest prostą reprezentacją końcowego obiektu na podstawię wcześniej zdefiniowanego prototypu class.
Oczywiście jest to bardzo prosty przykład, ale na jego podstawie można bez żadnego problemu tworzyć konkretne prototypy obiektów. Do uzupełnienia tego przykładu dodam, że potrzebowalibyśmy zaimplementować jeszcze metodę new najlepiej za pomocą prototypu tuż przed metodą extend.

Na zakończenie ciekawostka

Skoro funkcje anonimowe są tak naprawdę obiektem to dlaczego możemy ich używać jak zwykłych funkcji ? Już śpieszę z odpowiedzią, wszystko za sprawą metody magicznej __invoke która w locie dodaje do danej klasy typ callable. Co więcej metodę __invoke możemy dodać także do swoich klas, dzięki czemu będziemy w stanie używać ich jak zwykłej funkcji, musimy pamiętać aby odpowiednio zaprogramować zachowanie takiego obiektu. Nie wierzysz ? sprawdź sam, czy przykładowa klasa bez jak i z metodą __invoke jest typu callable czy też nie. Przykład takiej klasy, został zaprezentowany w tym wpisie a dokładnie w przydał się przy prototypowaniu.

Jak widać zastosowanie domknięć jest bardzo szerokie, praktycznie na każdym kroku można spotkać ich użycie lub możliwość zastosowania. Wszystko zależy tylko i wyłącznie od sytuacji oraz argumentów przyświecających za użyciem domknięcia lub też funkcji anonimowej.

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *