C++中等效的Java Builder模式是什么?

4
这是一个非常有用的成语,我在读完《Effective Java》后一直在使用它。我一直在寻找C++中类似的东西,但几乎没有什么进展。GoF书中传统的构建器模式在我的情况下并不适用。因为这个对象只有一个非常复杂和混乱的构造函数。以下是Java的一个简单实现。
class ComplicatedObject {

    private String field1;
    private String field2;
    private int      field3;

    private ComplicatedObject(Builder builder) {

            this.field1 = builder.myField1;
            this.field2 = builder.myField2;
            this.field3 = builder.myField3;

    }

    public static class Builder {

            private String myField1 = "some default";
            private String myField2 = "some other default";
            private int           myField3 = -1;

            public Builder() { }
            public Builder field1(String val) {
                    this.myField1 = val;
                    return this;
            }
            public Builder field2(String val) {
                    this.myField2 = val;
                    return this;
            }
            public Builder field3(int val) {
                    this.myField3 = val;
                    return this;
            }
            public ComplicatedObject build() {
                    return new ComplicatedObject(this);
            }
    }
    public static void main(final String[] args) {
    //built like this

            ComplicatedObject obj = new ComplicatedObject.Builder().field1("blah").field2("lol").field3(4).build();

    }
}

1
你可以在C++中几乎不做任何修改就能实现这个(只需将Builder类的方法的返回类型设置为引用即可)。 - Matteo Italia
2
Mike,每年我们都会在去年构建的层之上再构建一层...但如果你仍然对此感兴趣,请查看这个链接 - http://en.wikipedia.org/wiki/Builder_pattern#C.2B.2B - user405725
1
@VladLazarenko 我最初也考虑过那种方法,但是当你不是在设计一组对象而只是一个具有许多用户可配置成员变量的对象时,通过函数链接构建对象的过程往往是最佳方法。 - Mike Lyons
1
@Vlad 强烈不同意 :) 构建器的理念主要是为了允许构造函数构建完整的对象。SomeClass s = new SomeClass(); s.setAttr1(..); s.setAttr2(..)等基本适用于普通数据对象,但在面向对象中则是一种丑恶现象。 - Miserable Variable
建造者模式的优点已经被充分记录(查找“telescoping constructor pattern”),但像任何东西一样,建造者模式可能并不适用于每种情况。如果您正在为像Google的Guava这样的流行库构建坚如磐石的API,则应使用它。Joshua Bloch在这篇文章中详细解释了其优点:http://www.drdobbs.com/jvm/creating-and-destroying-java-objects-par/208403883?pgno=2 - michaelok
显示剩余4条评论
2个回答

6
#include <iostream>
#include <string>
using namespace std;

class ComplicatedObject {
    public: class Builder {
            friend class ComplicatedObject;

            private: string myField1;
            private: string myField2;
            private: int    myField3;

            public: Builder()
                    : myField1("some default"),
                     myField2 ("some other default"),
                     myField3(-1)
            { }
            public: Builder& field1(const string& val) {
                    myField1 = val;
                    return *this;
            }
            public: Builder& field2(const string& val) {
                    myField2 = val;
                    return *this;
            }
            public: Builder& field3(int val) {
                    myField3 = val;
                    return *this;
            }
            public: ComplicatedObject build() {
                    return ComplicatedObject(*this);
            }
    };

    private: string field1;
    private: string field2;
    private: int      field3;

    private: ComplicatedObject(const Builder& builder) 
            :field1(builder.myField1),
            field2(builder.myField2),
            field3(builder.myField3)
    {}
};

int main(int argc, char** argv) {
    if (argc < 4) {
        std::cout << "not enough params.";
        return 1;
    }
    ComplicatedObject obj(ComplicatedObject::Builder().field1("blah").field2("lol").field3(4));

}

我进行了最小的更改,使其变成了C++,更快速、更安全。 http://ideone.com/sCH1V

6
不仅可以将其适配到C ++,而且这种习惯用法已经从C ++中适配出来。
我认为我第一次听说这个习惯用法是在Java出现之前。如果我没记错的话,Bjarne Stroustrup在C ++第二版中提到了这一点,作为解释为什么C ++不需要Smalltalk样式的命名参数。
我可能会搞错我的日期,但这在C ++中已经有大约15年的历史了。
编辑:似乎它首先在《C ++设计与演化》(6.5.1)中被描述为“命名函数参数”。

我并不相信建造者模式在任何语言中都是第一次被使用,但显然C++中的例子早于Java,并且已经被C++的设计者所推崇。 - Peter Lawrey
@Peter Lawrence:当然,这不是它们第一次被使用!但这是它们第一次被收集在一本书中并命名!GoF书经常提供参考文献,说明模式在哪里被发现和使用,例如在ET++库(C++库)或Taligent Inc.或PARC等地方。 - Angel O'Sphere
@Angel和Peter,GoF的建造者模式与当今流行的Builder模式并不完全相同;具体来说,它没有使用命名函数参数所使用的Fluent接口。GoF的东西实际上是无关的。并不是说在Stroustrup写有关它之前没有使用过Fluent接口。 - Miserable Variable
@Hemal Pandya,嗯,我不同意你关于“命名参数习惯用法”的观点。毕竟它们有完全不同的目标。这种习惯用法用于更清楚地表达参数的含义,并能够更改顺序。建造者模式将结构与构建过程分离。具有交换构建过程的部分或更好地变化构建部件的选项。模式总是比习惯用法高一个级别。因此,许多模式可能是相同习惯用法的变体/组合。 - Angel O'Sphere
根据Bloch的说法,Builder模式是GoF Builder模式的一种形式:“幸运的是,有第三种选择,它将可伸缩构造器模式的安全性与JavaBeans模式的可读性结合在一起。它是Builder模式的一种形式[Gamme95,p. 97]。” http://www.drdobbs.com/jvm/creating-and-destroying-java-objects-par/208403883?pgno=2 - michaelok
显示剩余5条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接