設計模式之規格模式(Specification Pattern)
1 規格模式的定義
規格模式(Specification Pattern)可以認為是組合模式的一種擴展。很多時候程序中的某些條件決定了業務邏輯,這些條件就可以抽離出來以某種關系(與、或、非)進行組合,從而靈活地對業務邏輯進行定制。另外,在查詢、過濾等應用場合中,通過預定義多個條件,然后使用這些條件的組合來處理查詢或過濾,而不是使用邏輯判斷語句來處理,可以簡化整個實現邏輯。這里的每個條件都是一個規格,多個規格(條件)通過串聯的方式以某種邏輯關系形成一個組合式的規格。規格模式屬于結構型設計模式。
2 規格模式的應用場景
規格模式主要適用于以下應用場景。
(1)驗證對象,檢驗對象本身是否滿足某些業務要求或者是否已經為實現某個業務目標做好了準備。
(2)從集合中選擇符合特定業務規則的對象或對象子集。
(3)指定在創建新對象的時候必須要滿足某種業務要求。
3 規格模式的UML類圖
規格模式的UML類圖如下圖所示。
由上圖可以看到,規格模式主要包含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 規格模式的優點
規格模式非常巧妙地實現了對象篩選功能,適合在多個對象中篩選查找,或者業務規則不適于放在任何已有實體或值對象中,而且規則變化和組合會掩蓋對象的基本含義的情況。
6 規格模式的缺點
規格模式中有一個很嚴重的問題就是父類依賴子類,這種情景只有在非常明確不會發生變化的場景中存在,它不具備擴展性,是一種固化而不可變化的結構。一般在面向對象設計中應該盡量避免。