0

ストラテジーパターンと抽象ファクトリパターンを使用して、実行時にCalculatorクラスでさまざまなアルゴリズムを生成しています。

計算は、関係するタイプ間の関係に依存します。これが、「* Algorithm::calculate」をリレーションシップに関して一般的なメンバー関数テンプレートにした理由です。

ただし、既存のコードで完全に実装ベースのアルゴリズムがすでにあり、ジェネリックベースでもイテレーターベースでもないため、AbstractFactoryを使用して生成できるように、アルゴリズム階層に追加したいと思います。それがどのように動作するかを見てください。

実装ベースのアルゴリズムは、計算に関係するタイプのメンバー関数を使用して計算を実行します。この例では、RelationshipWithA :: target_typeメンバー関数を使用してType&のデータにアクセスし、「A」メンバー関数を使用してRelationshipWithA::a_のデータにアクセスします。

これは私がこれまでに思いついたものです(これは単なるモデルであり、Abstract FactoryとCalculatorクラスはありません):

#include <iostream>

class Result{}; 

class A {};  

class B {
    public: 
        void specific() const 
        { 
            std::cout << "B::specific()" << std::endl;
        };
};

class C : public B {};

class D {};

template<class Type>
class RelationshipWithA
{
    const A& a_; 

    const Type& t_;

    public: 
        typedef Type target_type; 

        RelationshipWithA (const A& a, const Type& t)
            :
                a_(a), 
                t_(t)

        {
            std::cout << "RelationshipWithA::ctor" << std::endl;
        }; 

        const A& a() const 
        {
            return a_; 
        }

        const Type& type() const
        {
            return t_;
        }
};

class DefaultAlgorithm 
{
    public:
        template <class Relationship>
        void calculate (Result& res, const Relationship& r)
        {
            std::cout << "DefaultAlgorithm::calculate" << std::endl;
            const A& a = r.a(); 
            const typename Relationship::target_type& t = r.type(); 
            // Default iterator based calculation on a, target_type and r
        };
};

class AlternativeAlgorithm 
: 
    public DefaultAlgorithm 
{
    public:
        template <class Relationship>
        void calculate (Result& res, const Relationship& r)
        {
            std::cout << "AlternativeAlgorithm::calculate" << std::endl;
            // Optimized iterator based calculation on a, target_type and r
        }
};

class ImplementationBasedAlgorithm 
:
    public DefaultAlgorithm 
{
    public:
        // No specialization: Relationships store
        // a const reference to any class that inherits from B
        template <class Relationship>
        void calculate (Result& res, const Relationship& r)
        {
            // Use B implementation and the Relationship With  A to compute the result
            std::cout << "ImplementationBasedAlgorithm::calculate" << std::endl;
            const A& a = r.a(); 
            const B& b = r.type();
            b.specific();
            // Implementation based on B implementation
        }
};

int main(int argc, const char *argv[])
{
    Result res; 

    A a; 
    C c; 

    RelationshipWithA<C> relationshipAC (a, c);

    DefaultAlgorithm defaultAlg; 
    AlternativeAlgorithm alternativeAlg;
    ImplementationBasedAlgorithm implementationAlg;

    defaultAlg.calculate(res, relationshipAC);
    alternativeAlg.calculate(res, relationshipAC);
    implementationAlg.calculate(res,relationshipAC);

    D d; 
    RelationshipWithA<D> relationshipAD (a, d);

    defaultAlg.calculate(res, relationshipAD);
    alternativeAlg.calculate(res, relationshipAD);
    // This fails, as expected
    //implementationAlg.calculate(res,relationshipAD);

    return 0;
}

アルゴリズムはジェネリッククラスではないため、この設計が気に入っています。これにより、GenericAbstractFactoryが実行時にアルゴリズムを簡単に作成できるようになります。

ただし、Effective C ++には、「継承された非仮想関数を再定義しないでください」という項目36があります。つまり、非仮想関数は実装に不変であり、一般的にオーバーライドするべきではありませんが、次のようになります。

  1. C++で使用できる仮想メンバー関数テンプレートはありません。
  2. アルゴリズムクラスをRelationshipWithAでジェネリックにし、「* Algorithm ::calculate」を仮想メンバー関数にすると、ファクトリはアルゴリズムを生成するためにRealtionshipについて知る必要があり、コードは(少なくとも私にとっては)ひどく臭くなります。

継承された非仮想関数(関数テンプレート)をオーバーライドしても、これは問題の適切な解決策ですか?

クライアントにとって、これまでのところ動作に違いはありません。結果はそこにあり、唯一の違いは計算方法にあります。これは、Is-A関係が引き続き維持されていることを意味します。「* Algorithm::calculate」は引き続きクライアントに対して実装不変です。

4

1 に答える 1

1

それは実際にはIs-Aの関係ではありません...

特定の実装は実際にはDefaultAlgorithmではありません...それらは特定のアルゴリズムです...

BaseAlgorithmファクトリで作成できる空のクラスを持つことができます。ただし、テンプレート関数を使用する前に、とにかく正しいタイプにキャストする必要があります。インターフェイスを使用していないので、これはとにかくファクトリパターンを打ち負かします。

あなたの場合、ファクトリが派生クラスの1つを作成し、基本クラスを返す場合、その変数を使用すると、基本クラスのメソッドが呼び出されます。

DefaultAlgorithm algo = Factory.CreateImplementationBasedAlgorithm();
RelationshipWithA<D> relationshipAD (a, d);
algo.calculate(res, relationshipAD); //won't fail because the base class methods are used (because it isn't virtual)

これを修正するには、基本Relationshipクラスを作成し、calculate()メソッドを仮想化することができます。
これにより、 calculate()メソッドが取得され、そのアルゴリズムに必要なインターフェイスを持つbase_relationshipインターフェイスにリレーションシップをstatic_castできるため、適切なa()またはtype()メソッドがないためにコンパイルの失敗を達成できます。

于 2013-03-02T13:35:47.810 に答える