Closures are a poor man’s object

Има се предвид, че клоужърите и ламбдите имайки свой скоуп на променливите, могат да извършват т.н. „data hiding“, тоест да „скриват“ и капсулират информацията от околният свят в себе си.

Aко погледнем следният пример, ще видим че даденият клоужър bloa() може да има свои, частни променливи, които не са достъпни отвън.

<?php
$patapan = function(string $s): string {
    return strtoupper($s);
};

$bloa = function($a) use ($patapan): string {
    $prom1 = 'Super!';
    return $patapan($a);
};

$strings = ["apple", "orange", "banana", "coconut"];
$upprs = array_map($bloa, $strings);

var_dump($upprs);  // Dumps the uppercased array
var_dump($prom1);  // Undefined variable $prom1 in…
echo $patapan('Good question');  // GOOD QUESTION

Какво излиза на практика? Че closures и lambdas си приличат с обектите, по това, че предлагат свой скоуп и правят data hiding. Moже би затова се казва, че са „the poor man objects“.

Coercion polymorphism

Нека първо си припомним какво е Implicit type casting в PHP.

<?php
declare(strict_types = 1);

$x = '25';
var_dump($x);       // string(2) "25"
var_dump($x * 2);   // int(50)

Виждаме как PHP сам е извършил type cast към integer за да изпълни аритметичната операция.

Explicit type casting е зададен програмно от нас, когато сами кастваме променлива към друг тип.

При Coercion polymorphism можем да използваме и двата вида type casting, като например с помоща на вторият вид (explicit), може например да кастваме връщаният резултат.

PHP поддържа Coercion polymorphism само ако зададем declare(strict_types = 0); за да разрешим на PHP функциите да приемат аргументи от различен от зададеният тип.

<?php

class SomeClass
{
    private int $a;

    public function setA(int $a): void
    {
        $this->a = $a;
        var_dump($this->a);
    }
}

$p = new SomeClass;
$p->setA(123);      // int(123)
$p->setA(123.45);   // int(123)

Ясно се вижда идеята на тoзи вид полиморфизъм – PHP сам, имплицитно преобразува подаденият аргумент и по този начин можем да използваме въпросният метод както с целочислени, така и с дробни аргументи.

Parametric polymorphism

Логиката на функцията трябва да се задава с това, какви типове аргументи и подаваме, а не да я override-ваме или overload-ваме (второто е друг тип полиморфизъм – Ad hoc).

За разлика от Ad hoc полиморфизмът, където имаме същата функция, пренаписана (overloaded) повече пъти, според това какъв тип аргументи и подаваме.

Примерно, ако не задаваш типа на входните параметри, както до скоро беше по принцип в PHP, можеш да използваш една и съща функция за например събиране на числа или конкатениране на стрингове…
Това е особено лесно например в JavaScript, където операторът „+“ служи и за двете.
Но това не е са добри примери за Parametric Polymorphism, защото изискването е да се спазва т.н. type safety.

… parametric polymorphism is a way to make a language more expressive, while still maintaining full static type-safety

Идеята на Parametric Polymorphism НЕ Е да имаме пълна либералност на това, какво подаваме като тип на параметрите, а по-скоро да разширим областта от възможни типове но така, че логиката вътре във функцията да е една и съща без значение какъв конкретно тип параметри подаваме. Под „да разширим“ се има предвид, да имаме т.н. ковариантност на входните типове.

Toест, не да можем да подаваме каквото си искаме, това би било т.н. Ad hoc plymorphism, което си е едно към едно overloading.
Following Christopher Strachey, parametric polymorphism may be contrasted with ad hoc polymorphism, in which a single polymorphic function can have a number of distinct and potentially heterogeneous implementations depending on the type of argument(s) to which it is applied. 

Защото това например НЕ Е Parametric Polymorphism:

function add($a, $b)
{
    if ((gettype($a) === 'string') && (gettype($b) === 'string')) {
        return $a . $b;
    }
    if ((gettype($a) === 'integer') && (gettype($b) === 'integer')) {
        return $a + $b;
    }
}

