簡體   English   中英

良好的構建器模式鏈

[英]Good chain of builder pattern

我有一個像這樣復雜的業務對象

public class BusinessObject {

    public Team1Object object1;
    public Team2Object object2;
    public String debug;
    ...

}

我正在使用責任鏈模式的輕微修改來構建上述對象。

這是我的界面

public interface BusinessObjectAppender {

    public void append(BusinessObjectBuilder businessObject, Context someApplicationContext);

}

現在團隊可以來編寫他們的 appender。 像這樣

public class Team1ObjectAppender implements BusinessObjectAppender {

    public void append(BusinessObjectBuilder businessObject, Context someApplicationContext) {
        Team1Object object1 = somehowComputeObject1();
        businessObject.object1(object1)
    }
}
public class Team2Appender implements BusinessObjectAppender {

    public void append(BusinessObjectBuilder businessObject, Context someApplicationContext) {
        Team2Object object2 = somehowComputeObject2();
        businessObject.object2(object2)
    }
}

通過使用這種方法,在復雜對象構造的情況下,邏輯不會膨脹。

但它也有類似的問題

  1. Team1 周圍沒有任何護欄來干擾另一個團隊的對象或依賴另一個團隊的數據。 除了代碼審查。

  2. BusinessObject 是多態的情況,一旦我創建了 1 種類型的構建器,就不可能在附加程序中更改它。

  1. 這是正確的模式嗎?

  2. 有什么其他方法可以實現同樣的目標? (以可擴展、可理解的方式創建復雜對象)

如果您計划使用構建器模式,那么在關注點分離之后,我更願意通過使用BusinessObjectBuilder構建器模式對象為BusinessObject對象使用單獨的類。 為了從相關域/業務對象訪問構建器模式對象,您可以(可選地,如果合適的話,我會建議)添加一個public static create()方法來實例化構建器對象和封裝的業務對象建立。 我個人更喜歡 builder 對象的流暢風格,因為這些方法可以鏈接在一起,這使得編寫代碼變得更加容易。

由於您擔心將 Team1Object 字段和 Team2Object 字段作為單獨的問題構建的復雜性,我認為您正在尋找的不是平面構建器模式,而是構建器模式的外觀或構建器方面。 為了使用構建器的外觀,您將使用公共構建器基類和從基類派生的構建器外觀類。

基類在實例化時將創建一個簡單的 BusinessObject 並提供一種方法來構建每個字段,包括通過合並流暢的外觀構建器。 流暢的立面構建器將僅構建對象包的一部分,該部分本身可能很復雜,因此可能與對象的整體構建作為一個整體分開關注。

與所有 fluent builder 類一樣,返回類型與 fluent builder(或 fluent facade builder)類相同。 考慮以下修改:

public class BusinessObject {
    internal BusinessObject() {
        // Default constructor should exist
        // but only needs to be visible at the
        // BusinessObjectBuilder scope.
        // use whatever access modifier you would
        // prefer, however, based on needs,
        // internal or public is appropriate.
        // in C++, use `friend BusinessObjectBuilder`
    }
    public Team1Object object1;
    public Team2Object object2;
    public String debug;
    ...
    public static BusinessObjectBuilder create() {
        return new BusinessObjectBuilder();
    }
}

public class BusinessObjectBuilder {
    protected BusinessObject bObject; // the object we will build
    internal BusinessObjectBuilder() {
        // A default constructor, again minimally visible
        // to BusinessObject; internal or public is good here.
        // Needs to create a basic BusinessObject.
        bObject = new BusinessObject();
    }
    public BusinessObjectBuilder debug(String debugString) {
        // Sets BusinessObject.debug
        this.bObject.debug += debugString + "\n";
        // Then returns the BusinessObjectBuilder.
        // Every method should return this except the facade methods
        // and the build method.
        return this;
    }
    public Team1ObjectBuilder team1Object() {
        // Returns the Team1Object builder facet.
        return new Team1ObjectBuilder(this.bObject);
    }
    public Team2ObjectBuilder team2Object() {
        // Returns the Team2Object builder facet.
        return new Team1ObjectBuilder(this.bObject);
    }
    public BusinessObject build() {
        // Technically, it's already built at this point. Return it.
        return this.bObject;
    }
}

public class Team1ObjectBuilder extends BusinessObjectBuilder {
    private BusinessObject bObject; // the object we will build
    internal Team1ObjectBuilder(BusinessObject bObject) {
        // This time we copy the object we were building
        this.bObject = bObject;
    }
    private Team1Object somehowComputeObject1() {
        // pour on the magic
        return new Team1Object();
    }
    public Team1ObjectBuilder append(Context someApplicationContext) {
        this.bObject.object1 = somehowComputeObject1();
    }
}

public class Team2ObjectBuilder extends BusinessObjectBuilder {
    private BusinessObject bObject; // the object we will build
    internal Team2ObjectBuilder(BusinessObject bObject) {
        // Again we copy the object we were building
        this.bObject = bObject;
    }
    private Team2Object somehowComputeObject2() {
        // pour on the magic
        return new Team2Object();
    }
    public Team2ObjectBuilder append(Context someApplicationContext) {
        this.bObject.object2 = somehowComputeObject2();
    }
}

