有什么好方法可以“重新实现”不流畅的界面?

时间:2017-02-11 11:39:37

标签: c++ fluent-interface

我正在寻找的是,有不流利的课程:

class NonFluent {
    int i=0;
public:
    void setValue(int i) {this->i = i;}
    void multiplyValue(int i) {this->i *= i;}
    int getValue() {return this->i;}
};

我想更改void方法以实际返回对*this的引用。我知道导出是不可能的,因为我们不能只改变返回类型,因为C ++将无法区分函数调用。

我们可以使用组合:

class Fluent {
    Fluent& setValue(int i) {var.setValue(i); return *this;}
    Fluent& multiplyValue(int i) {var.multiplyValue(i); return *this;}
    int getValue() {return var.getValue();}

private:
    NonFluent var;
};

但如果有许多void方法可以开始,那就太痛苦了。 我们也可以使用对象编辑器,我问过以下问题: Is Object Editor a good approach if there are multiple member functions to call?,但它有许多缺点。

你知道有什么好办法吗? (不直接改变非流利课程?)

3 个答案:

答案 0 :(得分:2)

您可以使用Fluent但不能使用NonFluent的更好方法是使您的类不可变,并使其方法返回带有结果的新对象修改:

class Fluent {
    static Fluent withValue(int i) {
        NonFluent v;
        v.setValue(i);
        return Fluent(v);
    }
    Fluent multiplyValue(int i) const {
        Fluent res(var);
        res.var.multiplyValue(i);
        return res;
    }
    int getValue() const {return var.getValue;}
private:
    Fluent(const NonFluent& v) : var(v) {}
    NonFluent var;
};

请注意创建Fluent对象的静态工厂方法。

使用工厂的代码如下所示:

int res = Fluent
    .withValue(5)
    .multiplyValue(2)
    .getValue();

这使您有机会重新编写API以包含其他流畅对象的方法,如下所示:

Fluent multiply(const Fluent& other) const {
    Fluent res(var);
    res.var.multiplyValue(other.getValue());
    return res;
}

总体结果是您的API变得兼容并发,而不会更改原始NonFluent API中的任何内容。

答案 1 :(得分:1)

我想了一会儿,我能够为继承方法制定一些舒适的语法,其中流畅的方法将从f_开始。

这是一些宏观魔法:

#define FIFTHS(_1, _2, _3, _4, _5, NAME, ...) NAME
#define MODIFY_ARGS_1(_1_) _1_ _1
#define MODIFY_ARGS_2(_1_, _2_) _1_ _1, _2_ _2
#define MODIFY_ARGS_3(_1_, _2_, _3_) _1_ _1, _2_ _2, _3_ _3
#define MODIFY_ARGS_4(_1_, _2_, _3_, _4_) _1_ _1,  _2_ _2, _3_ _3, _4_ _4
#define MODIFY_ARGS_5(_1_, _2_, _3_, _4_, _5_) _1_ _1,  _2_ _2, _3_ _3, _4_ _4, _5_ _5
#define MODIFY_ARGS(...) FIFTHS(__VA_ARGS__, MODIFY_ARGS_5, MODIFY_ARGS_4, MODIFY_ARGS_3, MODIFY_ARGS_2, MODIFY_ARGS_1)(__VA_ARGS__)

#define SEQUENCE_1(_1_) _1
#define SEQUENCE_2(_1_, _2_) _1, _2
#define SEQUENCE_3(_1_, _2_, _3_) _1, _2, _3
#define SEQUENCE_4(_1_, _2_, _3_, _4_) _1,  _2, _3, _4
#define SEQUENCE_5(_1_, _2_, _3_, _4_, _5_) _1,  _2, _3, _4, _5
#define SEQUENCE(...) FIFTHS(__VA_ARGS__, SEQUENCE_5, SEQUENCE_4, SEQUENCE_3, SEQUENCE_2, SEQUENCE_1)(__VA_ARGS__)

