用于委派模板成员功能的良好设计

时间:2012-08-14 11:12:24

标签: c++ templates encapsulation

我在为以下场景寻找简单而优雅的设计时遇到了麻烦。类Worker使用模板类Helper来完成一些工作。在简单的场景中,它看起来像这样:

template<typename T>
class Helper {
  public:
    void Help(T data) : m_data(data) {} //NOTICE: Copy
    T const& Data() const { return m_data; }

    T m_data;
}

class SimpleWorker {
  public:
    SimpleWorker() : m_helper(SimpleData()) {} // non-temp used in reality
    void DoWork()
    {
        m_helper.help();
    }        

    Helper<SimpleData> m_helper;
}

当模板参数更复杂并且与工作者的业务域相同时,事情变得复杂。 worker需要使用帮助器,但是它还需要调用帮助程序甚至不知道的数据对象上的某些方法(在本设计中)。类似的东西:

template<typename T>
class Helper {
  public:
    Helper(T data) : m_data(data) {} //NOTICE: Copy
    T const& Data() const { return m_data; }

    T m_data;
}

class ComplexWorker {
  public:
    ComplexWorker() : m_helper(ComplexData()) {} // non-temp used in reality

    void DoWork()
    {
        m_helper.help();
        m_helper.GetData().DoSomethingComplexNotConst(); // <-------------
    }    

    Helper<ComplexData> m_helper;    
}

显而易见的问题是我无法在Data()结果上调用非const函数。使Data()非const看起来是个坏主意,因为Helper也用于不同的上下文。我的目标是找到一种优雅的方式来改变ComplexData使用ComplexWorker中的成员函数 首先需要更改ComplexData,以便Helper可以继续使用更改的数据。

编辑:更改Helper构造以复制提供的数据以更好地类似于实际流量

3 个答案:

答案 0 :(得分:1)

我认为最好让Helper只有静态函数,而不是维护状态(在你自己的代码中ComplexData()创建临时ComplexWorker)。根据您是否需要修改,通过引用或const引用传递数据。

// primary template
template<typename T>
class Helper {
public:
    static void help(T const& data) const {} // non-modifying
};

// specialization for ComplexData
template<>
class Helper<ComplexData> {
public:
    static void help(ComplexData const& data) const { } // non-modifying

    static void DoSomethingComplexNotConst(ComplexData& data) // modifying
    {
         // your implementation here
    }
};

class ComplexWorker {
public: 
    ComplexWorker() : m_data(ComplexData()) {} // create new data

    void DoWork()
    {
        Helper<ComplexData>::help(m_data);
        Helper<ComplexData>::DoSomethingComplexNotConst(m_data); // <--- now no problem
    }

   private:
       ComplexData m_data;         
};

请注意,我为ComplexData制作了模板专精help()中存在一些代码重复,但您可以轻松地将其提取到一个常见的非成员帮助函数中。

答案 1 :(得分:0)

在我看来,这取决于Helper实际上在做什么。你的例子只给出了一个构造函数和一个访问器,但我怀疑它是否在实践中完成。

你考虑过简单地使用继承吗?您的Helper模板将如下所示:

template<typename T>
class Helper : public T {
    Helper(T data) : T(data) {}
    void Help() {};
}

在这种情况下,您可以直接在'is-a'关系中使用Helper<ComplexData>对象:

class ComplexWorker {
    Helper<ComplexData> m_helper;

    void DoWork()
    {
        m_helper.help();
        m_helper.DoSomethingComplexNotConst();
    }        
}

答案 2 :(得分:0)

为什么不参考STL中Container部分的实现。 Data()函数的重载可以实现安全和优雅之间的平衡。

template <typename T>
class Helper {
public:
    Helper(T data) : m_data(data) {} //NOTICE: Copy
    T const& Data() const { return m_data; }
    T& Data() {return m_data; }
private:
    T m_data;
}

class ComplexWorker {
public:
    ComplexWorker() : m_helper(ComplexData()) {} // non-temp used in reality

    void DoWork()
    {
        m_helper.help();
        ComplexData &cd1 = m_helper.Data();
        cd1.QuerySth();
        const ComplexData &cd2 = m_helper.Data();
        cd2.ModifySth();
    }    
private:
    Helper<ComplexData> m_helper;    
}