简体   繁体   English

良好的构建器模式链

[英]Good chain of builder pattern

i have a complex business object like so我有一个像这样复杂的业务对象

public class BusinessObject {

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

}

I am using slight modification of chain of responsibility pattern to build the above object.我正在使用责任链模式的轻微修改来构建上述对象。

Here is my interface这是我的界面

public interface BusinessObjectAppender {

    public void append(BusinessObjectBuilder businessObject, Context someApplicationContext);

}

Now teams can comes and write their appenders.现在团队可以来编写他们的 appender。 like so像这样

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)
    }
}

By using this approach, In case of complex object construction the logic does not bloat up.通过使用这种方法,在复杂对象构造的情况下,逻辑不会膨胀。

But It also has issues like但它也有类似的问题

  1. There are no guardrails around Team1 messing up with another team's object or being dependent on another team's data. Team1 周围没有任何护栏来干扰另一个团队的对象或依赖另一个团队的数据。 Apart from code reviews.除了代码审查。

  2. The cases where BusinessObject is polymorphic, Once i have created builder of 1 type it is not possible to change it in appenders. BusinessObject 是多态的情况,一旦我创建了 1 种类型的构建器,就不可能在附加程序中更改它。

Question

  1. Is this right pattern to do?这是正确的模式吗?

  2. what are other ways to achieve the same thing?有什么其他方法可以实现同样的目标? (creating complex objects in scalable, understandable way) (以可扩展、可理解的方式创建复杂对象)

If you plan to use a builder pattern, then following separation of concerns, I would prefer to use a separate class for the BusinessObject object by employing a BusinessObjectBuilder builder pattern object.如果您计划使用构建器模式,那么在关注点分离之后,我更愿意通过使用BusinessObjectBuilder构建器模式对象为BusinessObject对象使用单独的类。 In order to access the builder pattern object from the relevant domain/business object, you can then (optionally, and I would recommend if appropriate, to) add a public static create() method to instantiate both a builder object and an encapsulated business object to build.为了从相关域/业务对象访问构建器模式对象,您可以(可选地,如果合适的话,我会建议)添加一个public static create()方法来实例化构建器对象和封装的业务对象建立。 I personally prefer the fluent style of builder objects, as the methods can be chained together and it makes writing the code so much easier.我个人更喜欢 builder 对象的流畅风格,因为这些方法可以链接在一起,这使得编写代码变得更加容易。

As you are concerned about the complexity of building a Team1Object field and Team2Object field as separate concerns, I would think that what you are looking for is not a flat builder pattern, but instead facades of a builder pattern, or builder facets.由于您担心将 Team1Object 字段和 Team2Object 字段作为单独的问题构建的复杂性,我认为您正在寻找的不是平面构建器模式,而是构建器模式的外观或构建器方面。 In order to use facades of a builder, you would use a common builder base class and builder facade classes derived from the base class.为了使用构建器的外观,您将使用公共构建器基类和从基类派生的构建器外观类。

The base class, upon instantiation will create a simple BusinessObject and provide a method to build each field, including, by incorporating the fluent facade builders.基类在实例化时将创建一个简单的 BusinessObject 并提供一种方法来构建每个字段,包括通过合并流畅的外观构建器。 The fluent facade builders will build only one part of the object parcel, the part of which may be complex in and of itself and could therefore be a separate concern from the overall building of the object as a whole.流畅的立面构建器将仅构建对象包的一部分,该部分本身可能很复杂,因此可能与对象的整体构建作为一个整体分开关注。

As in all fluent builder classes, the return type is the same as the fluent builder (or fluent facade builder) class.与所有 fluent builder 类一样,返回类型与 fluent builder(或 fluent facade builder)类相同。 Consider the following modifications:考虑以下修改:

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();
    }
}

If you employ this fluent builder with fluent facade builder pattern, you can then use it like so:如果你使用这个 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();

But then I'm not sure if this is what you wanted to achieve, particularly because I'm not exquisitely familiar with Team1 and Team2 objects or how you might would define them in terms of duty and hierarchy.但是我不确定这是否是您想要实现的目标,特别是因为我对 Team1 和 Team2 对象并不十分熟悉,或者您可能如何根据职责和层次结构来定义它们。

