这种方法可能会激发一些更好的东西,所以我认为应该分享它。
首先使用构建器模式为您要提供的成员创建一个类,我们将其称为成员类和不可变类来构建构建器类。
成员类将用于:
构建器类将从它继承。
builder 类在其构造函数中接受它,为 const 成员提供所有 const 值。
现在我们要创建一个流畅的接口来设置成员类的成员变量。
出现冲突:要使构建器类成员为 const,成员类也需要使它们为 const。
但是流畅的构造需要一种每次给出一个参数的方法,理想情况下是一种控制可以给出参数的顺序的方法。
例子:
我们有一个代表正在运行的进程的类,要构造它,我们需要知道:
1.(命令)执行什么命令
2.(模式)将只需要从标准输出读取(读取模式)还是交互使用它需要写入其标准输入的能力(写入模式)。
3.(目标)标准输出应该重定向到哪里? cout,文件还是管道?
为简单起见,所有参数都将由字符串表示。
在每个提供的参数之后限制有效方法对于自动完成非常有用,但它需要我们使用有效方法定义范围
以及它将过渡到什么范围 - 对于构建的每个阶段。
也许依赖类型的命名空间会更好,但我想尽可能重用成员类。
每个参数接口都由一个类表示,该类具有用于提供构造函数参数的方法。
该方法将返回一个对象,该对象具有下一个接口作为其类型,用于提供下一个构造函数参数或完成的构建器对象。
我在所有构造阶段都重用同一个对象,但接口通过静态转换改变。
我们首先创建客户端在构建构建器类之前将使用的最后一个接口,在本例中为 (3) 目标参数。
如果在那之后让我们命名:
struct Target : protected members_class
{
builder_class havingTarget( const string& _target )
{
this->target = target;
return builder_class ( *(this) ) ;
}
};
构建器类可以通过给它一个 members_class 对象来构建,我们从 members_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);
}
};
从模式继承并返回一个在获取命令参数后转换为模式类型的 this 指针。
但是我们有一个冲突,builder 类使用 members 类来继承成员,我们希望它们是 const。
但是构建器模式使用成员类的方式是每次提供一个参数。
struct members_class
{
string target;
string mode;
string command;
};
首先让我们启用一种提供模板参数的方法,该参数将决定成员是否为 const:
template <typename T>
using noop = T;
template< template <typename> class constner = noop >
struct members_dyn_const
默认情况下,参数是 no 操作,但如果提供 std::remove_const_t 成员将不是 const,因为它们是这样声明的:
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>;
现在我们要启用具有可变成员类的 const 成员类的构造:
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(""){}
现在我们定义继承自 const 成员类的构建器类,但接受可变成员类来构造 const:
class base_process : protected members
{
public:
base_process( members_mutable _members ) : members( _members ) {}
现在我们可以构造一个构建器类:
process_builder.withCommand( "ls" ).inMode( "read" ).havingTarget( "cout" );
一个不可变的类是用 const 成员创建的。
我还没有在其他任何地方看到过这种方法的描述,所以我想分享它,因为它可能会为更好的方法提供灵感,但我不能真正推荐它,而且除了概念证明之外,我还没有真正测试或完善代码。
#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;
}
https://onlinegdb.com/BySX9luim