规格模式(Specification Pattern)

本文节选自《设计模式就该这样学》

1 规格模式的定义#

规格模式(Specification Pattern)可以认为是组合模式的一种扩展。很多时候程序中的某些条件决定了业务逻辑,这些条件就可以抽离出来以某种关系(与、或、非)进行组合,从而灵活地对业务逻辑进行定制。另外,在查询、过滤等应用场合中,通过预定义多个条件,然后使用这些条件的组合来处理查询或过滤,而不是使用逻辑判断语句来处理,可以简化整个实现逻辑。
这里的每个条件都是一个规格,多个规格(条件)通过串联的方式以某种逻辑关系形成一个组合式的规格。规格模式属于结构型设计模式。

2 规格模式的应用场景#

规格模式主要适用于以下应用场景。

(1)验证对象,检验对象本身是否满足某些业务要求或者是否已经为实现某个业务目标做好了准备。

(2)从集合中选择符合特定业务规则的对象或对象子集。

(3)指定在创建新对象的时候必须要满足某种业务要求。

3 规格模式的UML类图#

规格模式的UML类图如下图所示。

file

由上图可以看到,规格模式主要包含6个角色。

(1)抽象规格书(Specification):对规格书的抽象定义。

(2)组合规格书(CompositeSpecification):一般设计为抽象类,对规格书进行与或非操作,实现and()、or()、not()方法,在方法中关联子类,因为子类为固定类,所以父类可以进行关联。

(3)与规格书(AndSpecification):对规格书进行与操作,实现isSatisfiedBy()方法。

(4)或规格书(OrSpecification):对规格书进行或操作,实现isSatisfiedBy()方法。

(5)非规格书(NotSpecification):对规格书进行非操作,实现isSatisfiedBy()方法。

(6)业务规格书(BizSpecification):实现isSatisfiedBy()方法,对业务进行判断,一个类为一种判断方式,可进行扩展。

4 规格模式的通用写法#

以下是规格模式的通用写法。


public class Client {

    public static void main(String[] args) {
        //待分析的对象
        List<Object> list = new ArrayList<Object>();
        //定义两个业务规格书
        ISpecification spec1 = new BizSpecification("a");
        ISpecification spec2 = new BizSpecification("b");
        //规格调用
        for (Object o : list) {
            if(spec1.and(spec2).isSatisfiedBy(o)){  //如果o满足spec1 && spec2
                System.out.println(o);
            }
        }
    }

    //抽象规格书
    interface ISpecification {
        //候选者是否满足条件
        boolean isSatisfiedBy (Object candidate) ;
        //and操作
        ISpecification and (ISpecification spec);
        //or操作
        ISpecification or (ISpecification spec);
        //not操作
        ISpecification not ();
    }

    //组合规格书
    static abstract class CompositeSpecification implements ISpecification {
        //是否满足条件由子类实现
        public abstract boolean isSatisfiedBy (Object candidate) ;
        //and操作
        public ISpecification and (ISpecification spec) {
            return new AndSpecification(this, spec);
        }
        //or操作
        public ISpecification or(ISpecification spec) {
            return new OrSpecification(this, spec);
        }
        //not操作
        public ISpecification not() {
            return new NotSpecification(this);
        }
    }

    //与规格书
    static class AndSpecification extends CompositeSpecification {
        //传递两个规格书进行and操作
        private ISpecification left;
        private ISpecification right;

        public AndSpecification(ISpecification left, ISpecification right) {
            this.left = left;
            this.right = right;
        }
        
        //进行and运算
        public boolean isSatisfiedBy(Object candidate) {
            return left.isSatisfiedBy(candidate) && right.isSatisfiedBy(candidate);
        }
    }


    static class OrSpecification extends CompositeSpecification {
        //传递两个规格书进行or操作
        private ISpecification left;
        private ISpecification right;

        public OrSpecification(ISpecification left, ISpecification right) {
            this.left= left;
            this.right = right;
        }

        //进行or运算
        public boolean isSatisfiedBy(Object candidate) {
            return left.isSatisfiedBy(candidate) || right.isSatisfiedBy(candidate);
        }
    }

    static class NotSpecification extends CompositeSpecification {
        //传递两个规格书进行非操作
        private ISpecification spec;

        public NotSpecification(ISpecification spec) {
            this.spec = spec;
        }

        //进行not运算
        public boolean isSatisfiedBy(Object candidate) {
            return !spec.isSatisfiedBy(candidate);
        }
    }

    //业务规格书
    static class BizSpecification extends CompositeSpecification {
        //基准对象,如姓名等,也可以是int等类型
        private String obj;
        public BizSpecification(String obj) {
            this.obj = obj;
        }
        //判断是否满足要求
        public boolean isSatisfiedBy(Object candidate){
            //根据基准对象判断是否符合
            return true;
        }
    }
}

5 规格模式的优缺点#

5.1 优点#

规格模式非常巧妙地实现了对象筛选功能,适合在多个对象中筛选查找,或者业务规则不适于放在任何已有实体或值对象中,而且规则变化和组合会掩盖对象的基本含义的情况。

5.2 缺点#

规格模式中有一个很严重的问题就是父类依赖子类,这种情景只有在非常明确不会发生变化的场景中存在,它不具备扩展性,是一种固化而不可变化的结构。一般在面向对象设计中应该尽量避免。
关注微信公众号『 Tom弹架构 』回复“设计模式”可获取完整源码。

【推荐】Tom弹架构:30个设计模式真实案例(附源码),挑战年薪60W不是梦

本文为“Tom弹架构”原创,转载请注明出处。技术在于分享,我分享我快乐!
如果本文对您有帮助,欢迎关注和点赞;如果您有任何建议也可留言评论或私信,您的支持是我坚持创作的动力。关注微信公众号『 Tom弹架构 』可获取更多技术干货!

推荐文章

基于HADOOP_HOME自动加载HDFS配置?

基于HADOOP_HOME自动加载HDFS配置?

推荐文章

PHP-MySQL,排序克隆

PHP-MySQL,排序克隆

推荐文章

为什么Opera要包装一个在所有其他浏览器中以内联方式显示的列表?

为什么Opera要包装一个在所有其他浏览器中以内联方式显示的列表?

推荐文章

IOS-当模式视图位于主视图顶部时,不会接收到远程控制事件

IOS-当模式视图位于主视图顶部时,不会接收到远程控制事件

推荐文章

使用内容脚本时窗口中缺少属性

使用内容脚本时窗口中缺少属性

推荐文章

将Collection转换为Collection

将Collection转换为Collection

推荐文章

当mouseover和mouseout时jQuery动画化

当mouseover和mouseout时jQuery动画化

推荐文章

如何使用RVideo判断文件是否是有效的视频流?

如何使用RVideo判断文件是否是有效的视频流?

推荐文章

这是在Oracle数据库中插入行的正确方法吗?

这是在Oracle数据库中插入行的正确方法吗?

推荐文章

如何在ERD数据库图中表示多个相似的外键?

如何在ERD数据库图中表示多个相似的外键?

推荐文章

这个MPI代码有什么问题?

这个MPI代码有什么问题?

推荐文章

我可以使用字段哈希而不是直接字段比较来简化记录的比较吗?

我可以使用字段哈希而不是直接字段比较来简化记录的比较吗?

推荐文章

跟踪过程的最佳方法

跟踪过程的最佳方法

推荐文章

Servicemix导入所有依赖项

Servicemix导入所有依赖项

推荐文章

MySQL InnoDB锁行

MySQL InnoDB锁行

推荐文章

ListArray和List View,从解析器填充列表

ListArray和List View,从解析器填充列表