Нито пък това, което би било Ad hoc и го давам като неработещ пример, защото както знаем в PHP оверлоудване не се поддържа:

function add(int $a, int $b): int
{
    return $a + $b;
}

function add(string $a, string $b): string
{
    return $a . $b;
}

За PHP специално, добър пример би бил използването на SOLID принципът Dependency Inversion, при който изискваме абстракция, а не конкретна функционалност за тип на входен параметър. Тоест, подаваме на една функция различни обекти но от един и същ интерфейс или абстрактен клас, и тези подадени обекти съответно правейки различни неща, определят поведението на функцията.

interface CacheableInterface
{
    public function getCacheKey(): string;
}

class ProductClass implements CacheableInterface
{
    public function getCacheKey(string $id): string
    {
        return 'product_' . $id;
    }
}

class CategoryClass implements CacheableInterface
{
    public function getCacheKey(string $id): string
    {
        return 'category_' . $id;
    }
}

function store(CacheableInterface $cacheable): string
{
    return $cacheable->getCacheKey('ABC-120-3G');
}

Вижда се как на store() може да подаваме различни обекти, стига да са от интерфейс CacheableInterface и съответно, тези отделни обекти имат свое виждане за това, което извеждат. От там идва и различното поведение на store()

Друго, което можем да забележим, че Parametric Polymorphism доста прилича на Subtype Polymorphism, защото горният пример би бил добър и за Subtype Polymorphism ако имахме и йерархия от класове.

Добър въпрос би бил, каква е разликата между Parametric Plymorphism и Subtype Polymorphism?

При Subtype Plymorphism трябва да имаме йерархия на типовете, например на класовете или на примитивните типове (float, int…).

При Parametric Polymorphism – не. Както се вижда от горният пример, няма йерархия между взаимозаменяемите ProductClass и CategoryClass, между тях самите, нищо че имплементират общ интерфейс.

Затова Parametric Polymorphism е по-общият случай на Subtype Polymorphism.

Литература:

https://sergeyzhuk.me/2017/04/09/oop-polymorphism/

https://en.wikipedia.org/wiki/Parametric_polymorphism

Inclusion plymorphism

Същият като Subtyping Polymorphism – ако имаме клас А, който бива наследен от клас B, то обекти от клас B могат да бъдат използвани вместо такива от клас А.

Както и, можем вместо float да използваме int.

Става въпрос за типовете на подаваните аргументи.

Multiton design pattern /Registry of Singletons/

Този Design Pattern е така да се каже разширение на Singleton Design Pattern и също е известна като „Registry of Singletons“.

В смисъл такъв, че Singleton гарантира само един обект от даденият клас, а Multiton – повече от един обект от СЪЩИЯТ клас, но съдържащи се в хеш таблица или масив на друг обект.
…the multiton pattern instead ensures a single instance per key.

Представете си го като асоциативен масив с много съставки от дадена кулинарна рецепта, но от съставка можем да имаме само по една:

static private $ingredients = null;

...

self::$ingredients['tomato'] = Ingredients.getInstance('tomato');
self::$ingredients['pepper'] = Ingredients.getInstance('pepper');
self::$ingredients['cheese'] = Ingredients.getInstance('cheese');

...

и разбира се за всяка съставка, трябва първо да проверяваме дали вече не е добавена, тоест, дали вече имаме стойност в масива $shopskaSalad за даденият ключ.

...

if (self::$ingredients['tomato'] === null)
    self::$ingredients['tomato'] = Ingredients.getInstance('tomato');

if (self::$ingredients['pepper'] === null)
    self::$ingredients['pepper'] = Ingredients.getInstance('pepper');

if (self::$ingredients['cheese'] === null)
    self::$ingredients['cheese'] = Ingredients.getInstance('cheese');

...

As the name suggests, a multiton is a design pattern that helps you create multiple instances of itself. Both singleton and multiton are same, the only difference is that a multiton can store and retrieve multiple instances of itself. Obviously multiton suffers from same problems as singletons.

