繁体   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