如果你使用這個 fluent builder 和 fluent Facade builder 模式,你可以像這樣使用它:

BusinessObject complexBusinessObject = BusinessObject.create()
                                                     .debug("Let's build team1Object.")
                                                     .team1Object().append( /* someApplicationContext */)
                                                     .debug("Let's build team2Object.")
                                                     .team2Object().append( /* someApplicationContext */)
                                                     .debug("All done.")
                                                     .build();

但是我不確定這是否是您想要實現的目標,特別是因為我對 Team1 和 Team2 對象並不十分熟悉,或者您可能如何根據職責和層次結構來定義它們。

你提到了責任鏈。 當組件鏈中的每個組件都輪流(在鏈中)來處理命令/查詢時,將使用此模式,並可選擇停止鏈繼續進行。

考慮一個過程,例如雇用一名員工。 一路上有幾個過程。 隨着每個過程的完成,鏈中的下一個過程開始。 如果發生異常,則該員工可能根本沒有被雇用(停止供應鏈)。

為此,我們有一個責任鏈,我們將使用責任鏈設計模式。 例如,如果 Team2 流程依賴於 Team1 流程,您可以使用此模式,因為它可以解決此問題。

為了使用責任鏈模式,您將需要BusinessObject以及一個或多個BusinessObjectModifier類。 由於這里的范圍僅限於Team1AppenderTeam2Appender對象,我們將使用這兩個作為參考。

為了構建鏈,您可能需要一個基類用於鏈中下一個鏈接的next字段,並使用add()方法切換到鏈中的下一個負責鏈接。

考慮以下責任鏈模式:

    public class BusinessObject {
        public Team1Object object1;
        public Team2Object object2;
        public String debug;
        ...
    }

    public abstract class BusinessObjectAppender { // provides shared append() modifier
        protected BusinessObjectAppender next = null;
        public void add(BusinessObjectAppender boa) {
            if (this.next == null) {
                this.next = boa;
            }
            else {
                next.add(boa); // recursive call to the end of the linked list "chain"
            }
        }
        public abstract void append(BusinessObject businessObject, Context someApplicationContext);
    }

    public class Team1ObjectAppender extends BusinessObjectAppender {
        public BusinessObject append(BusinessObject businessObject, Context someApplicationContext) {
            Team1Object object1 = somehowComputeObject1();
            businessObject.object1 = object1;
            if (this.next == null) {
                return businessObject; // you have to since you can't pass by ref/out in java
            }
            else {
                return next.append(businessObject, someApplicationContext);
            }
        }
    }

    public class Team2ObjectAppender extends BusinessObjectAppender {
        public BusinessObject append(BusinessObject businessObject, Context someApplicationContext) {
            Team2Object object2 = somehowComputeObject2();
            businessObject.object2 = object2;
            if (this.next == null) {
                return businessObject; // you have to since you can't pass by ref/out in java
            }
            else {
                return next.append(businessObject, someApplicationContext);
            }
        }
    }

現在,這應該設置鏈。 要使用它,您可以執行以下操作:

BusinessObject businessObject = new BusinessObject();
BusinessObjectAppender appendChain = new Team1ObjectAppender();
appendChain.add(new Team2ObjectAppender()); // start --> team1 --> team2 --> done
businessObject = appendChain(businessObject, /*someApplicationContext*/);

這能解決您的問題嗎? 如果你有責任鏈,那么也許吧。

我看到您的原始規范使用構建器作為主題來傳遞鏈而不是最終對象。 這是兩種模式的有趣交集。

如果您想使用構建器,然后使用責任鏈方法構建對象,您可能會考慮以下方面的內容:

public class BusinessObject {
    internal BusinessObject() {
        // Default constructor should exist
        // but only needs to be visible at the
        // BusinessObjectBuilder scope.
        // use whatever access modifier you would
        // prefer, however, based on needs,
        // internal or public is appropriate.
        // in C++, use `friend BusinessObjectBuilder`
    }
    public Team1Object object1;
    public Team2Object object2;
    public String debug;
    ...
    public static BusinessObjectBuilder create() {
        return new BusinessObjectBuilder();
    }
}

public abstract class BusinessObjectAppender { // provides shared append() modifier
    protected BusinessObjectAppender next = null;
    public void add(BusinessObjectAppender boa) {
        if (this.next == null) {
            this.next = boa;
        }
        else {
            next.add(boa); // recursive call to the end of the linked list "chain"
        }
    }
    public abstract void append(BusinessObject businessObject, Context someApplicationContext);
}

public class Team1ObjectAppender extends BusinessObjectAppender {
    public BusinessObject append(BusinessObject businessObject, Context someApplicationContext) {
        Team1Object object1 = somehowComputeObject1();
        businessObject.object1 = object1;
        if (this.next == null) {
            return businessObject; // you have to since you can't pass by ref/out in java
        }
        else {
            return next.append(businessObject, someApplicationContext);
        }
    }
}