You mentioned chain of responsibility.你提到了责任链。 This pattern is used when a chain of components each get a turn (in a chain) to process a command/query, and optionally stop the chain from proceeding.当组件链中的每个组件都轮流(在链中)来处理命令/查询时,将使用此模式,并可选择停止链继续进行。

Consider a process such as hiring an employee.考虑一个过程,例如雇用一名员工。 There are several processes along the way.一路上有几个过程。 As each process is completed, the next process in the chain begins.随着每个过程的完成,链中的下一个过程开始。 If an exception occurs, perhaps the employee isn't hired after all (stopping the chain).如果发生异常,则该员工可能根本没有被雇用(停止供应链)。

For this we have a chain of responsibilities and we would use the chain of responsibility design pattern.为此,我们有一个责任链,我们将使用责任链设计模式。 If, for example, Team2 processes depend on Team1 processes, you can use this pattern as it would solve this issue.例如,如果 Team2 流程依赖于 Team1 流程,您可以使用此模式,因为它可以解决此问题。

In order to use a chain of responsibility pattern, you will need the BusinessObject as well as one or more BusinessObjectModifier classes.为了使用责任链模式,您将需要BusinessObject以及一个或多个BusinessObjectModifier类。 Since the scope here is limited to Team1Appender and Team2Appender objects, we'll use those two as a reference.由于这里的范围仅限于Team1AppenderTeam2Appender对象,我们将使用这两个作为参考。

In order to build the chain, you might want a base class to use for a next field for the next link in the chain and an add() method to hand-off to the next responsible link in the chain.为了构建链,您可能需要一个基类用于链中下一个链接的next字段,并使用add()方法切换到链中的下一个负责链接。

Consider the following chain of responsibility pattern:考虑以下责任链模式:

    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);
            }
        }
    }

Now, this should set up the chain.现在,这应该设置链。 To use it, you might do something like:要使用它,您可以执行以下操作:

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

Does this solve your problem?这能解决您的问题吗? If you have a chain of responsibility, then perhaps.如果你有责任链,那么也许吧。

I see your original specification used a builder as the subject to pass around the chain instead of the final object.我看到您的原始规范使用构建器作为主题来传递链而不是最终对象。 This is an interesting intersection of the two patterns.这是两种模式的有趣交集。

If you wanted to use a builder but then build an object using a chain of responsibility method, you might consider something along the lines of:如果您想使用构建器,然后使用责任链方法构建对象,您可能会考虑以下方面的内容:

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;
    }
}

And then use it like so:然后像这样使用它:

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

This isn't the only way to intersect these two concepts.这不是交叉这两个概念的唯一方法。 There are several endpoints where the lines blur between patterns, though I don't really wish to enumerate them all.有几个端点的模式之间的线条模糊,尽管我并不真的想一一列举。

I would like to, of course, answer your questions.当然,我想回答你的问题。

  1. Is it the right pattern?这是正确的模式吗? That depends on what you need.这取决于你需要什么。

Chain of responsibility consists of a source of command (the append() caller block in this case) which processes the command ( append ) through each processing object within a singly linked-list of a serially-sequenced series of processing objects ( BusinessObjectAppender objects).责任链由一个命令源(在本例中为append()调用程序块)组成,它通过序列处理对象系列( BusinessObjectAppender对象)的单链表中的每个处理对象处理命令( append ) .

If you have no chain, it's definitely the wrong pattern.如果你没有链条,那肯定是错误的模式。 If you don't require a single source of command (calling append() in one place), then it's not necessarily the right design or could be refactored until it is.如果您不需要单一的命令源append()在一个地方调用append() ),那么它不一定是正确的设计,或者可以重构直到它是。

Builder pattern provides a solution for building a complex object when a constructor just doesn't cut it.构建器模式为构建复杂对象提供了一种解决方案,当构造函数不剪切它时。 In this case, constructing such an object is itself a separate concern, and therefore construction is broken off of the class it is building and put into a separate builder class.在这种情况下,构造这样的对象本身就是一个单独的问题,因此构造从它正在构建的类中分离出来并放入一个单独的构建器类中。

