bentools / specification
提供 Specification Pattern 的实现。PHP7+
Requires
- php: >=7.1
Requires (Dev)
- phpunit/phpunit: @stable
- satooshi/php-coveralls: @stable
- squizlabs/php_codesniffer: @stable
- symfony/var-dumper: ^3.3
README
bentools/specification
PHP7.1+ 对 Specification Pattern 的实现。
目标
Specification Pattern 允许你将一些条件结构封装成值对象。这在你平时的代码中可能没有意义,但当你的业务规则组合达到较高的圈复杂度时,它可能很有用。
因此,将你的条件封装到具有特定错误处理的命名对象中,可以帮助你
- 向现有规则中添加新业务规则
- 更好地理解你的条件结构
- 找出哪些条件使其他条件无效
- 避免难以阅读的“千层面”代码
Specification Pattern 的原则借鉴了领域驱动设计,但可以在任何地方应用。
概述
规范是一种增强的条件结构,可以与其他规范链接。
以下是创建规范的方法
require_once __DIR__ . '/vendor/autoload.php'; use function BenTools\Specification\spec; $color = 'green'; $spec = spec('green' === $color); $spec->validate(); // Hurray! Our specification has been validated.
如你所见,使用 validate()
方法来验证规范是否满足。它返回 void
(无)。
当一个规范未满足时,validate()
方法会抛出 UnmetSpecificationException
use function BenTools\Specification\spec; $color = 'green'; $size = 'small'; $spec = spec('green' === $color)->and('big' === $size); $spec->validate(); // Oh no! An UnmetSpecificationException has been thrown.
处理这些异常时,你可以知道哪些规范失败了。为了识别它们,你可以给每个规范命名
use BenTools\Specification\Exception\UnmetSpecificationException; use function BenTools\Specification\spec; $color = 'green'; $size = 'small'; $spec = spec('green' === $color)->withLabel('Color specification') ->and('big' === $size)->withLabel('Size specification'); try { $spec->validate(); } catch (UnmetSpecificationException $e) { foreach ($e->getUnmetSpecifications() as $unmetSpecification) { if (null !== $unmetSpecification->getLabel()) { printf('%s failed.' . PHP_EOL, $unmetSpecification->getLabel()); } } } // Outputs: Size specification failed.
规范快速检查
你不必处理 try/catch 块,可以通过调用 $spec->isSatisfied()
来检查规范是否满足,这将为你处理这些并返回一个布尔值。
use BenTools\Specification\Exception\UnmetSpecificationException; use function BenTools\Specification\spec; $color = 'green'; $size = 'small'; $spec = spec('green' === $color)->withLabel('Color specification') ->and('big' === $size)->withLabel('Size specification'); var_dump($spec->isSatisfied()); // (bool) false
创建规范
spec()
、group()
和 not()
函数以及 and()
和 or()
方法是规范工厂,它们将返回一个 Specification
对象。它们接受以下参数
- 一个布尔值
- 一个将解析为布尔值的可调用对象
- 一个现有的
Specification 对象
示例
use function BenTools\Specification\spec; use function BenTools\Specification\group; use function BenTools\Specification\not; spec(true); // Specification met not(false); // Specification met not(spec(function () { return false; })); // Specification met group(not(spec(false))->or(true)); // Specification met
分组和链式规范
一个 Specification
对象包含 and
和 or
方法,可以用来创建复合规范。你也可以使用 group()
函数,它将像括号一样工作
use function BenTools\Specification\spec; use function BenTools\Specification\group; use function BenTools\Specification\not; spec(true) ->and( group( spec(true)->or(false) ) ->or( not(false) ) ); // Specification met
创建你自己的规范
由于 Specification Pattern 的目的是测试你的业务规则,因此最好实现你自己的 Specification
类。
use BenTools\Specification\Exception\UnmetSpecificationException; use BenTools\Specification\Specification; class SpecProductInStock extends Specification { /** * @var Product */ private $product; /** * SpecProductInStock constructor. * @param Product $product */ public function __construct(Product $product) { $this->product = $product; $this->label = sprintf('Product %s in stock verification', $product->getName()); } /** * Validate the specification. * If the specification is unmet the implementation MUST throw an UnmetSpecificationException. * * @throws UnmetSpecificationException */ public function validate(): void { if (false === $this->product->isInStock()) { throw new UnmetSpecificationException($this); } } }
高级示例
由于 Specification Pattern 的目的是测试你的业务规则,因此你应该实现自己的 Specification
类。
查看我们的示例以开始。
安装
composer require bentools/specification ^3.0
许可证
MIT