3

次のシナリオのシンプルでエレガントなデザインを見つけるのに苦労しています。クラス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;
}

テンプレートの引数がより複雑で、ワーカーの同じビジネス ドメインのものである場合、事態は複雑になります。ワーカーはヘルパーを使用する必要がありますが、(この設計では) ヘルパーが認識していないデータ オブジェクトのメソッドを呼び出す必要もあります。何かのようなもの:

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

明らかな問題は、結果に対して not const 関数を呼び出せないData()ことです。Data()非 const にすることは、さまざまなコンテキストでも使用されるため、悪い考えのように思えますHelper。私の目標はComplexData、メンバー関数を使用して変更するエレガントな方法を見つけることです。変更されたデータを引き続き使用できるように、最初に変更する必要がありComplexWorker ます。ComplexDataHelper

編集:Helper提供されたデータをコピーして実際の流れに似せるように構造を変更しました

4

3 に答える 3

1

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.

于 2012-08-14T11:24:11.963 に答える
0

STLでのコンテナ部分の実現を参考にしてみてはいかがでしょうか。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;    
}
于 2012-08-14T17:40:49.687 に答える
0

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();
    }        
}
于 2012-08-14T11:24:23.103 に答える