#define FLUENTIZE_DERIVE() CONCATENATE(Fluent,CURRENT_BASE) : public CURRENT_BASE
#define FLUENTIZE_METHOD(name, ...) CONCATENATE(Fluent,CURRENT_BASE)& f_ ## name (MODIFY_ARGS(__VA_ARGS__)) {name(SEQUENCE(__VA_ARGS__)); return *this;}
#define FLUENTIZE_PROCEDURE(name) CONCATENATE(Fluent,CURRENT_BASE)& f_ ## name() {name(); return *this;}
#define FLUENTIZE_DFLT_CONSTRUCTOR(name) CONCATENATE(Fluent,CURRENT_BASE)() {}
#define FLUENTIZE_CONSTRUCTOR(...) CONCATENATE(Fluent,CURRENT_BASE)(MODIFY_ARGS(__VA_ARGS__)) : CURRENT_BASE(SEQUENCE(__VA_ARGS__)) {}

注意:_x_代表类型,_x代表参数名称。

现在,我们有非流利的课程:

class Simple {
    int i=0;   
public:
    Simple() : Simple(0) {}
    Simple(int i) {this->i = i;}
    void setValue(int i) {this->i = i;}
    void multiplyValue(int i) {this->i *= i;}
    void halve() {this->i /= 2;}
    int getValue() {return this->i;}

};

这就是如何重新创建流利的课程:

#define CURRENT_BASE Simple
class FLUENTIZE_DERIVE() {
public:
    FLUENTIZE_DFLT_CONSTRUCTOR()
    FLUENTIZE_CONSTRUCTOR(int)
    FLUENTIZE_METHOD(setValue, int)
    FLUENTIZE_METHOD(multiplyValue, int)
    FLUENTIZE_PROCEDURE(halve)
};
#undef CURRENT_BASE

有了这个,我们创建了一个名为FluentSimple的类,使用流畅的方法f_setValuef_multiplyValuef_halve。通过一些宏观魔法,我自动命名了函数参数(参数被命名为序列_1, _2, _3 ...,最多为_5进行测试)。请注意,我必须为不接受任何参数的方法创建另一个宏,因为我使用的宏技术无法处理空__VA_ARGS__宏。

现在,那就是如何使用流畅的类:

std::cout << 
FluentSimple()
    .f_setValue(10)
    .f_multiplyValue(10)
    .f_halve()
    .getValue() << std::endl;

另请注意,使用此构造,fluent类可以访问所有基类方法和一些功能,如Qt插槽和信号。

如果它有任何缺点,除了使用宏之外,请告诉我:)

答案 2 :(得分:0)

作为另一个答案,我采用了不同的方法,这导致了最低的代码添加:

编辑:

template <class T>
struct EditorImpl
{
    T* ptr;

    template <class F>
    EditorImpl& operator()(F&& f)
    {
        f(ptr);
        return *this;
    }

    T* yield() const {
        return ptr;
    }
};

template <class T>
EditorImpl<T> Editor(T* ptr) { return EditorImpl<T>{ptr}; }

template <class T>
EditorImpl<T> Editor(T& ref) { return EditorImpl<T>{&ref}; }

GET_MACRO(一切都达到100):

namespace detail {
#define GET_MACRO_1(_1, NAME, ...) NAME
#define GET_MACRO_2(_1, _2, NAME, ...) NAME
#define GET_MACRO_3(_1, _2, _3, NAME, ...) NAME
//...
#define GET_MACRO_98(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63, _64, _65, _66, _67, _68, _69, _70, _71, _72, _73, _74, _75, _76, _77, _78, _79, _80, _81, _82, _83, _84, _85, _86, _87, _88, _89, _90, _91, _92, _93, _94, _95, _96, _97, _98, NAME, ...) NAME
#define GET_MACRO_99(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63, _64, _65, _66, _67, _68, _69, _70, _71, _72, _73, _74, _75, _76, _77, _78, _79, _80, _81, _82, _83, _84, _85, _86, _87, _88, _89, _90, _91, _92, _93, _94, _95, _96, _97, _98, _99, NAME, ...) NAME
#define GET_MACRO_100(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63, _64, _65, _66, _67, _68, _69, _70, _71, _72, _73, _74, _75, _76, _77, _78, _79, _80, _81, _82, _83, _84, _85, _86, _87, _88, _89, _90, _91, _92, _93, _94, _95, _96, _97, _98, _99, _100, NAME, ...) NAME
}

生成lambda的宏,该lambda调用对象上的宏参数:

