规则模式 目的 规则模式,通过制定一份明确的业务规则来检查对象是否满足业务要求。提供规则的类有一个名为 isSatisfiedBy 的方法,该方法会检查指定对象是否满足规则,返回相应的 true 或 false。
使用场景
UML 类图
代码 Item.php 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <?php declare (strict_types = 1 );namespace DesignPatterns \Extend \Specification ;class Item { private float $price ; public function __construct (float $price ) { $this ->price = $price ; } public function getPrice ( ): float { return $this ->price; } }
Specification.php 1 2 3 4 5 6 7 8 <?php declare (strict_types = 1 );namespace DesignPatterns \Extend \Specification ;interface Specification { public function isSatisfiedBy (Item $item ): bool ; }
OrSpecification.php 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 <?php declare (strict_types = 1 );namespace DesignPatterns \Extend \Specification ;class OrSpecification implements Specification { private array $specifications ; public function __construct (Specification ...$specifications ) { $this ->specifications = $specifications ; } public function isSatisfiedBy (Item $item ): bool { foreach ($this ->specifications as $specification ) { if ($specification ->isSatisfiedBy ($item )) { return true ; } } return false ; } }
PriceSpecification.php 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 <?php declare (strict_types = 1 );namespace DesignPatterns \Extend \Specification ;class PriceSpecification implements Specification { private ?float $maxPrice ; private ?float $minPrice ; public function __construct (?float $minPrice , ?float $maxPrice ) { $this ->minPrice = $minPrice ; $this ->maxPrice = $maxPrice ; } public function isSatisfiedBy (Item $item ): bool { if ($this ->maxPrice !== null && $item ->getPrice () > $this ->maxPrice) { return false ; } if ($this ->minPrice !== null && $item ->getPrice () < $this ->minPrice) { return false ; } return true ; } }
AndSpecification.php 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 <?php declare (strict_types = 1 );namespace DesignPatterns \Extend \Specification ;class AndSpecification implements Specification { private array $specifications ; public function __construct (Specification ...$specifications ) { $this ->specifications = $specifications ; } public function isSatisfiedBy (Item $item ): bool { foreach ($this ->specifications as $specification ) { if (!$specification ->isSatisfiedBy ($item )) { return false ; } } return true ; } }
NotSpecification.php 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <?php declare (strict_types = 1 );namespace DesignPatterns \Extend \Specification ;class NotSpecification implements Specification { private Specification $specification ; public function __construct (Specification $specification ) { $this ->specification = $specification ; } public function isSatisfiedBy (Item $item ): bool { return !$this ->specification->isSatisfiedBy ($item ); } }
测试 Tests/SpecificationTest.php 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 <?php declare (strict_types = 1 );namespace DesignPatterns \Extend \Specification \Tests ;use DesignPatterns \Extend \Specification \Item ;use DesignPatterns \Extend \Specification \NotSpecification ;use DesignPatterns \Extend \Specification \OrSpecification ;use DesignPatterns \Extend \Specification \AndSpecification ;use DesignPatterns \Extend \Specification \PriceSpecification ;use PHPUnit \Framework \TestCase ;class SpecificationTest extends TestCase { public function testCanOr ( ) { $spec1 = new PriceSpecification (50 , 99 ); $spec2 = new PriceSpecification (101 , 200 ); $orSpec = new OrSpecification ($spec1 , $spec2 ); $this ->assertFalse ($orSpec ->isSatisfiedBy (new Item (100 ))); $this ->assertTrue ($orSpec ->isSatisfiedBy (new Item (51 ))); $this ->assertTrue ($orSpec ->isSatisfiedBy (new Item (150 ))); } public function testCanAnd ( ) { $spec1 = new PriceSpecification (50 , 100 ); $spec2 = new PriceSpecification (80 , 200 ); $andSpec = new AndSpecification ($spec1 , $spec2 ); $this ->assertFalse ($andSpec ->isSatisfiedBy (new Item (150 ))); $this ->assertFalse ($andSpec ->isSatisfiedBy (new Item (1 ))); $this ->assertFalse ($andSpec ->isSatisfiedBy (new Item (51 ))); $this ->assertTrue ($andSpec ->isSatisfiedBy (new Item (100 ))); } public function testCanNot ( ) { $spec1 = new PriceSpecification (50 , 100 ); $notSpec = new NotSpecification ($spec1 ); $this ->assertTrue ($notSpec ->isSatisfiedBy (new Item (150 ))); $this ->assertFalse ($notSpec ->isSatisfiedBy (new Item (50 ))); } }