規格模式(Specification Pattern)

Tom彈架構發表於2021-11-25

本文節選自《設計模式就該這樣學》

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 規格模式的優點

規格模式非常巧妙地實現了物件篩選功能,適合在多個物件中篩選查詢,或者業務規則不適於放在任何已有實體或值物件中,而且規則變化和組合會掩蓋物件的基本含義的情況。

6 規格模式的缺點

規格模式中有一個很嚴重的問題就是父類依賴子類,這種情景只有在非常明確不會發生變化的場景中存在,它不具備擴充套件性,是一種固化而不可變化的結構。一般在物件導向設計中應該儘量避免。

關注微信公眾號『 Tom彈架構 』回覆“設計模式”可獲取完整原始碼。

【推薦】Tom彈架構:30個設計模式真實案例(附原始碼),挑戰年薪60W不是夢

本文為“Tom彈架構”原創,轉載請註明出處。技術在於分享,我分享我快樂!
如果本文對您有幫助,歡迎關注和點贊;如果您有任何建議也可留言評論或私信,您的支援是我堅持創作的動力。關注微信公眾號『 Tom彈架構 』可獲取更多技術乾貨!

相關文章