具有 Fluent 界面的 C++ Builder 模式
C++ Builder pattern with Fluent interface
我正在尝试使用流畅的界面实现构建器模式,以便在 C++ 中构建对象。我希望构建器遵循 CRTP 模式。
在 Java 中,我将执行类似于以下代码的操作。我如何在 C++ 中做同样的事情?
下面是一些 java 代码,它有一个基础 class 和一个派生 class。派生 class 的构建器继承了基础 class 的构建器..
// Base class
public abstract class BaseClass {
private final int base_class_variable;
BaseClass(final Builder <?> builder) {
this.base_class_variable = builder.base_class_variable;
}
public abstract static class Builder <B extends Builder> {
int base_class_variable;
public B setBaseClassVariable(final int variable) {
this.base_class_variable = variable;
return self();
}
protected abstract B self();
}
}
// Derived class
public final class DerivedClass extends BaseClass {
private final int derived_class_variable;
private DerivedClass(final Builder builder) {
super(builder);
this.derived_class_variable = derived_class_variable;
}
public static Builder builder() {
return new Builder();
}
public static final class Builder extends BaseClass.Builder <Builder> {
private int derived_class_variable;
public Builder setDerivedClassVariable(final int variable) {
this.derived_class_variable = variable;
return self();
}
public DerivedClass build() {
return new DerivedClass(this);
}
@Override
protected Builder self() {
return this;
}
}
}
// Creating an instance of DerivedClass
DerivedClass dInstance = DerivedClass.builder()
.setBaseClassVariable(5)
.setDerivedClassVariable(10)
.build();
这是一种用 C++ 实现的方法:
template <typename T>
class Builder {
public:
static T builder() { return {}; }
T & build() {return static_cast<T&>(*this); }
};
template <typename T>
class BaseClass : public Builder<T> {
int base_class_variable;
public:
T& setBaseClassVariable(int variable) {
base_class_variable = variable;
return static_cast<T&>(*this);
}
};
class DerivedClass : public BaseClass<DerivedClass> {
int derived_class_variable;
public:
DerivedClass& setDerivedClassVariable(int variable) {
derived_class_variable = variable;
return *this;
}
};
int main()
{
// Creating an instance of DerivedClass
DerivedClass dInstance = DerivedClass::builder()
.setBaseClassVariable(5)
.setDerivedClassVariable(10)
.build();
}
这是一个示例,它只允许在右值引用(由构建器返回)上更改值:
#include <utility>
template <typename T>
class Builder {
public:
static T builder() { return {}; }
T & build() {return static_cast<T&>(*this); }
};
template <typename T>
class BaseClass : public Builder<T> {
int base_class_variable;
public:
T&& setBaseClassVariable(int variable) && {
base_class_variable = variable;
return std::move(static_cast<T&>(*this));
}
};
class DerivedClass : public BaseClass<DerivedClass> {
int derived_class_variable;
public:
DerivedClass&& setDerivedClassVariable(int variable) && {
derived_class_variable = variable;
return std::move(*this);
}
};
int main()
{
// Creating an instance of DerivedClass
DerivedClass dInstance = DerivedClass::builder()
.setBaseClassVariable(5)
.setDerivedClassVariable(10)
.build();
//dInstance.setBaseClassVariable(34); // will not compile
}
这是第三个解决方案,它使用 Proto
class,它由 builder()
返回。必须使用 using
语句指定私有成员函数,以便它们可供 Proto
使用 public。最后 build()
函数 returns DerivedClass
不公开成员函数。
template<typename T>
class BaseClass;
class DerivedClass;
template <typename T>
class Proto : public T {
public:
using BaseClass<T>::setBaseClassVariable;
using T::setDerivedClassVariable;
};
template <typename T>
class Builder {
public:
static Proto<T> builder() { return {}; }
T& build() { return static_cast<T&>(*this); }
};
template <typename T>
class BaseClass : public Builder<T> {
int base_class_variable;
Proto<T>& setBaseClassVariable(int variable) {
base_class_variable = variable;
return static_cast<Proto<T>&>(*this);
}
friend class Proto<T>;
};
class DerivedClass : public BaseClass<DerivedClass> {
int derived_class_variable;
Proto<DerivedClass>& setDerivedClassVariable(int variable) {
derived_class_variable = variable;
return static_cast<Proto<DerivedClass>&>(*this);
}
friend class Proto<DerivedClass>;
};
int main()
{
// Creating an instance of DerivedClass
DerivedClass dInstance = DerivedClass::builder()
.setBaseClassVariable(5)
.setDerivedClassVariable(10)
.build();
//dInstance.setBaseClassVariable(34); // cannot access private member
}
这种方法可能会激发出更好的灵感,所以我认为应该分享它。
首先使用构建器模式为您要提供的成员创建一个 class,我们将其称为成员 class 和不可变的 class 以构建构建器 class.
成员 class 将用于:
构建器 class 将从它继承。
构建器 class 在其构造函数中接受它,用于为 const 成员提供所有 const 值。
现在我们要创建一个流畅的界面来设置成员的成员变量class。
出现冲突:要使构建器 class 成员为常量,成员 class 也需要让它们为常量。
但是流畅的构造需要一种一次给出参数的方法,并且理想情况下需要一种控制可以给出参数的顺序的方法。
示例:
我们有一个 class 代表一个 运行 进程,要构建它我们需要知道:
1.(命令)执行什么命令
2.(模式) 只需要从 stdout 读取(读取模式)还是交互式使用需要写入其 stdin 的能力(写入模式)。
3.(目标)stdout应该重定向到哪里? cout,文件还是管道?
为简单起见,所有参数都将用字符串表示。
在每个提供的参数之后限制有效方法对于自动完成非常有用,但它需要我们用有效方法定义一个范围
以及它将过渡到什么范围-对于构建的每个阶段。
也许依赖于类型的命名空间会更好,但我想尽可能重用成员 class。
每个参数接口都由 class 表示,带有提供构造函数参数的方法。
该方法将 return 具有下一个接口的对象作为其类型,用于提供下一个构造函数参数或完成的构建器对象。
我在所有构造阶段重复使用同一个对象,但界面因静态转换而改变。
我们首先创建客户端将在构建器 class 构建之前使用的最后一个接口,在本例中为 (3) 目标参数。
让我们命名如果在那之后:
struct Target : protected members_class
{
builder_class havingTarget( const string& _target )
{
this->target = target;
return builder_class ( *(this) ) ;
}
};
构建器 class 可以通过给它一个 members_class 对象来构造,我们从 members_class 继承,所以我们可以 return 构建一个构建器 class 通过提供 this 指针。
在目标界面之前我们有设置模式参数的界面:
struct Mode : protected Target
{
Target& inMode( const string& mode )
{
this->mode = mode;
return static_cast<Target&>(*this);
}
};
Mode 继承自 target,为了在提供 mode 参数后切换到目标接口,我们将 this 指针转换为目标接口。
最后一个命令界面:
struct Command : protected Mode
{
Mode& withCommand( const string& command )
{
this->command = command;
return static_cast<Mode&>(*this);
}
};
从模式继承并return在获取命令参数后将 this 指针转换为模式类型。
但是我们有一个冲突,成员 class 被构建器 class 用于继承成员,我们希望它们是常量。
但是构建器模式以一种每次提供一个参数的方式使用成员 class。
struct members_class
{
string target;
string mode;
string command;
};
首先让我们启用一种方法来提供模板参数,该参数将决定成员是否为常量:
template <typename T>
using noop = T;
template< template <typename> class constner = noop >
struct members_dyn_const
默认情况下,参数是空操作,但如果提供 std::remove_const_t,则成员将不是 const,因为它们声明如下:
constner<const string> target;
constner<const string> mode;
constner<const string> command;
两种创建方法的别名 class:
using members = members_dyn_const<>;
using members_mutable = members_dyn_const<std::remove_const_t>;
现在我们想要使用可变成员 class 构建常量成员 class:
template< template <typename> class C>
members_dyn_const( members_dyn_const<C> m) : target(m.target), mode(m.mode), command(m.command){}
但我们还需要为成员定义默认值,当它被构造为可变时class:
members_dyn_const () : target(""), mode(""), command(""){}
现在我们定义构建器 class 从 const 成员 class 继承,但接受可变成员 class 来构造 const:
class base_process : protected members
{
public:
base_process( members_mutable _members ) : members( _members ) {}
现在我们可以构建一个构建器 class 具有:
process_builder.withCommand( "ls" ).inMode( "read" ).havingTarget( "cout" );
并且使用 const 成员创建了一个不可变 class。
我没有在其他任何地方看到过这种方法的描述,所以我想分享它,因为它可能会为更好的方法提供灵感,但我不能真正推荐它,而且我还没有真正测试或完善代码超出概念证明.
#include <string>
#include <iostream>
using namespace std;
namespace process
{
namespace details
{
template <typename T>
using noop = T;
template< template <typename> class constner = noop >
struct members_dyn_const
{
friend class members_dyn_const< noop >;
template< template <typename> class C>
members_dyn_const( members_dyn_const<C> m) : target(m.target), mode(m.mode), command(m.command){}
members_dyn_const () : target(""), mode(""), command(""){}
protected:
constner<const string> target;
constner<const string> mode;
constner<const string> command;
};
using members = members_dyn_const<>;
using members_mutable = members_dyn_const<std::remove_const_t>;
namespace builder
{
class base_process : protected members
{
public:
base_process( members_mutable _members ) : members( _members ) {}
void test() { /*command = "X";*/ cout << "Executing command: " << command << " in mode " << mode << " having target " << target << endl; }
};
namespace arguments
{
struct Target : protected members_mutable
{
base_process havingTarget( const string& _target )
{
this->target = target;
return base_process( *(this) ) ;
}
};
struct Mode : protected Target
{
auto& inMode( const string& mode )
{
this->mode = mode;
return static_cast<Target&>(*this);
}
};
struct Command : protected Mode
{
Mode& withCommand( const string& command )
{
this->command = command;
return static_cast<Mode&>(*this);
}
};
}
}
}
using details::builder::base_process;
using details::builder::arguments::Command;
Command process_builder = Command();
}
using namespace process;
int main()
try
{
process_builder.withCommand( "ls" ).inMode( "read" ).havingTarget( "cout" ).test();
return 0;
}
catch( exception& e )
{
cout << "ERROR:" << e.what() << endl;
return -1;
}
我正在尝试使用流畅的界面实现构建器模式,以便在 C++ 中构建对象。我希望构建器遵循 CRTP 模式。 在 Java 中,我将执行类似于以下代码的操作。我如何在 C++ 中做同样的事情?
下面是一些 java 代码,它有一个基础 class 和一个派生 class。派生 class 的构建器继承了基础 class 的构建器..
// Base class
public abstract class BaseClass {
private final int base_class_variable;
BaseClass(final Builder <?> builder) {
this.base_class_variable = builder.base_class_variable;
}
public abstract static class Builder <B extends Builder> {
int base_class_variable;
public B setBaseClassVariable(final int variable) {
this.base_class_variable = variable;
return self();
}
protected abstract B self();
}
}
// Derived class
public final class DerivedClass extends BaseClass {
private final int derived_class_variable;
private DerivedClass(final Builder builder) {
super(builder);
this.derived_class_variable = derived_class_variable;
}
public static Builder builder() {
return new Builder();
}
public static final class Builder extends BaseClass.Builder <Builder> {
private int derived_class_variable;
public Builder setDerivedClassVariable(final int variable) {
this.derived_class_variable = variable;
return self();
}
public DerivedClass build() {
return new DerivedClass(this);
}
@Override
protected Builder self() {
return this;
}
}
}
// Creating an instance of DerivedClass
DerivedClass dInstance = DerivedClass.builder()
.setBaseClassVariable(5)
.setDerivedClassVariable(10)
.build();
这是一种用 C++ 实现的方法:
template <typename T>
class Builder {
public:
static T builder() { return {}; }
T & build() {return static_cast<T&>(*this); }
};
template <typename T>
class BaseClass : public Builder<T> {
int base_class_variable;
public:
T& setBaseClassVariable(int variable) {
base_class_variable = variable;
return static_cast<T&>(*this);
}
};
class DerivedClass : public BaseClass<DerivedClass> {
int derived_class_variable;
public:
DerivedClass& setDerivedClassVariable(int variable) {
derived_class_variable = variable;
return *this;
}
};
int main()
{
// Creating an instance of DerivedClass
DerivedClass dInstance = DerivedClass::builder()
.setBaseClassVariable(5)
.setDerivedClassVariable(10)
.build();
}
这是一个示例,它只允许在右值引用(由构建器返回)上更改值:
#include <utility>
template <typename T>
class Builder {
public:
static T builder() { return {}; }
T & build() {return static_cast<T&>(*this); }
};
template <typename T>
class BaseClass : public Builder<T> {
int base_class_variable;
public:
T&& setBaseClassVariable(int variable) && {
base_class_variable = variable;
return std::move(static_cast<T&>(*this));
}
};
class DerivedClass : public BaseClass<DerivedClass> {
int derived_class_variable;
public:
DerivedClass&& setDerivedClassVariable(int variable) && {
derived_class_variable = variable;
return std::move(*this);
}
};
int main()
{
// Creating an instance of DerivedClass
DerivedClass dInstance = DerivedClass::builder()
.setBaseClassVariable(5)
.setDerivedClassVariable(10)
.build();
//dInstance.setBaseClassVariable(34); // will not compile
}
这是第三个解决方案,它使用 Proto
class,它由 builder()
返回。必须使用 using
语句指定私有成员函数,以便它们可供 Proto
使用 public。最后 build()
函数 returns DerivedClass
不公开成员函数。
template<typename T>
class BaseClass;
class DerivedClass;
template <typename T>
class Proto : public T {
public:
using BaseClass<T>::setBaseClassVariable;
using T::setDerivedClassVariable;
};
template <typename T>
class Builder {
public:
static Proto<T> builder() { return {}; }
T& build() { return static_cast<T&>(*this); }
};
template <typename T>
class BaseClass : public Builder<T> {
int base_class_variable;
Proto<T>& setBaseClassVariable(int variable) {
base_class_variable = variable;
return static_cast<Proto<T>&>(*this);
}
friend class Proto<T>;
};
class DerivedClass : public BaseClass<DerivedClass> {
int derived_class_variable;
Proto<DerivedClass>& setDerivedClassVariable(int variable) {
derived_class_variable = variable;
return static_cast<Proto<DerivedClass>&>(*this);
}
friend class Proto<DerivedClass>;
};
int main()
{
// Creating an instance of DerivedClass
DerivedClass dInstance = DerivedClass::builder()
.setBaseClassVariable(5)
.setDerivedClassVariable(10)
.build();
//dInstance.setBaseClassVariable(34); // cannot access private member
}
这种方法可能会激发出更好的灵感,所以我认为应该分享它。
首先使用构建器模式为您要提供的成员创建一个 class,我们将其称为成员 class 和不可变的 class 以构建构建器 class.
成员 class 将用于:
构建器 class 将从它继承。
构建器 class 在其构造函数中接受它,用于为 const 成员提供所有 const 值。
现在我们要创建一个流畅的界面来设置成员的成员变量class。
出现冲突:要使构建器 class 成员为常量,成员 class 也需要让它们为常量。
但是流畅的构造需要一种一次给出参数的方法,并且理想情况下需要一种控制可以给出参数的顺序的方法。
示例:
我们有一个 class 代表一个 运行 进程,要构建它我们需要知道:
1.(命令)执行什么命令
2.(模式) 只需要从 stdout 读取(读取模式)还是交互式使用需要写入其 stdin 的能力(写入模式)。
3.(目标)stdout应该重定向到哪里? cout,文件还是管道?
为简单起见,所有参数都将用字符串表示。
在每个提供的参数之后限制有效方法对于自动完成非常有用,但它需要我们用有效方法定义一个范围 以及它将过渡到什么范围-对于构建的每个阶段。
也许依赖于类型的命名空间会更好,但我想尽可能重用成员 class。
每个参数接口都由 class 表示,带有提供构造函数参数的方法。 该方法将 return 具有下一个接口的对象作为其类型,用于提供下一个构造函数参数或完成的构建器对象。
我在所有构造阶段重复使用同一个对象,但界面因静态转换而改变。
我们首先创建客户端将在构建器 class 构建之前使用的最后一个接口,在本例中为 (3) 目标参数。 让我们命名如果在那之后:
struct Target : protected members_class
{
builder_class havingTarget( const string& _target )
{
this->target = target;
return builder_class ( *(this) ) ;
}
};
构建器 class 可以通过给它一个 members_class 对象来构造,我们从 members_class 继承,所以我们可以 return 构建一个构建器 class 通过提供 this 指针。
在目标界面之前我们有设置模式参数的界面:
struct Mode : protected Target
{
Target& inMode( const string& mode )
{
this->mode = mode;
return static_cast<Target&>(*this);
}
};
Mode 继承自 target,为了在提供 mode 参数后切换到目标接口,我们将 this 指针转换为目标接口。
最后一个命令界面:
struct Command : protected Mode
{
Mode& withCommand( const string& command )
{
this->command = command;
return static_cast<Mode&>(*this);
}
};
从模式继承并return在获取命令参数后将 this 指针转换为模式类型。
但是我们有一个冲突,成员 class 被构建器 class 用于继承成员,我们希望它们是常量。 但是构建器模式以一种每次提供一个参数的方式使用成员 class。
struct members_class
{
string target;
string mode;
string command;
};
首先让我们启用一种方法来提供模板参数,该参数将决定成员是否为常量:
template <typename T>
using noop = T;
template< template <typename> class constner = noop >
struct members_dyn_const
默认情况下,参数是空操作,但如果提供 std::remove_const_t,则成员将不是 const,因为它们声明如下:
constner<const string> target;
constner<const string> mode;
constner<const string> command;
两种创建方法的别名 class:
using members = members_dyn_const<>;
using members_mutable = members_dyn_const<std::remove_const_t>;
现在我们想要使用可变成员 class 构建常量成员 class:
template< template <typename> class C>
members_dyn_const( members_dyn_const<C> m) : target(m.target), mode(m.mode), command(m.command){}
但我们还需要为成员定义默认值,当它被构造为可变时class:
members_dyn_const () : target(""), mode(""), command(""){}
现在我们定义构建器 class 从 const 成员 class 继承,但接受可变成员 class 来构造 const:
class base_process : protected members
{
public:
base_process( members_mutable _members ) : members( _members ) {}
现在我们可以构建一个构建器 class 具有:
process_builder.withCommand( "ls" ).inMode( "read" ).havingTarget( "cout" );
并且使用 const 成员创建了一个不可变 class。
我没有在其他任何地方看到过这种方法的描述,所以我想分享它,因为它可能会为更好的方法提供灵感,但我不能真正推荐它,而且我还没有真正测试或完善代码超出概念证明.
#include <string>
#include <iostream>
using namespace std;
namespace process
{
namespace details
{
template <typename T>
using noop = T;
template< template <typename> class constner = noop >
struct members_dyn_const
{
friend class members_dyn_const< noop >;
template< template <typename> class C>
members_dyn_const( members_dyn_const<C> m) : target(m.target), mode(m.mode), command(m.command){}
members_dyn_const () : target(""), mode(""), command(""){}
protected:
constner<const string> target;
constner<const string> mode;
constner<const string> command;
};
using members = members_dyn_const<>;
using members_mutable = members_dyn_const<std::remove_const_t>;
namespace builder
{
class base_process : protected members
{
public:
base_process( members_mutable _members ) : members( _members ) {}
void test() { /*command = "X";*/ cout << "Executing command: " << command << " in mode " << mode << " having target " << target << endl; }
};
namespace arguments
{
struct Target : protected members_mutable
{
base_process havingTarget( const string& _target )
{
this->target = target;
return base_process( *(this) ) ;
}
};
struct Mode : protected Target
{
auto& inMode( const string& mode )
{
this->mode = mode;
return static_cast<Target&>(*this);
}
};
struct Command : protected Mode
{
Mode& withCommand( const string& command )
{
this->command = command;
return static_cast<Mode&>(*this);
}
};
}
}
}
using details::builder::base_process;
using details::builder::arguments::Command;
Command process_builder = Command();
}
using namespace process;
int main()
try
{
process_builder.withCommand( "ls" ).inMode( "read" ).havingTarget( "cout" ).test();
return 0;
}
catch( exception& e )
{
cout << "ERROR:" << e.what() << endl;
return -1;
}