If you need a way to construct an object which is different than the way it will be represented, it could be the right pattern.如果您需要一种方法来构造一个与表示方式不同的对象,那么它可能是正确的模式。

For example, the way in which an automobile is presented to a driver or buyer or seller is most certainly not using the same interfaces that were used to build it in a factory.例如,汽车向驾驶员或买家或卖家展示的方式肯定不会使用与在工厂中制造汽车时使用的相同界面。 Of course, it will have a make, model, and year, all the same.当然,它会有一个品牌、型号和年份,都是一样的。 But the customer isn't concerned with the cost of parts, time it takes to build, test results for various systems tests, which employees were involved on the days it was being built.但客户并不关心零件成本、构建时间、各种系统测试的测试结果,以及员工在构建当天所参与的工作。 But, sure enough, it goes 0-60 in 6.5 seconds and was painted the color red.但是,果然,它在 6.5 秒内变为 0-60,并被涂成红色。

When building an object is complex and representation differs from the way in which it is built, a Builder pattern will solve it.当构建对象很复杂并且表示与构建方式不同时,构建器模式将解决它。 (And it looks nice when you use a fluent approach.) (当您使用流畅的方法时,它看起来不错。)

Both the builder pattern and the chain of responsibility pattern are part of the original "Gang of Four" design patterns.建造者模式和责任链模式都是原始“四人帮”设计模式的一部分。

Where they differ is Builder pattern is a Creational pattern and Chain of Responsibility is a Behavioral pattern.它们的不同之处在于 Builder 模式是一种创建模式,而责任链是一种行为模式。

I don't aim to rewrite the book, so I could just refer you to the title, "Design Patterns: Elements of Reusable Object-Oriented Software" (1994. Gamma, Erich; Helm, Richard; Johnson, Ralph; and Vlissides, John.) if you are looking to match one of their patterns to your own needs.我不打算重写这本书,所以我可以让你参考标题,“设计模式:可重用面向对象软件的元素”(1994。Gamma,Erich;Helm,Richard;Johnson,Ralph;和 Vlissides,约翰。)如果您希望将他们的模式之一与您自己的需求相匹配。 Since you haven't explained the purpose of team 1 and team 2, I can't decide for you what is best.由于您还没有解释团队 1 和团队 2 的目的,我无法为您决定哪个最好。

  1. What are the other ways?其他方式是什么?

The Gang of Four provided a few other Creational and Behavioral patterns.四人帮提供了其他一些创造和行为模式。

If Chain of Responsibility doesn't solve your problem, then a Command pattern might.如果责任链不能解决您的问题,那么命令模式可能会解决您的问题。 (That's pretty much what the BusinessObjectAppender.append() abstract method did for you, minus the chain; as append() is roughly execute() once implemented.) (这几乎是BusinessObjectAppender.append()抽象方法为您所做的,减去链;因为append()大致是execute()一旦实现。)

If you need to execute the same Command for the same subject across several (1...n) processes, but where the processes are not linked together in a responsibility chain and require no particular order, then a simple Iterator would do fine.如果您需要在多个 (1...n) 个进程中为同一主题执行相同的 Command,但这些进程没有在责任链中链接在一起并且不需要特定顺序,那么简单的 Iterator 就可以了。 Fortunately Java provides many facilities which are iterable very easily.幸运的是,Java 提供了许多非常容易迭代的工具。 Consider ArrayList<Appender> appenders .考虑ArrayList<Appender> appenders

There are many, many options to choose from.有很多很多选择可供选择。 Sometimes you can mix them up.有时你可以把它们混合起来。

I actually picked up a design patterns class on Udemy, specifically for C++, but there's many places online where you can find this information.我实际上在 Udemy 上学习了一个设计模式课程,专门针对 C++,但是网上有很多地方可以找到这些信息。 This looks like a good source of summaries, particularly because the examples are in Java, and offer alternatives to the design choices I chose, giving you some extra examples: JournalDev .这看起来是一个很好的摘要来源,特别是因为这些示例是用 Java 编写的,并且为我选择的设计选择提供了替代方案,为您提供了一些额外的示例: JournalDev

Hopefully, that helps point you in the right direction.希望这有助于为您指明正确的方向。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM