到现在为止,你应该很清楚的明白规范模式就如一个接口。使用PHP5明确的表示出这个规范模式:
interface Specification { public function isSatisfiedBy($datasource); } |
我们把这些基本的模块按照一定的格式集中起来,这甚至是非常有用的。因为从一个规范类的方法isSatisfiedBy()中返回的结果是一个布尔值(boolean),把这些布尔值运用到具体不同的规范类的效果都是相当好的。
为了实现逻辑与,我们创建一个类,把两个具体的规范实例类组合起来,当给出的数据源参数同时满足给定的条件后就返回true。
class AndSpecification implements Specification { protected $spec; protected $andSpec; public function __construct($spec, $andSpec) { $this->spec = $spec; $this->andSpec = $andSpec; function isSatisfiedBy($datasource) { return ($this->spec->isSatisfiedBy($datasource) && $this->andSpec->isSatisfiedBy($datasource)); } |
你也可以类似的实现逻辑或。
class OrSpecification implements Specification { protected $spec; protected $orSpec; public function __construct($spec, $orSpec) { $this->spec = $spec; $this->orSpec = $orSpec; function isSatisfiedBy($datasource) { return ($this->spec->isSatisfiedBy($datasource) || $this->orSpec->isSatisfiedBy($datasource)); } |
利用已经给出的“逻辑”规范和最初的一些规规范集合,你可以实现一些复杂的验证。
class PolicyFactory { public function createJasonPolicy() { $name_jason = new FieldEqualSpecification(‘name’, ‘Jason’); $age_at_least_thirty = new FieldGreaterThanOrEqualSpecification(‘age’, 30); $male = new FieldEqualSpecification(‘sex’, ‘male’); $jasons_email = new OrSpecification( new FieldEqualSpecification(‘email’, ‘jsweat_php@yahoo.com’) , new FieldEqualSpecification(‘email’, ‘jsweat@users.sourceforge.net’)); return new AndSpecification( $name_jason, new AndSpecification( $age_at_least_thirty, new AndSpecification($male, $jasons_email) )); } } |
一开始,策略工厂看起来有点零乱,主要是因为临时变量的数量阻止了单个具体规范的实例化。然而,代码中最有趣的部分是使用或规范(OrSpecification)和与规范(AndSpecification)类(在上面高亮显示了)。对email进行FieldEqualSpecification字段等价规范的两个实例化都被当作参数传递到或规范(OrSpecification)的构造方法中。因为或规范(OrSpecification)执行了规范接口,这个$jasons_email对象可以象任何其他具体的规范实例一样处理。事实上,在4行后它在又按照同样的方式被使用,具体在new AndSpecification($male, $jasons_email)。
使用上面给出的方法器PolicyFactor(上面的PolicyFactory),我们可以做到:
$jason = PolicyFactory::createJasonPolicy(); $jason->isSatisfiedBy($datasource); |
这两行代码用于验证数据源$datasource的name字段的值是否是“jason”,它的age字段的值是否至少大于30而且它的email是否是jsweat_php@yahoo.com或者jsweat@users.sourceforge.net。
从审美观点上来说,所有在构建具体规范过程中所出现的中间变量都是令人不高兴的。那么,有没有方法来清除这些中间变量使得代码更加容易读和维护?是的,当然有!简单的利用PHP5的一个新特征来链接到对象的方法并返回这个方法。
具体方法的第一步可能是允许单个具体的规范知道如何“与”和“或”它自身。这可以通过引入Factory方法(具体请看第三章——工厂方法模式)来创建或规范(OrSpecification)和与规范(AndSpecification)对象。由于这些特点对所有的规范来说都是通用的,所以把它们封装到一个抽象的基类里绝对是个很好的主意。
abstract class BaseSpecification implements Specification { protected $field; public function and_($spec) { return new AndSpecification($this, $spec); } public function or_($spec) { return new OrSpecification($this, $spec); } } |
这有趣的方法名字add_()和or_()是必须的,因为”add”和”or”在PHP中是关键字。
通过引入这个基类,那些在前面书写的具体的类都必须修改,得让它们都继承于BaseSpecification:
class FieldEqualSpecification extends BaseSpecification { // ...} |
接下来要引入Factory方法来更加容易的创建单个具体的规范类。这可能会在单独的factory类里面实现,但是为了更加方便,你可以把这个方法增加到PolicyFactory类里面。
class PolicyFactory { protected function equal($field, $value) { return new FieldEqualSpecification($field, $value); } protected function gTorEq($field, $value) { return new FieldGreaterThanOrEqualSpecification($field, $value); } } |
现在,让我们联合这些所有的Factory方法来创建一个综述,就象下面显示的一样:
class PolicyFactory { // .. public function createJasonPolicy() { return $this->equal(‘name’, ‘Jason’)->and_( $this->gTorEq(‘age’, 30)->and_( $this->equal(‘sex’, ‘male’)->and_( $this->equal(‘email’, ‘jsweat_php@yahoo.com’)->or_( $this->equal(‘email’, ‘jsweat@users.sourceforge.net’) )))); } } |
就和前面一样,方法createJasonPolicy()创建了一个策略(Policy),但是这代码的可读性好很多了。
在经过所有的重组后,类的图表就如下所示:
规范模式能让你在应用程序里更加容易的组织和架构你的商业逻辑。我把这个模式包含在这本书里面的一个原因就是因为它开始显示了在真正的显示世界应用中模式是如何修改和组合的。