namespace detail {
#define E_1(_1)[](auto* p) {p -> _1; }
#define E_2(_1,_2)[](auto* p) {p -> _1; p -> _2; }
#define E_3(_1,_2,_3)[](auto* p) {p -> _1; p -> _2; p -> _3; }
//...    
#define E_98(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,_21,_22,_23,_24,_25,_26,_27,_28,_29,_30,_31,_32,_33,_34,_35,_36,_37,_38,_39,_40,_41,_42,_43,_44,_45,_46,_47,_48,_49,_50,_51,_52,_53,_54,_55,_56,_57,_58,_59,_60,_61,_62,_63,_64,_65,_66,_67,_68,_69,_70,_71,_72,_73,_74,_75,_76,_77,_78,_79,_80,_81,_82,_83,_84,_85,_86,_87,_88,_89,_90,_91,_92,_93,_94,_95,_96,_97,_98)[](auto* p) {p -> _1; p -> _2; p -> _3; p -> _4; p -> _5; p -> _6; p -> _7; p -> _8; p -> _9; p -> _10; p -> _11; p -> _12; p -> _13; p -> _14; p -> _15; p -> _16; p -> _17; p -> _18; p -> _19; p -> _20; p -> _21; p -> _22; p -> _23; p -> _24; p -> _25; p -> _26; p -> _27; p -> _28; p -> _29; p -> _30; p -> _31; p -> _32; p -> _33; p -> _34; p -> _35; p -> _36; p -> _37; p -> _38; p -> _39; p -> _40; p -> _41; p -> _42; p -> _43; p -> _44; p -> _45; p -> _46; p -> _47; p -> _48; p -> _49; p -> _50; p -> _51; p -> _52; p -> _53; p -> _54; p -> _55; p -> _56; p -> _57; p -> _58; p -> _59; p -> _60; p -> _61; p -> _62; p -> _63; p -> _64; p -> _65; p -> _66; p -> _67; p -> _68; p -> _69; p -> _70; p -> _71; p -> _72; p -> _73; p -> _74; p -> _75; p -> _76; p -> _77; p -> _78; p -> _79; p -> _80; p -> _81; p -> _82; p -> _83; p -> _84; p -> _85; p -> _86; p -> _87; p -> _88; p -> _89; p -> _90; p -> _91; p -> _92; p -> _93; p -> _94; p -> _95; p -> _96; p -> _97; p -> _98; }
#define E_99(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,_21,_22,_23,_24,_25,_26,_27,_28,_29,_30,_31,_32,_33,_34,_35,_36,_37,_38,_39,_40,_41,_42,_43,_44,_45,_46,_47,_48,_49,_50,_51,_52,_53,_54,_55,_56,_57,_58,_59,_60,_61,_62,_63,_64,_65,_66,_67,_68,_69,_70,_71,_72,_73,_74,_75,_76,_77,_78,_79,_80,_81,_82,_83,_84,_85,_86,_87,_88,_89,_90,_91,_92,_93,_94,_95,_96,_97,_98,_99)[](auto* p) {p -> _1; p -> _2; p -> _3; p -> _4; p -> _5; p -> _6; p -> _7; p -> _8; p -> _9; p -> _10; p -> _11; p -> _12; p -> _13; p -> _14; p -> _15; p -> _16; p -> _17; p -> _18; p -> _19; p -> _20; p -> _21; p -> _22; p -> _23; p -> _24; p -> _25; p -> _26; p -> _27; p -> _28; p -> _29; p -> _30; p -> _31; p -> _32; p -> _33; p -> _34; p -> _35; p -> _36; p -> _37; p -> _38; p -> _39; p -> _40; p -> _41; p -> _42; p -> _43; p -> _44; p -> _45; p -> _46; p -> _47; p -> _48; p -> _49; p -> _50; p -> _51; p -> _52; p -> _53; p -> _54; p -> _55; p -> _56; p -> _57; p -> _58; p -> _59; p -> _60; p -> _61; p -> _62; p -> _63; p -> _64; p -> _65; p -> _66; p -> _67; p -> _68; p -> _69; p -> _70; p -> _71; p -> _72; p -> _73; p -> _74; p -> _75; p -> _76; p -> _77; p -> _78; p -> _79; p -> _80; p -> _81; p -> _82; p -> _83; p -> _84; p -> _85; p -> _86; p -> _87; p -> _88; p -> _89; p -> _90; p -> _91; p -> _92; p -> _93; p -> _94; p -> _95; p -> _96; p -> _97; p -> _98; p -> _99; }
#define E_100(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,_21,_22,_23,_24,_25,_26,_27,_28,_29,_30,_31,_32,_33,_34,_35,_36,_37,_38,_39,_40,_41,_42,_43,_44,_45,_46,_47,_48,_49,_50,_51,_52,_53,_54,_55,_56,_57,_58,_59,_60,_61,_62,_63,_64,_65,_66,_67,_68,_69,_70,_71,_72,_73,_74,_75,_76,_77,_78,_79,_80,_81,_82,_83,_84,_85,_86,_87,_88,_89,_90,_91,_92,_93,_94,_95,_96,_97,_98,_99,_100)[](auto* p) {p -> _1; p -> _2; p -> _3; p -> _4; p -> _5; p -> _6; p -> _7; p -> _8; p -> _9; p -> _10; p -> _11; p -> _12; p -> _13; p -> _14; p -> _15; p -> _16; p -> _17; p -> _18; p -> _19; p -> _20; p -> _21; p -> _22; p -> _23; p -> _24; p -> _25; p -> _26; p -> _27; p -> _28; p -> _29; p -> _30; p -> _31; p -> _32; p -> _33; p -> _34; p -> _35; p -> _36; p -> _37; p -> _38; p -> _39; p -> _40; p -> _41; p -> _42; p -> _43; p -> _44; p -> _45; p -> _46; p -> _47; p -> _48; p -> _49; p -> _50; p -> _51; p -> _52; p -> _53; p -> _54; p -> _55; p -> _56; p -> _57; p -> _58; p -> _59; p -> _60; p -> _61; p -> _62; p -> _63; p -> _64; p -> _65; p -> _66; p -> _67; p -> _68; p -> _69; p -> _70; p -> _71; p -> _72; p -> _73; p -> _74; p -> _75; p -> _76; p -> _77; p -> _78; p -> _79; p -> _80; p -> _81; p -> _82; p -> _83; p -> _84; p -> _85; p -> _86; p -> _87; p -> _88; p -> _89; p -> _90; p -> _91; p -> _92; p -> _93; p -> _94; p -> _95; p -> _96; p -> _97; p -> _98; p -> _99; p -> _100; }
}