public class Team2ObjectAppender extends BusinessObjectAppender {
    public BusinessObject append(BusinessObject businessObject, Context someApplicationContext) {
        Team2Object object2 = somehowComputeObject2();
        businessObject.object2 = object2;
        if (this.next == null) {
            return businessObject; // you have to since you can't pass by ref/out in java
        }
        else {
            return next.append(businessObject, someApplicationContext);
        }
    }
}

public class BusinessObjectBuilder {
    protected BusinessObject bObject; // the object we will build
    internal BusinessObjectBuilder() {
        // A default constructor, again minimally visible
        // to BusinessObject; internal or public is good here.
        // Needs to create a basic BusinessObject.
        bObject = new BusinessObject();
    }
    public BusinessObjectBuilder debug(String debugString) {
        // Sets BusinessObject.debug
        this.bObject.debug += debugString + "\n";
        // Then returns the BusinessObjectBuilder.
        // Every method should return this except the facade methods
        // and the build method.
        return this;
    }
    public BusinessObjectBuilder append(Context someApplicationContext) {
        // Create the chain
        BusinessObjectAppender appendChain = new Team1ObjectAppender();
        appendChain.add(new Team2ObjectAppender()); // start --> team1 --> team2 --> done
        this.bObject = appendChain(this.bObject, someApplicationContext);
        // Return the Builder.
        return this;
    }
    public BusinessObject build() {
        // Technically, it's already built at this point. Return it.
        return this.bObject;
    }
}

然后像這樣使用它:

BusinessObject complexBusinessObject = BusinessObject.create()
                                                     .debug("Run through the chain of responsibilities.")
                                                     .append( /* someApplicationContext */)
                                                     .debug("All done.")
                                                     .build();

這不是交叉這兩個概念的唯一方法。 有幾個端點的模式之間的線條模糊,盡管我並不真的想一一列舉。

當然,我想回答你的問題。

  1. 這是正確的模式嗎? 這取決於你需要什么。

責任鏈由一個命令源(在本例中為append()調用程序塊)組成,它通過序列處理對象系列( BusinessObjectAppender對象)的單鏈表中的每個處理對象處理命令( append ) .

如果你沒有鏈條,那肯定是錯誤的模式。 如果您不需要單一的命令源append()在一個地方調用append() ),那么它不一定是正確的設計,或者可以重構直到它是。

構建器模式為構建復雜對象提供了一種解決方案,當構造函數不剪切它時。 在這種情況下,構造這樣的對象本身就是一個單獨的問題,因此構造從它正在構建的類中分離出來並放入一個單獨的構建器類中。

如果您需要一種方法來構造一個與表示方式不同的對象,那么它可能是正確的模式。

例如,汽車向駕駛員或買家或賣家展示的方式肯定不會使用與在工廠中制造汽車時使用的相同界面。 當然,它會有一個品牌、型號和年份,都是一樣的。 但客戶並不關心零件成本、構建時間、各種系統測試的測試結果,以及員工在構建當天所參與的工作。 但是,果然,它在 6.5 秒內變為 0-60,並被塗成紅色。

當構建對象很復雜並且表示與構建方式不同時,構建器模式將解決它。 (當您使用流暢的方法時,它看起來不錯。)

建造者模式和責任鏈模式都是原始“四人幫”設計模式的一部分。

它們的不同之處在於 Builder 模式是一種創建模式,而責任鏈是一種行為模式。

我不打算重寫這本書,所以我可以讓你參考標題,“設計模式:可重用面向對象軟件的元素”(1994。Gamma,Erich;Helm,Richard;Johnson,Ralph;和 Vlissides,約翰。)如果您希望將他們的模式之一與您自己的需求相匹配。 由於您還沒有解釋團隊 1 和團隊 2 的目的,我無法為您決定哪個最好。

  1. 其他方式是什么?

四人幫提供了其他一些創造和行為模式。

如果責任鏈不能解決您的問題,那么命令模式可能會解決您的問題。 (這幾乎是BusinessObjectAppender.append()抽象方法為您所做的,減去鏈;因為append()大致是execute()一旦實現。)

如果您需要在多個 (1...n) 個進程中為同一主題執行相同的 Command,但這些進程沒有在責任鏈中鏈接在一起並且不需要特定順序,那么簡單的 Iterator 就可以了。 幸運的是,Java 提供了許多非常容易迭代的工具。 考慮ArrayList<Appender> appenders

有很多很多選擇可供選擇。 有時你可以把它們混合起來。

我實際上在 Udemy 上學習了一個設計模式課程,專門針對 C++,但是網上有很多地方可以找到這些信息。 這看起來是一個很好的摘要來源,特別是因為這些示例是用 Java 編寫的,並且為我選擇的設計選擇提供了替代方案,為您提供了一些額外的示例: JournalDev

希望這有助於為您指明正確的方向。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM