Good design for delegating template-member functio

2019-07-07 03:03发布

I am having trouble in finding simple and elegant design for the following scenario. Class Worker uses template class Helper to do some work. In the simple scenario it looks like this:

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;
}

Things get complicated for me when the template argument is more complex and is of the same business domain of the worker. The worker needs to use the helper but it would also need to call some methods on the data object that the helper doesn't even know about (in this design). Something like:

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;    
}

The obvious problem is that I can't call not const function on Data() result. Making Data() non-const seems like a bad idea as Helper is used in different contexts as well. My goal is to find an elegant way to alter ComplexData using its member functions from ComplexWorker The ComplexData needs to be altered in the first place so that Helper can continue working with the altered data.

EDIT: Changed Helper construction to copy the provided data to better resemble actual flow

3条回答
叛逆
2楼-- · 2019-07-07 03:28

I think it's best to let Helper have only static functions, not maintaing state (as you create temporary ComplexData() in ComplexWorker in your own code). Pass the data by reference or const-reference depending on whether you need to modify or not.

// 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;         
};

Note that I made a template specialization for the ComplexData. There is some code duplication in help() but you can easily extract this into a common non-member helper function.

查看更多
ゆ 、 Hurt°
3楼-- · 2019-07-07 03:31

Why not refer to the realization of the Container part in STL. An overload of the Data() function may achieve a ballance between safty and elegance.

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;    
}
查看更多
做自己的国王
4楼-- · 2019-07-07 03:40

Seems to me that it depends on what Helper is actually doing. Your example just gives a constructor and an accessor but I doubt that is all it does in practice.

Have you considered simply using inheritance? Your Helper template would then look like this:

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

In this case you could use the Helper<ComplexData> object directly in an 'is-a' relationship:

class ComplexWorker {
    Helper<ComplexData> m_helper;

    void DoWork()
    {
        m_helper.help();
        m_helper.DoSomethingComplexNotConst();
    }        
}
查看更多
登录 后发表回答