带有Fluent界面的C ++ Builder模式

时间:2018-05-19 21:26:11

标签: c++ c++11 c++builder builder fluent

我正在尝试使用流畅的界面实现构建器模式,以便在C ++中构建对象。我希望构建器遵循CRTP模式。 在Java中,我会做类似于下面的代码。我如何在C ++中做同样的事情?

以下是一些具有基类和派生类的java代码。派生类的构建器继承基类的构建器..

// 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();

2 个答案:

答案 0 :(得分:3)

这是在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();
}

这是一个示例,它只允许在rvalue引用上更改值(由构建器返回):

#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类返回的builder()类的解决方案。私有成员函数必须使用using语句指定,以便Proto可供公众使用。最后,build()函数返回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
}

答案 1 :(得分:0)

这种方法可能会激发出更好的效果,因此我认为应该共享。

首先使用构建器模式为要提供的成员创建一个类,将其称为成员类和不可变类以构建构建器类。

members类将用于:

builder类将从其继承。

builder类在其构造函数中接受它,以提供const成员的所有const值。

现在,我们要创建一个流畅的界面,用于在members类上设置成员变量。

出现冲突:要使构建器类成员const成为成员类,还需要使它们成为const。

但是流畅的构造要求一次给出一个参数的方法,理想情况下,是一种控制可以按什么顺序输入参数的方法。

示例:

我们有一个表示正在运行的进程的类,要构造它,我们需要知道:

1.(命令)执行什么命令

2.(模式)仅需要从stdout读取(读取模式),或者将其交互使用以要求对其stdin进行写入(写入模式)的功能。

3.(目标)标准输出应重定向到哪里? cout,文件还是管道?

为简单起见,所有参数均由字符串表示。

在每个提供的参数之后限制有效方法非常适合自动补全,但是这要求我们使用有效方法定义范围  以及在施工的每个阶段将过渡到什么范围。

也许依赖类型的名称空间会更好,但我想尽可能重用members类。

每个参数接口由具有用于提供构造函数参数的方法的类表示。 该方法将返回一个具有下一个接口作为其类型的对象,以提供下一个构造函数参数或完成的生成器对象。

我在所有构造阶段都重复使用同一对象,但是界面通过静态转换而改变。

我们首先创建在构造构建器类之前客户端将使用的最后一个接口,在本例中为(3)目标参数。 在此之后让我们命名:

struct Target : protected members_class
{
    builder_class havingTarget( const string& _target ) 
    {
        this->target = target;
        return builder_class ( *(this) )  ;
    }
};      

可以通过为其提供一个members_class对象来构造该构建器类,我们从members_class继承该对象,因此我们可以通过提供this指针来返回构造的构建器类。

在目标界面之前,我们具有用于设置mode参数的界面:

struct Mode : protected Target
{
    Target& inMode( const string& mode )
    {
        this->mode = mode;
        return static_cast<Target&>(*this);
    }
};  

Mode从目标继承而来,为了在提供mode参数后切换到目标接口,我们将this指针强制转换为目标接口。

最后一个Command界面:

struct  Command : protected Mode
{
    Mode& withCommand( const string& command )
    {
        this->command = command;
        return static_cast<Mode&>(*this);
    }
};

从mode继承并在接受命令参数后返回强制转换为mode类型的this指针。

但是我们有一个冲突,builder类使用memberers类来插入成员,我们希望它们成为const。 但是,构建器模式使用成员类的方式是每次提供一个参数。

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;

创建类的两种方式的两个别名:

 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成员创建了一个不可变的类。

我没有看到这种方法在其他任何地方有描述,因此我想分享它,因为它可能会为更好的方法提供灵感,但是我不能真正推荐它,并且我还没有在概念验证之外真正地测试或完善了代码。 / p>

#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