#define E_(...) GET_MACRO_100(__VA_ARGS__, E_100, E_99, E_98, E_97, E_96, E_95, E_94, E_93, E_92, E_91, E_90, E_89, E_88, E_87, E_86, E_85, E_84, E_83, E_82, E_81, E_80, E_79, E_78, E_77, E_76, E_75, E_74, E_73, E_72, E_71, E_70, E_69, E_68, E_67, E_66, E_65, E_64, E_63, E_62, E_61, E_60, E_59, E_58, E_57, E_56, E_55, E_54, E_53, E_52, E_51, E_50, E_49, E_48, E_47, E_46, E_45, E_44, E_43, E_42, E_41, E_40, E_39, E_38, E_37, E_36, E_35, E_34, E_33, E_32, E_31, E_30, E_29, E_28, E_27, E_26, E_25, E_24, E_23, E_22, E_21, E_20, E_19, E_18, E_17, E_16, E_15, E_14, E_13, E_12, E_11, E_10, E_9, E_8, E_7, E_6, E_5, E_4, E_3, E_2, E_1)(__VA_ARGS__)

最终宏:

namespace detail {
#define EDIT_0(_1 )Editor(_1).yield()
#define EDIT_1(_1,_2)Editor(_1)(E_(_2)).yield()
#define EDIT_2(_1,_2,_3)Editor(_1)(E_(_2,_3)).yield()
#define EDIT_3(_1,_2,_3,_4)Editor(_1)(E_(_2,_3,_4)).yield()
//...
#define EDIT_98(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,_21,_22,_23,_24,_25,_26,_27,_28,_29,_30,_31,_32,_33,_34,_35,_36,_37,_38,_39,_40,_41,_42,_43,_44,_45,_46,_47,_48,_49,_50,_51,_52,_53,_54,_55,_56,_57,_58,_59,_60,_61,_62,_63,_64,_65,_66,_67,_68,_69,_70,_71,_72,_73,_74,_75,_76,_77,_78,_79,_80,_81,_82,_83,_84,_85,_86,_87,_88,_89,_90,_91,_92,_93,_94,_95,_96,_97,_98,_99)Editor(_1)(E_(_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,_21,_22,_23,_24,_25,_26,_27,_28,_29,_30,_31,_32,_33,_34,_35,_36,_37,_38,_39,_40,_41,_42,_43,_44,_45,_46,_47,_48,_49,_50,_51,_52,_53,_54,_55,_56,_57,_58,_59,_60,_61,_62,_63,_64,_65,_66,_67,_68,_69,_70,_71,_72,_73,_74,_75,_76,_77,_78,_79,_80,_81,_82,_83,_84,_85,_86,_87,_88,_89,_90,_91,_92,_93,_94,_95,_96,_97,_98,_99)).yield()
#define EDIT_99(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,_21,_22,_23,_24,_25,_26,_27,_28,_29,_30,_31,_32,_33,_34,_35,_36,_37,_38,_39,_40,_41,_42,_43,_44,_45,_46,_47,_48,_49,_50,_51,_52,_53,_54,_55,_56,_57,_58,_59,_60,_61,_62,_63,_64,_65,_66,_67,_68,_69,_70,_71,_72,_73,_74,_75,_76,_77,_78,_79,_80,_81,_82,_83,_84,_85,_86,_87,_88,_89,_90,_91,_92,_93,_94,_95,_96,_97,_98,_99,_100)Editor(_1)(E_(_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,_21,_22,_23,_24,_25,_26,_27,_28,_29,_30,_31,_32,_33,_34,_35,_36,_37,_38,_39,_40,_41,_42,_43,_44,_45,_46,_47,_48,_49,_50,_51,_52,_53,_54,_55,_56,_57,_58,_59,_60,_61,_62,_63,_64,_65,_66,_67,_68,_69,_70,_71,_72,_73,_74,_75,_76,_77,_78,_79,_80,_81,_82,_83,_84,_85,_86,_87,_88,_89,_90,_91,_92,_93,_94,_95,_96,_97,_98,_99,_100)).yield()
}
#define EDIT(...) GET_MACRO_100(__VA_ARGS__, EDIT_99, EDIT_98, EDIT_97, EDIT_96, EDIT_95, EDIT_94, EDIT_93, EDIT_92, EDIT_91, EDIT_90, EDIT_89, EDIT_88, EDIT_87, EDIT_86, EDIT_85, EDIT_84, EDIT_83, EDIT_82, EDIT_81, EDIT_80, EDIT_79, EDIT_78, EDIT_77, EDIT_76, EDIT_75, EDIT_74, EDIT_73, EDIT_72, EDIT_71, EDIT_70, EDIT_69, EDIT_68, EDIT_67, EDIT_66, EDIT_65, EDIT_64, EDIT_63, EDIT_62, EDIT_61, EDIT_60, EDIT_59, EDIT_58, EDIT_57, EDIT_56, EDIT_55, EDIT_54, EDIT_53, EDIT_52, EDIT_51, EDIT_50, EDIT_49, EDIT_48, EDIT_47, EDIT_46, EDIT_45, EDIT_44, EDIT_43, EDIT_42, EDIT_41, EDIT_40, EDIT_39, EDIT_38, EDIT_37, EDIT_36, EDIT_35, EDIT_34, EDIT_33, EDIT_32, EDIT_31, EDIT_30, EDIT_29, EDIT_28, EDIT_27, EDIT_26, EDIT_25, EDIT_24, EDIT_23, EDIT_22, EDIT_21, EDIT_20, EDIT_19, EDIT_18, EDIT_17, EDIT_16, EDIT_15, EDIT_14, EDIT_13, EDIT_12, EDIT_11, EDIT_10, EDIT_9, EDIT_8, EDIT_7, EDIT_6, EDIT_5, EDIT_4, EDIT_3, EDIT_2, EDIT_1, EDIT_0)(__VA_ARGS__)

示例类:

class Simple {
    int i=0;
public:
    Simple() : Simple(0) {}
    Simple(int i) {this->i = i;}
    void setValue(int i) {this->i = i;}
    void multiplyValue(int i) {this->i *= i;}
    void halve() {this->i /= 2;}
    int getValue() {return this->i;}

};

在经历了所有这些疯狂之后,用法:

Simple* simple = EDIT(new Simple, setValue(50), multiplyValue(3));

这是我能做的最好的事情:)