在基于策略的设计中多次使用策略

multiple use of policies in policy-based design

本文关键字:策略 于策略      更新时间:2023-10-16

我有一个类模板roundtrip它采用两个策略。 只要它们不同,一切都很好,但是使用一个策略两次会导致编译错误。

例:

#include <iostream>
class walk {
protected:
    void move() {
        std::cout<<"i'm walking."<<std::endl;
    }
};
class car {
protected:
    void move() {
        std::cout<<"i'm driving in a car."<<std::endl;
    }
};
template<typename S, typename T>
class roundtrip : private S, private T {
public:
    void printSchedule(void) {
        std::cout<<"away: ";
        S::move();
        std::cout<<"return: ";
        T::move();
    }
};
int main(void){
    roundtrip<walk,car> LazyTrip;
    LazyTrip.printSchedule();
    roundtrip<car,car> VeryLazyTrip; // ERROR: error: duplicate base type ‘walk’ invalid
    VeryLazyTrip.printSchedule();
    return 0;
}

如何解决这个问题?或者有更好的设计来实现相同的行为?

编辑:我在策略中添加了包装器,用户界面没有改变。 您如何看待这个解决方案,它是一个干净的设计吗?

template<typename T>
class outbound : private T {
protected:
    void moveOutbound(void) {
        T::move();
    }
};
template<typename T>
class inbound : private T {
protected:
    void moveInbound(void) {
        T::move();
    }
};
template<typename S, typename T>
class roundtrip : private outbound<S>, private inbound<T> {
public:
    void printSchedule(void) {
        std::cout<<"away: ";
        this->moveOutbound();
        std::cout<<"return: ";
        this->moveInbound();
    }
};

您可以将数据成员添加到 roundtrip 中,而不是继承,但 walkcar 中的函数当前protected 。如果无法将walkcar中的函数设为公开,则可以:

  • roundtrip模板交朋友

    class walk {
    protected:
        void move() {
            std::cout<<"i'm walking."<<std::endl;
        }
        template<class T, class S>
        friend class roundtrip;
    };
    
  • 使成员函数可通过帮助程序类roundtrip访问:

    template<typename S, typename T>
    class roundtrip {
    private:
        struct helperT : private T
        {
        public: // or private + friend roundtrip
            using T::move;
            using T::T;
        } mT;
        struct helperS : private S
        {
        public:
            using S::move;
            using S::S;
        } mS;
    public:
        void printSchedule(void) {
            std::cout<<"away: ";
            mT.move();
            std::cout<<"return: ";
            mS.move();
        }
    };
    

    如果两种类型的接口相同,则可以使用帮助程序类模板而不是两个帮助程序类:

    template<typename S, typename T>
    class roundtrip {
    private:
        template<class U>
        struct helper : private U
        {
        public: // or private + friend roundtrip
            using U::move;
            using U::U;
        };
        helper<S> mS;
        helper<T> mT;
    public:
        void printSchedule(void) {
            std::cout<<"away: ";
            mT.move();
            std::cout<<"return: ";
            mS.move();
        }
    };
    

    (也可以从帮助程序模板专用化继承,例如 class roundtrip : helperT<T>, helperS<S>

正如 DyP 所注意到的,这就足够了:

template<typename S, typename T>
class roundtrip {
    public:
    void printSchedule(void) {
        std::cout<<"away: ";
        awayPolicy.move();
        std::cout<<"return: ";
        returnPolicy.move();
    }
    private:
    T awayPolicy;
    S returnPolicy;
};

但这需要使move方法public。您还可以将它们设为staticpublic,因此您不需要实例字段:

class walk {
public:
    static void move() {
    std::cout<<"i'm walking."<<std::endl;
    }
};
// the same for car
template<typename S, typename T>
class roundtrip {
    public:
    void printSchedule(void) {
        std::cout<<"away: ";
        S::move();
        std::cout<<"return: ";
        T::move();
    }
};

您还可以考虑创建一个基类:

class travelPolicy
{
public:
    virtual void move() = 0;        
    //...
};

从中衍生出carwalk。然后,roundtrip类可以通过构造函数接受这两个策略,并通过指针在printSchedule中使用它们。

class roundtrip 
{
public:
    roundtrip(
        std::shared_ptr<travelpolicy> awayPolicy,
        std::shared_ptr<travelpolicy> returnPolicy)
    {
        this->awayPolicy = awayPolicy;
        this->returnPolicy = returnPolicy;
    }

    void printSchedule(void) 
    {
        std::cout<<"away: ";
        awayPolicy->move();
        std::cout<<"return: ";
        returnPolicy->move();
    }
private:
    std::shared_ptr<travelPolicy> awayPolicy;
    std::shared_ptr<travelPolicy> returnPolicy;
};

更新(回应OP的编辑):

一般来说,您的解决方案很好,但是似乎仍然过度使用继承。当A继承B时,这是一种说BA的方式。这里肯定不是这样。私人继承"黑客"使这种尴尬变得无形,这就是为什么它对我来说似乎是可以接受的。

为什么不专门研究相同基类的情况呢?如果可以的话,我使用了boost的mpl::if_而不是我的和c ++ 11或boost的type_trait的is_same。我手边没有编译器,所以下面可能存在一些语法问题

#include <type_traits>
template<typename S, typename T>
class roundtrip_diff : private S, private T {
public:
    void printSchedule(void) {
        std::cout<<"away: ";
        S::move();
        std::cout<<"return: ";
        T::move();
    }
};
template<typename S>
class roundtrip_same : private S {
public:
    void printSchedule(void) {
        std::cout<<"away: ";
        S::move();
        std::cout<<"return: ";
        S::move();
    }
};
template<bool, typename True, typename False> struct if_{ typedef True type; };
template<typename True, typename False> struct if_<false,True,False> { typedef False type; };
template<typename A, typename B> struct is_same { enum{value=0}; };
template<typename A> struct is_same<A,A> { enum{value=1}; };
template<typename S, typename T>
class roundtrip : if_< is_same<S,T>::value, roundtrip_same<S>, roundtrip_diff<S,T> >::type { };

显然,当您允许任意数量的参数时,需要找到更优雅的解决方案,但我认为这应该消除重复基础的错误。