ストラテジーパターンと抽象ファクトリパターンを使用して、実行時に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があります。つまり、非仮想関数は実装に不変であり、一般的にオーバーライドするべきではありませんが、次のようになります。
- C++で使用できる仮想メンバー関数テンプレートはありません。
- アルゴリズムクラスをRelationshipWithAでジェネリックにし、「* Algorithm ::calculate」を仮想メンバー関数にすると、ファクトリはアルゴリズムを生成するためにRealtionshipについて知る必要があり、コードは(少なくとも私にとっては)ひどく臭くなります。
継承された非仮想関数(関数テンプレート)をオーバーライドしても、これは問題の適切な解決策ですか?
クライアントにとって、これまでのところ動作に違いはありません。結果はそこにあり、唯一の違いは計算方法にあります。これは、Is-A関係が引き続き維持されていることを意味します。「* Algorithm::calculate」は引き続きクライアントに対して実装不変です。