论坛首页 Java企业应用论坛

23模式之外的模式——规格模式 Specification Pattern

浏览 9147 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (19) :: 隐藏帖 (0)
作者 正文
   发表时间:2009-11-30   最后修改:2009-11-30

     规格模式是组合模式的一种扩展,在框架性开发中使用较多(项目级开发很少使用),这里做一个简单的介绍。由于是摘录,上下文章节可以参考我的博客,http://hi.baidu.com/cbf4life,或者下载PDF文件也一样。  发带格式、带图片的DOC文档太麻烦了,刚刚还出现了内容都整理好了,然后附件上传不上来,就在那转圈,我晕,只有重新发了!



      这里假设了一个这样一个场景,有一堆的User对象,UserProvider提供查询服务。们来看组合规格书(CompositeSpecification),它是一个抽象类,实现了与或非的操作,如下所示。

 

public abstract class CompositeSpecification implements IUserSpecification {
	//是否满足条件有实现类实现
	public abstract boolean isSatisfiedBy(User user);
	//and操作
	public IUserSpecification and(IUserSpecification spec) {
		return new AndSpecification(this,spec);
	}
	//not操作
	public IUserSpecification not() {
		return new NotSpecification(this);
	}
	//or操作
	public IUserSpecification or(IUserSpecification spec) {
		return new OrSpecification(this,spec);
	}
}

 

      候选对象是否满足条件是由isSatisfiedBy方法决定的,它代表的是一个判断逻辑,由各个实现类实现。三个与或非操作在抽象类中实现,它是通过直接new了一个子类,如此设计非常符合单一职责原则,每个子类都有一个独立的职责,要么完成与操作,要么完成或操作,要么完成非操作。我们先来看与操作规格书,如下所示。

 public class AndSpecification extends CompositeSpecification {

	//传递两个规格书进行and操作
	private IUserSpecification left;
	private IUserSpecification right;
	
	public AndSpecification(IUserSpecification _left,IUserSpecification _right){
		this.left = _left;
		this.right = _right;
	}
	//进行and运算
	@Override
	public boolean isSatisfiedBy(User user) {
		return left.isSatisfiedBy(user) && right.isSatisfiedBy(user);
	}
} 

      通过构造函数传递过来两个需要操作的规格书,然后通过isSatisfiedBy方法返回两者and操作结果。或规格书和非规格书与此类似,分别如下所示。

 

public class OrSpecification extends CompositeSpecification {
	//左右两个规格书
	private IUserSpecification left;
	private IUserSpecification right;
	
	public OrSpecification(IUserSpecification _left,IUserSpecification _right){
		this.left = _left;
		this.right = _right;
	}
	//or运算
	@Override
	public boolean isSatisfiedBy(User user) {
		return left.isSatisfiedBy(user) || right.isSatisfiedBy(user);
	}
}


public class NotSpecification extends CompositeSpecification {
	//传递一个规格书
	private IUserSpecification spec;
	
	public NotSpecification(IUserSpecification _spec){
		this.spec = _spec;
	}
	//not操作
	@Override
	public boolean isSatisfiedBy(User user) {
		return !spec.isSatisfiedBy(user);
	}
}
 

 

      该三个规格书都是不发生变化的,只要使用该框架,三个规格书都要实现的,而且代码基本上是雷同的,所以才有了父类依赖子类的设计,否则是严禁出现父类依赖子类的情况。大家再仔细看看这三个规格书和组合规格书,代码很简单,但也很巧妙,它跳出了我们面向对象设计的思维,不变部分使用一种固化方式实现。

名字相等、年龄大于基准年龄、Like格式等规格书都有少许改变,把实现接口变为继承基类,我们以名字相等规格书为例,如下所示。
public class UserByNameEqual extends CompositeSpecification {
	//基准姓名
	private String name;
	//构造函数传递基准姓名
	public UserByNameEqual(String _name){
		this.name = _name;
	}
	//检验用户是否满足条件
	public boolean isSatisfiedBy(User user) {
		return user.getName().equals(name);
	}
} 
      仅仅修改了黑体部分,其他没有任何改变。另外两个规格书修改相同,不再赘述。其他的User及UserProvider没有任何改动,不再赘述。
      我们修改一下场景类,如下所示。
public class Client {
	
	public static void main(String[] args) {
		//首先初始化一批用户
		ArrayList<User> userList = new ArrayList<User>();
		userList.add(new User("苏国庆",23));
		userList.add(new User("国庆牛",82));		
		userList.add(new User("张国庆三",10));
		userList.add(new User("李四",10));
		//定义一个用户查询类
		IUserProvider userProvider = new UserProvider(userList);
		//打印出名字包含国庆的人员
		System.out.println("===名字包含国庆的人员===");
		//定义一个规格
		IUserSpecification spec = new UserByAgeThan(25);
		IUserSpecification spec2 = new UserByNameLike("%国庆%");
		for(User u:userProvider.findUser(spec.and(spec2))){
			System.out.println(u);
		}
	}
} 
       在场景类中我们建立了两个规格书,一个是年龄大于25的用户,一个是名字中包含“国庆”两个字的用户,这两个规格书之间的关系是与关系,运行结果如下:

===名字包含国庆的人员===

用户名:国庆牛 年龄:82

  • 大小: 10.6 KB
   发表时间:2009-12-01  
没人顶,自己安慰一下吧
0 请登录后投票
   发表时间:2009-12-01  
~我刚刚载下了,好好学学,嘿嘿
0 请登录后投票
   发表时间:2009-12-01  
楼主辛苦了。
确实是挺有意思的一个模式,值得学习。
我感觉 它其实也是其他模式的一些组合吧?不过新提炼出一种来,确实也有他的独到之处。
0 请登录后投票
   发表时间:2009-12-02  
想问下,最后main方法里 

# for(User u:userProvider.findUser(spec.and(spec2))){ 
#             System.out.println(u); 
#         } 

spec.and(spec2) 返回的应该是基于 IUserSpecification 接口的对象。从接口里唯一可以确定是2着是不是符合要求 isSatisfiedBy(User user),返回布尔值。怎么具体把2个条件拼在一起的呢??没有看出来啊
0 请登录后投票
   发表时间:2009-12-02  
基本上就是 Composite pattern
0 请登录后投票
   发表时间:2009-12-02  
streamfly 写道
想问下,最后main方法里 

# for(User u:userProvider.findUser(spec.and(spec2))){ 
#             System.out.println(u); 
#         } 

spec.and(spec2) 返回的应该是基于 IUserSpecification 接口的对象。从接口里唯一可以确定是2着是不是符合要求 isSatisfiedBy(User user),返回布尔值。怎么具体把2个条件拼在一起的呢??没有看出来啊



   产生了一个递归调用,你可以仔细看一下PDF文件。
0 请登录后投票
   发表时间:2009-12-02  
这个循序渐进的讲解方式很不错,估计楼主读了不少外国技术书籍
0 请登录后投票
   发表时间:2009-12-02  
晕,少见多怪,人家都用了很多年了
0 请登录后投票
   发表时间:2009-12-02  
具体的好处在哪,使用于什么具体的场景,解决什么样的问题,LZ能再说一下吗?
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics