diff --git a/Behavioral/Specification/AbstractSpecification.php b/Behavioral/Specification/AbstractSpecification.php deleted file mode 100644 index 66d61b8..0000000 --- a/Behavioral/Specification/AbstractSpecification.php +++ /dev/null @@ -1,52 +0,0 @@ -specifications = $specifications; + } + + public function isSatisfiedBy(Item $item): bool + { + $satisfied = []; + + foreach ($this->specifications as $specification) { + $satisfied[] = $specification->isSatisfiedBy($item); + } + + return !in_array(false, $satisfied); + } +} diff --git a/Behavioral/Specification/Either.php b/Behavioral/Specification/Either.php deleted file mode 100644 index a30372a..0000000 --- a/Behavioral/Specification/Either.php +++ /dev/null @@ -1,36 +0,0 @@ -left = $left; - $this->right = $right; - } - - /** - * Returns the evaluation of both wrapped specifications as a logical OR. - * - * @param Item $item - * - * @return bool - */ - public function isSatisfiedBy(Item $item) - { - return $this->left->isSatisfiedBy($item) || $this->right->isSatisfiedBy($item); - } -} diff --git a/Behavioral/Specification/Item.php b/Behavioral/Specification/Item.php index 8d639e0..a167f0a 100644 --- a/Behavioral/Specification/Item.php +++ b/Behavioral/Specification/Item.php @@ -2,29 +2,19 @@ namespace DesignPatterns\Behavioral\Specification; -/** - * An trivial item. - */ class Item { - protected $price; - /** - * An item must have a price. - * - * @param int $price + * @var float */ - public function __construct($price) + private $price; + + public function __construct(float $price) { $this->price = $price; } - /** - * Get the items price. - * - * @return int - */ - public function getPrice() + public function getPrice(): float { return $this->price; } diff --git a/Behavioral/Specification/Not.php b/Behavioral/Specification/Not.php deleted file mode 100644 index e99a6cc..0000000 --- a/Behavioral/Specification/Not.php +++ /dev/null @@ -1,33 +0,0 @@ -spec = $spec; - } - - /** - * Returns the negated result of the wrapped specification. - * - * @param Item $item - * - * @return bool - */ - public function isSatisfiedBy(Item $item) - { - return !$this->spec->isSatisfiedBy($item); - } -} diff --git a/Behavioral/Specification/NotSpecification.php b/Behavioral/Specification/NotSpecification.php new file mode 100644 index 0000000..db47b51 --- /dev/null +++ b/Behavioral/Specification/NotSpecification.php @@ -0,0 +1,21 @@ +specification = $specification; + } + + public function isSatisfiedBy(Item $item): bool + { + return !$this->specification->isSatisfiedBy($item); + } +} diff --git a/Behavioral/Specification/OrSpecification.php b/Behavioral/Specification/OrSpecification.php new file mode 100644 index 0000000..62a4e08 --- /dev/null +++ b/Behavioral/Specification/OrSpecification.php @@ -0,0 +1,30 @@ +specifications = $specifications; + } + + public function isSatisfiedBy(Item $item): bool + { + $satisfied = []; + + foreach ($this->specifications as $specification) { + $satisfied[] = $specification->isSatisfiedBy($item); + } + + return in_array(true, $satisfied); + } +} diff --git a/Behavioral/Specification/Plus.php b/Behavioral/Specification/Plus.php deleted file mode 100644 index 26bd585..0000000 --- a/Behavioral/Specification/Plus.php +++ /dev/null @@ -1,36 +0,0 @@ -left = $left; - $this->right = $right; - } - - /** - * Checks if the composite AND of specifications passes. - * - * @param Item $item - * - * @return bool - */ - public function isSatisfiedBy(Item $item) - { - return $this->left->isSatisfiedBy($item) && $this->right->isSatisfiedBy($item); - } -} diff --git a/Behavioral/Specification/PriceSpecification.php b/Behavioral/Specification/PriceSpecification.php index 2019ad2..3b54586 100644 --- a/Behavioral/Specification/PriceSpecification.php +++ b/Behavioral/Specification/PriceSpecification.php @@ -2,47 +2,35 @@ namespace DesignPatterns\Behavioral\Specification; -/** - * A specification to check an Item is priced between min and max. - */ -class PriceSpecification extends AbstractSpecification +class PriceSpecification implements SpecificationInterface { - protected $maxPrice; - protected $minPrice; + /** + * @var float|null + */ + private $maxPrice; /** - * Sets the optional maximum price. - * - * @param int $maxPrice + * @var float|null */ - public function setMaxPrice($maxPrice) + private $minPrice; + + /** + * @param float $minPrice + * @param float $maxPrice + */ + public function __construct($minPrice, $maxPrice) { + $this->minPrice = $minPrice; $this->maxPrice = $maxPrice; } - /** - * Sets the optional minimum price. - * - * @param int $minPrice - */ - public function setMinPrice($minPrice) + public function isSatisfiedBy(Item $item): bool { - $this->minPrice = $minPrice; - } - - /** - * Checks if Item price falls between bounds. - * - * @param Item $item - * - * @return bool - */ - public function isSatisfiedBy(Item $item) - { - if (!empty($this->maxPrice) && $item->getPrice() > $this->maxPrice) { + if ($this->maxPrice !== null && $item->getPrice() > $this->maxPrice) { return false; } - if (!empty($this->minPrice) && $item->getPrice() < $this->minPrice) { + + if ($this->minPrice !== null && $item->getPrice() < $this->minPrice) { return false; } diff --git a/Behavioral/Specification/README.rst b/Behavioral/Specification/README.rst index b6957e0..84d6913 100644 --- a/Behavioral/Specification/README.rst +++ b/Behavioral/Specification/README.rst @@ -38,15 +38,9 @@ SpecificationInterface.php :language: php :linenos: -AbstractSpecification.php +OrSpecification.php -.. literalinclude:: AbstractSpecification.php - :language: php - :linenos: - -Either.php - -.. literalinclude:: Either.php +.. literalinclude:: OrSpecification.php :language: php :linenos: @@ -56,15 +50,15 @@ PriceSpecification.php :language: php :linenos: -Plus.php +AndSpecification.php -.. literalinclude:: Plus.php +.. literalinclude:: AndSpecification.php :language: php :linenos: -Not.php +NotSpecification.php -.. literalinclude:: Not.php +.. literalinclude:: NotSpecification.php :language: php :linenos: diff --git a/Behavioral/Specification/SpecificationInterface.php b/Behavioral/Specification/SpecificationInterface.php index 796af9f..7387700 100644 --- a/Behavioral/Specification/SpecificationInterface.php +++ b/Behavioral/Specification/SpecificationInterface.php @@ -2,36 +2,7 @@ namespace DesignPatterns\Behavioral\Specification; -/** - * An interface for a specification. - */ interface SpecificationInterface { - /** - * A boolean evaluation indicating if the object meets the specification. - * - * @param Item $item - * - * @return bool - */ - public function isSatisfiedBy(Item $item); - - /** - * Creates a logical AND specification. - * - * @param SpecificationInterface $spec - */ - public function plus(SpecificationInterface $spec); - - /** - * Creates a logical OR specification. - * - * @param SpecificationInterface $spec - */ - public function either(SpecificationInterface $spec); - - /** - * Creates a logical not specification. - */ - public function not(); + public function isSatisfiedBy(Item $item): bool; } diff --git a/Behavioral/Specification/Tests/SpecificationTest.php b/Behavioral/Specification/Tests/SpecificationTest.php index 5abc82a..d7676a4 100644 --- a/Behavioral/Specification/Tests/SpecificationTest.php +++ b/Behavioral/Specification/Tests/SpecificationTest.php @@ -3,101 +3,44 @@ namespace DesignPatterns\Behavioral\Specification\Tests; use DesignPatterns\Behavioral\Specification\Item; +use DesignPatterns\Behavioral\Specification\NotSpecification; +use DesignPatterns\Behavioral\Specification\OrSpecification; +use DesignPatterns\Behavioral\Specification\AndSpecification; use DesignPatterns\Behavioral\Specification\PriceSpecification; -/** - * SpecificationTest tests the specification pattern. - */ class SpecificationTest extends \PHPUnit_Framework_TestCase { - public function testSimpleSpecification() + public function testCanOr() { - $item = new Item(100); - $spec = new PriceSpecification(); + $spec1 = new PriceSpecification(50, 99); + $spec2 = new PriceSpecification(101, 200); - $this->assertTrue($spec->isSatisfiedBy($item)); + $orSpec = new OrSpecification($spec1, $spec2); - $spec->setMaxPrice(50); - $this->assertFalse($spec->isSatisfiedBy($item)); - - $spec->setMaxPrice(150); - $this->assertTrue($spec->isSatisfiedBy($item)); - - $spec->setMinPrice(101); - $this->assertFalse($spec->isSatisfiedBy($item)); - - $spec->setMinPrice(100); - $this->assertTrue($spec->isSatisfiedBy($item)); + $this->assertFalse($orSpec->isSatisfiedBy(new Item(100))); + $this->assertTrue($orSpec->isSatisfiedBy(new Item(51))); + $this->assertTrue($orSpec->isSatisfiedBy(new Item(150))); } - public function testNotSpecification() + public function testCanAnd() { - $item = new Item(100); - $spec = new PriceSpecification(); - $not = $spec->not(); + $spec1 = new PriceSpecification(50, 100); + $spec2 = new PriceSpecification(80, 200); - $this->assertFalse($not->isSatisfiedBy($item)); + $orSpec = new AndSpecification($spec1, $spec2); - $spec->setMaxPrice(50); - $this->assertTrue($not->isSatisfiedBy($item)); - - $spec->setMaxPrice(150); - $this->assertFalse($not->isSatisfiedBy($item)); - - $spec->setMinPrice(101); - $this->assertTrue($not->isSatisfiedBy($item)); - - $spec->setMinPrice(100); - $this->assertFalse($not->isSatisfiedBy($item)); + $this->assertFalse($orSpec->isSatisfiedBy(new Item(150))); + $this->assertFalse($orSpec->isSatisfiedBy(new Item(1))); + $this->assertFalse($orSpec->isSatisfiedBy(new Item(51))); + $this->assertTrue($orSpec->isSatisfiedBy(new Item(100))); } - public function testPlusSpecification() + public function testCanNot() { - $spec1 = new PriceSpecification(); - $spec2 = new PriceSpecification(); - $plus = $spec1->plus($spec2); + $spec1 = new PriceSpecification(50, 100); + $orSpec = new NotSpecification($spec1); - $item = new Item(100); - - $this->assertTrue($plus->isSatisfiedBy($item)); - - $spec1->setMaxPrice(150); - $spec2->setMinPrice(50); - $this->assertTrue($plus->isSatisfiedBy($item)); - - $spec1->setMaxPrice(150); - $spec2->setMinPrice(101); - $this->assertFalse($plus->isSatisfiedBy($item)); - - $spec1->setMaxPrice(99); - $spec2->setMinPrice(50); - $this->assertFalse($plus->isSatisfiedBy($item)); - } - - public function testEitherSpecification() - { - $spec1 = new PriceSpecification(); - $spec2 = new PriceSpecification(); - $either = $spec1->either($spec2); - - $item = new Item(100); - - $this->assertTrue($either->isSatisfiedBy($item)); - - $spec1->setMaxPrice(150); - $spec2->setMaxPrice(150); - $this->assertTrue($either->isSatisfiedBy($item)); - - $spec1->setMaxPrice(150); - $spec2->setMaxPrice(0); - $this->assertTrue($either->isSatisfiedBy($item)); - - $spec1->setMaxPrice(0); - $spec2->setMaxPrice(150); - $this->assertTrue($either->isSatisfiedBy($item)); - - $spec1->setMaxPrice(99); - $spec2->setMaxPrice(99); - $this->assertFalse($either->isSatisfiedBy($item)); + $this->assertTrue($orSpec->isSatisfiedBy(new Item(150))); + $this->assertFalse($orSpec->isSatisfiedBy(new Item(50))); } }