I have a class hierarchy in which every class has some defined properties.
I want to use builder pattern for all of these classes. Since the classes
are set up in a hierachy, ideally I would do the same with the builders.
The build
function can validate its parts before constructing the concrete
object. This guarantees that all necessary invariants are met.
Basic design:
- Every class has private members for storage and provide getters for those members.
- I want to avoid people from constructing these classes directly, so the constructor has protected visibility.
- The builders must have access to the constructor also hence they can be declared as a friend.
- Use a macro to define storge & setter, and use that in the builder class.
Note that the builders do not form a hierarchy because setters cannot be inherited properly as the setter method of a sub-builder that is inherited froma parent builder must return the sub-builder class rather than parent builder class.
Pros
- Easy to use since I can chain setters and the
build
method
Cons
- Hard to maintain because every sub builder must repeat the definition of parent fields
- A macro is used to ease the pain of duplicate code but it is less idiomatic in C++
#include <stdexcept>
// forward decl
class BaseBuidler;
class SubBuilder;
// immutable objects
class Base {
public:
int get_x() { return x_; }
int get_y() { return y_; }
protected:
friend class BaseBuilder;
Base(int x, int y) : x_(x), y_(y) {}
private:
int x_;
int y_;
};
class Sub : public Base {
public:
int get_z() { return z_; }
protected:
friend class SubBuilder;
Sub(int x, int y, int z) : Base(x, y), z_(z) {}
private:
int z_;
};
// builders
#define PROP(B, T, v) \
private: \
T v##_; \
public: \
B set_##v(T val) { v##_ = val; return *this; } \
private: \
class BaseBuilder {
PROP(BaseBuilder, int, x)
PROP(BaseBuilder, int, y)
public:
Base build() {
return Base(x_, y_);
}
};
class SubBuilder {
PROP(SubBuilder, int, x); // dup from BaseBuilder
PROP(SubBuilder, int, y); // dup from BaseBuilder
PROP(SubBuilder, int, z);
public:
Sub build() {
return Sub(x_, y_, z_);
}
};
int main() {
Base b = BaseBuilder().set_x(10).set_y(10).build();
Sub s = SubBuilder().set_x(1).set_y(2).set_z(1).build();
return s.get_z();
}
The setters for the builder class here returns void rather than the builder class itself.
Pros
- No need to use macro as there is no duplicate code anymore
Cons
- The code is more verbose since the user cannot chain the setters and build method.
// use builders
#include <stdexcept>
// forward decl
class BaseBuidler;
class SubBuilder;
// immutable objects
class Base {
public:
int get_x() { return x_; }
int get_y() { return y_; }
protected:
friend class BaseBuilder;
Base(int x, int y) : x_(x), y_(y) {}
private:
int x_;
int y_;
};
class Sub : public Base {
public:
int get_z() { return z_; }
protected:
friend class SubBuilder;
Sub(int x, int y, int z) : Base(x, y), z_(z) {}
private:
int z_;
};
// Builders
class BaseBuilder {
public:
void set_x(int x) { x_ = x; }
void set_y(int y) { y_ = y; }
Base build() {
return Base{x_, y_};
}
protected:
int x_;
int y_;
};
class SubBuilder : public BaseBuilder {
public:
void set_z(int z) { z_ = z; }
Sub build() {
return Sub(x_, y_, z_);
}
protected:
int z_;
};
int main() {
BaseBuilder builder;
builder.set_x(1);
builder.set_y(2);
Base b = builder.build();
SubBuilder sb;
sb.set_x(1);
sb.set_y(2);
sb.set_z(10);
Sub s = sb.build();
return s.get_z();
}