Specifications modelini açıklamak istersek, şöyle olur; true veya false döndüren bir mantık. Kompozit specification classı, true veya false değerleri döndüren isSatisfiedBy adında standart bir methoda sahiptir. Bu, tarafımızdan belirlenen kriterleri karşılayıp karşılamadığını görmek için verilen nesneyi kontrol ederek yapılır.


Senaryo


Evinizi satıyorsunuz ve kullanıcının evinizi satın alabilmesi için bazı kriterleri yerine getirmesi gerekir. Kullanıcı:



Örnek


User class


class User
{
private $age;
private $amountOwned;
private $isAlive;

public function __construct(int $age, float $amountOwned, bool $isAlive)
{
$this->age = $age;
$this->amountOwned = $amountOwned;
$this->isAlive = $isAlive;
}

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

public function getAmountOwned(): float
{
return $this->amountOwned;
}

public function getIsAlive(): bool
{
return $this->isAlive;
}
}

Klasik bir işlevsel programlama tekniği kullanıyor olsaydık, kodumuz aşağıdaki gibi olurdu.


$saleAmount = 500000.00;

$user = new User(29, 700000.00, true);

if (
true === $user->getIsAlive() &&
$user->getAge() >= 18 &&
$user->getAmountOwned() >= $saleAmount
) {
echo 'Can buy'.PHP_EOL;
} else {
echo 'Cannot buy'.PHP_EOL;
}

// Result "Can buy"

Uygulamamızın birçok yerinde bu kod parçasını kullandığımızı varsayalım. Kriterler bir gün değişirse, değişiklikleri yansıtırken potansiyel olarak zaman kaybetme, hata yapma gibi bazı sorunlar ortaya çıkabilir. Ayrıca zaten hoş görünmüyor ancak daha fazla kriter eklemek zorunda kalırsak, daha çirkin bir hal alacağıda malum.


Aşağıdaki örnekte, bir kerede hem "sabit kodlanmış specifications" hem de "parametreli hale getirilmiş specifications" yöntemlerini uygulayacağız (yapmak zorunda değilsiniz). "Sabit kodlu specifications", gerçek specifications classına geçirilen herhangi bir argümana ihtiyaç duymaz - örneğin, UserStillAliveSpecification ve UserIsAdultSpecification. "Parametreli hale getirilmiş specifications", gerçek specifications classına geçirilen argüman veya argümanlara ihtiyaç duyar - örneğin, UserHasEnoughMoneySpecification.


interface SpecificationInterface
{
public function isSatisfiedBy(User $user): bool;
}

class UserStillAliveSpecification implements SpecificationInterface
{
public function isSatisfiedBy(User $user): bool
{
return $user->getIsAlive();
}
}

class UserIsAdultSpecification implements SpecificationInterface
{
private const MINIMUM_LEGAL_AGE_LIMIT = 18;

public function isSatisfiedBy(User $user): bool
{
return $user->getAge() >= self::MINIMUM_LEGAL_AGE_LIMIT;
}
}

class UserHasEnoughMoneySpecification implements SpecificationInterface
{
private $saleAmount;

public function __construct(float $saleAmount)
{
$this->saleAmount = $saleAmount;
}

public function isSatisfiedBy(User $user): bool
{
return $user->getAmountOwned() >= $this->saleAmount;
}
}

class House
{
private $specifications;

public function add(SpecificationInterface $specification)
{
$this->specifications[] = $specification;

return $this;
}

public function canBeSold(User $user): bool
{
/** @var SpecificationInterface $specification */
foreach ($this->specifications as $specification) {
if (!$specification->isSatisfiedBy($user)) {
return false;
}
}

return true;
}
}

$saleAmount = 500000.00;

$user = new User(29, 700000.00, true);

$house = (new House())
->add(new UserStillAliveSpecification())
->add(new UserIsAdultSpecification())
->add(new UserHasEnoughMoneySpecification($saleAmount));

$result = $house->canBeSold($user);

echo true === $result ? 'Can Buy' : 'Cannot buy';