1. モード定義
仕様モードは、組み合わせモードの拡張と考えることができます。プロジェクト内の特定の条件によってビジネス ロジックが決定される場合がありますが、これらの条件を抽出して特定の関係 (AND、OR、NOT) で組み合わせることで、ビジネス ロジックを柔軟にカスタマイズできます。さらに、クエリやフィルタリングなどのアプリケーションでは、論理的な判断文を使用する代わりに、複数の条件を事前に定義し、それらの条件の組み合わせを使用してクエリやフィルタリングを処理することにより、実装ロジック全体を簡素化できます。
ここでの各条件は仕様であり、複数の仕様/条件が直列に接続されて、特定の論理関係を持つ組み合わせ仕様を形成します。
2. UML クラス図
3. サンプルコード
item.php
1 | <?phpnamespace DesignPatterns\Behavioral\Specification; class Item{ protected $price ; public function __construct( $price ) { $this ->price = $price ; } public function getPrice() { return $this ->price; }}
|
ログイン後にコピー
SpecificInterface.php
1 | <?phpnamespace DesignPatterns\Behavioral\Specification; interface SpecificationInterface{ public function isSatisfiedBy(Item $item ); public function plus(SpecificationInterface $spec ); public function either(SpecificationInterface $spec ); public function not();}
|
ログイン後にコピー
AbstractSpecific.php
1 | <?phpnamespace DesignPatterns\Behavioral\Specification; abstract class AbstractSpecification implements SpecificationInterface{ abstract public function isSatisfiedBy(Item $item ); public function plus(SpecificationInterface $spec ) { return new Plus( $this , $spec ); } public function either(SpecificationInterface $spec ) { return new Either( $this , $spec ); } public function not() { return new Not( $this ); }}
|
ログイン後にコピー
Plus.php
1 | <?phpnamespace DesignPatterns\Behavioral\Specification; class Plus extends AbstractSpecification{ protected $left ; protected $right ; public function __construct(SpecificationInterface $left , SpecificationInterface $right ) { $this ->left = $left ; $this ->right = $right ; } public function isSatisfiedBy(Item $item ) { return $this ->left->isSatisfiedBy( $item ) && $this ->right->isSatisfiedBy( $item ); }}
|
ログイン後にコピー
Each.php
1 | <?phpnamespace DesignPatterns\Behavioral\Specification; class Either extends AbstractSpecification{ protected $left ; protected $right ; public function __construct(SpecificationInterface $left , SpecificationInterface $right ) { $this ->left = $left ; $this ->right = $right ; } public function isSatisfiedBy(Item $item ) { return $this ->left->isSatisfiedBy( $item ) || $this ->right->isSatisfiedBy( $item ); }}
|
ログイン後にコピー
Not.php
1 | <?phpnamespace DesignPatterns\Behavioral\Specification; class Not extends AbstractSpecification{ protected $spec ; public function __construct(SpecificationInterface $spec ) { $this ->spec = $spec ; } public function isSatisfiedBy(Item $item ) { return ! $this ->spec->isSatisfiedBy( $item ); }}
|
ログイン後にコピー
PriceSpecific.php
1 | <?phpnamespace DesignPatterns\Behavioral\Specification; class PriceSpecification extends AbstractSpecification{ protected $maxPrice ; protected $minPrice ; public function setMaxPrice( $maxPrice ) { $this ->maxPrice = $maxPrice ; } public function setMinPrice( $minPrice ) { $this ->minPrice = $minPrice ; } public function isSatisfiedBy(Item $item ) { if (! empty ( $this ->maxPrice) && $item ->getPrice() > $this ->maxPrice) { return false; } if (! empty ( $this ->minPrice) && $item ->getPrice() < $this ->minPrice) { return false; } return true; }}
|
ログイン後にコピー
4. テストコード
テスト/仕様Test.php
1 | <?phpnamespace DesignPatterns\Behavioral\Specification\Tests; use DesignPatterns\Behavioral\Specification\PriceSpecification; use DesignPatterns\Behavioral\Specification\Item; class SpecificationTest extends \PHPUnit_Framework_TestCase{ public function testSimpleSpecification() { $item = new Item(100); $spec = new PriceSpecification(); $this ->assertTrue( $spec ->isSatisfiedBy( $item )); $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 )); } public function testNotSpecification() { $item = new Item(100); $spec = new PriceSpecification(); $not = $spec ->not(); $this ->assertFalse( $not ->isSatisfiedBy( $item )); $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 )); } public function testPlusSpecification() { $spec1 = new PriceSpecification(); $spec2 = new PriceSpecification(); $plus = $spec1 ->plus( $spec2 ); $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 )); }}
|
ログイン後にコピー