Java Builder 模式和“深度”构建模式对象层次结构
在“深层”对象层次结构中使用生成器模式的最佳实践是什么?为了详细说明,我探索了将 Joshua Bloch 提出的 Builder 模式应用到我的 XML 绑定代码的想法(我使用的是 SimpleXML,但这个问题适用于任何情况)。我的对象层次结构有 4 层深,具有不同程度的复杂性。我的意思是,在某些级别上,我的对象只有几个属性,而在其他一些级别上,我有多达 10 个属性。
因此,请考虑这个假设的示例(为了简洁起见,我省略了简单的 XML 注释)
public class Outermost {
private String title;
private int channel;
private List<Middle> middleList;
}
class Middle {
private int id;
private String name;
private boolean senior;
/* ... ... 10 such properties */
private Innermost inner;
}
class Innermost {
private String something;
private int foo;
/* ... Few more of these ..*/
}
如果我想要使用构建器强制创建 Outermost
对象,最好的方法是什么?最明显的答案是为上述每个类提供内部静态构建器
类。
但是,这不会让事情变得像构建器模式试图解决的问题一样难以处理吗?我正在考虑这样的事情 - 这将强制执行“由内而外”的方法 - 意味着 Innermost
对象必须完全构造并实例化,然后才能添加到 Middle
代码>对象。但我们都知道,在实践中(尤其是在构建 XML 或 JSON 时),我们很少有“及时”信息来完成这一任务。
很有可能,我们最终会为所有级别的每个属性都设置变量;并在最后创建对象。或者,最终会在代码中出现多个级别的 Builder,从而增加混乱。
那么,关于如何优雅地实现这一点有什么想法吗?
What is the best practice for using Builder pattern in "deep" object hierarchies? To elaborate, I explored the idea of applying the Builder pattern as proposed by Joshua Bloch, to my XML binding code (I am using SimpleXML but this question would apply to any case). My object hierarchy is 4 levels deep, with various degree of complexity. By that, I mean, in some levels I have just a couple of properties for my objects, whereas at some other levels I have up to 10.
So consider this hypothetical example (I am leaving out the Simple XML annotations for brevity)
public class Outermost {
private String title;
private int channel;
private List<Middle> middleList;
}
class Middle {
private int id;
private String name;
private boolean senior;
/* ... ... 10 such properties */
private Innermost inner;
}
class Innermost {
private String something;
private int foo;
/* ... Few more of these ..*/
}
If I wanted to enforce creation of the Outermost
object using builders, what would be the best way to go about it? The most obvious answer is to have inner static Builder
classes for each of the above classes.
But, wouldn't that make things as unwieldy as the very problem Builder pattern tries to solve? I am thinking about stuff like - this will enforce an "inside out" approach - meaning that the Innermost
object will have to be fully constructed and instantiated before it can be added to the Middle
object. But we all know that in practice (especially when one is building XML or JSON), we rarely have "timely" information to accomplish this.
Chances are, one will end up having variables for each and every property - across all levels; and create the objects in the very end. OR, one will end up having Builder for multiple levels floating around in the code, adding to the confusion.
So, any ideas on how to elegantly accomplish this?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
构建器模式的描述这里是我猜猜你指的是什么;它与维基百科此处中描述的模式略有不同,我更喜欢前者。
从我读到的描述中,我不认为您对构造顺序或封装损失不可避免的担忧。对我来说,最大的问题是原始数据的结构。
假设我们
现在可以根据需要创建任意数量的 middleBuilder
我们可以将相同的模式应用于进一步的嵌套级别。
为了解决你的第一点,每个属性都有一个变量:取决于我们如何设计构建器以及我们的数据来自哪里。如果我们来自 UI,那么我们几乎每个属性都有一个变量,我们的情况也不会更糟。如果按照我上面的建议,我们正在迭代某些数据结构,那么构建器可能负责解释该数据结构。在我的示例中,我们向下传递 MiddleData 实例。一些额外的耦合,但它确实封装了细节。
为了解决你的第二点,我们不会边走边构建东西,而是有效地使用构建器作为数据的积累点。最终我们调用“Go and Build”方法,但此时我们应该拥有所有数据,以便构建整个层次结构。
The description of the Builder Pattern here is I guess what you are referring to; it's a .little different than the pattern described in Wikipedia here, I prefer the former.
I don't see that your concerns about order of construction or loss of encapsulation inevitable follow from the descriptions I read. For me the big question is the structure of your raw data.
Suppose we have
Now we can create as many middleBuilders as we need
We can apply the same pattern to the further levels of nesting.
To address your first point, a variable for every property: depends on how we design the builders and where our data is coming from. If we're, say coming from a UI then we pretty much have a variable per property anyway, we're no worse off. If as per my suggestion above we're iterating some data structure, then maybe the builder takes responsibility for iterpreting that data structure. In my example we pass MiddleData instances down. Some extra coupling but it does encapsulate the details.
To address your second point we don't build things as we go, instead we're effectively using the builder as the accumulation point for the data. Eventually we call the "Go and Build" method, but at that point we should have all the data in place so the whole hierarchy just builds.
这是可以做到的,但可以说不值得这样做。显而易见的实现......
很快就遇到了问题。如果您尝试类似的操作
,则不会编译,因为
opacity()
不知道它返回的是Rectangle.Builder
,而只是返回Shape.Builder;
。因此,您必须按从派生最多到派生最少的顺序调用属性:如果您想解决此问题,则需要使属性方法通用,以便超类方法仍将返回子类构建器。据我所知,不可能做到 100% 可靠,但通过一些自引用泛型,您可以接近:
注意
@SuppressWarnings
注释;如果子类打破了FooBuilder
始终扩展FooSuperclassBuilder
的约定,系统就会崩溃。你可以看到代码变得多么丑陋。此时,也许最好放弃 第 2 项 并相反,冥想 第 16 条:优先考虑组合而不是继承。
It can be done, but it's arguably not worth doing. The obvious implementation...
...quickly runs into a problem. If you try something like
it's not going to compile, because
opacity()
doesn't know it's returning aRectangle.Builder
, just aShape.Builder<Rectangle>
. So you have to call the attributes in order, from most-derived to least-derived:If you want to get around this, you need to make the attribute methods generic, so that the superclass methods will still return the subclass builders. There's no way AFAIK to make this 100% reliable, but with some self-referential generics you can get close:
Note the
@SuppressWarnings
annotations; if a subclass breaks the convention thatFooBuilder
always extendsFooSuperclassBuilder<Foo, FooBuilder>
, the system breaks down.And you can see how ugly the code gets. At this point maybe it's better to abandon Item 2 and instead meditate on Item 16: Favor composition over inheritance.
如果您使用 JAXB 从 XML 模式生成代码,则来自 jaxb2- rich-contract-plugin 将为您提供帮助。它生成一个深层构建器模式,您可以将构建器链接在一起,并使用“end()”方法完成构建嵌套对象并返回到其父级的构建器上下文。
然而,为给定的 Java 类手动编写这个似乎有点乏味......
If you generate code from an XML schema with JAXB, the "fluent-builder" plugin from jaxb2-rich-contract-plugin will help you. It generates a deep builder pattern where you can chain builders together, and use an "end()" method to finish building a nested object and return to the builder context of its parent.
However, writing this by hand for a given Java class seems a bit tedious...