Here is example of multiton:

class Logger
{
    private static $instances = array();

    public static function getInstance(string $key): self
    {
        if (!array_key_exists($key, self::$instances)) {
            self::$instances[$key] = new self();
        }
        return self::$instances[$key];
    }

    // prevent creating multiple instances due to "private" constructor
    private function __construct(){}

    // prevent the instance from being cloned
    private function __clone(){}

    // prevent from being unserialized
    public function __wakeup(){}
}

$firstInstance = Logger::getInstance('file');
$secondInstance = Logger::getInstance('email');

Литература:

https://en.wikipedia.org/wiki/Multiton_pattern

https://www.codeproject.com/Articles/1178694/Singleton-and-Multiton-Pattern

https://codeinphp.github.io/post/singleton-and-multiton-design-patterns/

Lazy loading

There are four common ways of implementing the lazy load design pattern:

  1. Lazy initialization (synonym for Lazy instantiation)
  2. Virtual proxy
  3. Ghost
  4. Value holder

1. Lazy initialization

Чак когато инстанция на даден обект ни потрябва, тогава го инстанцираме.

2. Virtual proxy

Virtual Proxy is an object with the same interface as the real object. The first time one of its methods are called it loads the real object and then delegates.

Създаваме 2 обекта, от 2 отделни класа, имплементиращи 1 общ интерфейс.
Когато извикаме някой метод от ПЪРВИЯ обект, той създава обект от ВТОРИЯ клас, присвоява го на пропърти от ПЪРВИЯТ, и ОТ ТУК НАТАТЪК каквото искаме да достъпим от ВТОРИЯ, викаме („минаваме“) през ПЪРВИЯ.

A virtual proxy object shares an interface with the „real“ object. The first time a property of the virtual proxy is accessed, the real object gets initialized. From that point on, accessing properties of the virtual object returns the corresponding property of the real object.

Явно идеята е ако оригиналният обект е ресурсоемък или времеемък, но трябва да го инициализираме по една или друга причина, да не го инициализираме него, а някакъв друг, виртуален, който има същият интерфейс, и чак когато ни трябва оригиналът, тогава във виртуалният създаваме оригиналният, и с делегиране го използваме (delegation).

3. Ghost

A „ghost“ is the object that is to be loaded in a partial state. It may only contain the object’s identifier, but it loads its own data the first time one of its properties is accessed. For example, consider that a user is about to request content via an online form. At the time of creation all we know is that content will be accessed but what action or content is unknown.

ghost is the real object without any data. The first time you call a method the ghost loads the full data into its fields.

Или с други думи, създаваме обект но съвсем опростен с идеята в последвие да добавяме към него повече данни. Добър пример би бил, ако първо създадем някакъв „полупразен“ обект, в който ще съхраняваме и обработваме информация от събмитната HTML форма, но го „пълним“ след събмитване.

$userData = array(
    "UID" = > uniqid(),
    "requestTime" => microtime(true),
    "dataType" => "",
    "request" => ""
);

if (isset($ _POST['data']) && $userData) {
    // …
}

4. Value holder

Basically, а value holder is a generic object that handles the lazy loading behavior and appears in place of the object’s data fields. When the user needs to access it, they simply ask the value holder for its value by calling the GetValue method. At that time (and only then), the value gets loaded from a database or from a service (this is not always needed).

Най просто, идеята е когато имаме нужда от дадено пропърти на даден обект, и това е свързано с ресурсоемка или времеемка операция (извличане от БД например), това извличане да стане чак тогава, през даден гетър например, в който например може да има допълнителна логика, която да запази тази информация в статично пропърти, за да не се извлича всеки път.

public function getValue($parameter) {
    if (self::$value == null) {
        self::$value = valueRetrieval($parameter);
    }
    return self::$value;
}

Литература:

https://en.wikipedia.org/wiki/Lazy_loading

https://en.wikipedia.org/wiki/Lazy_initialization#PHP

https://www.geeksforgeeks.org/lazy-loading-design-pattern/