1

この構造を変更せずに機能を追加したい場合:

class MemberBase
{
public:
    virtual MemberBase* clone() const; 
    virtual void action1(); 
    ... 
}

class Container
{
public:
    virtual void functionUsingAction1();
    ...
private:
    std::vector<MemberBase*> members_
}

MemberBaseをサブクラス化して

class MemberType1 : public MemberBase
{
public:
    MemberBase* clone() const;// Reimplementation
    void action1();           // Reimplementation
    virtual void action2();   // New functionality
}

を使用して、コンテナに目的のタイプのデータを入力できます。

MemberBase* MemberBase::clone() const;

方法。しかし、どうすればいいですか

void MemberType1::action2(); 

Containerクラスのメソッドから?dynamic_castを使用できますが、これは設計上の欠陥と見なされます。回避策はありますか?それ以上に、私が従うことができるパターンはありますか?

4

3 に答える 3

2

正直なところ、私がそれを見れば見るほど、それは私にとって典型的なビジターアプリケーションのように見えます。

クラスにインテリジェンスを実装することは避けContainer、代わりにアクションを特定のクラスに委任することをお勧めしますVisitor。ベースVisitorには、基礎となるコンテナ構造と安全に対話するための何らかの方法を与えることができます(消去/挿入)。

// MemberBase.hpp
class Visitor;

class MemberBase {
public:
  virtual MemberBase* clone() const = 0;

  virtual void accept(Visitor&) = 0;
  virtual void accept(Visitor&) const = 0;
}; // class MemberBase

// Visitor.hpp
class Member1;
class Member2;

class Visitor {
public:
  virtual void visit(Member1&) = 0;
  virtual void visit(Member1 const&) = 0;

  virtual void visit(Member2&) = 0;
  virtual void visit(Member2 const&) = 0;
};

// Container.hpp
#include <MemberBase.hpp>

class Container {
public:
  void accept(Visitor& v) {
    BOOST_FOREACH(MemberBase& mb, _members) {
      mb.accept(v);
    }
  }

  void accept(Visitor& v) const {
    BOOST_FOREACH(MemberBase const& mb, _members) {
      mb.accept(v);
    }
  }

private:
  boost::ptr_vector<MemberBase> _members;
};

accept次に、メンバーのメソッドを実装する必要があります。それは純粋に機械的です。

// Member1.hpp
#include <MemberBase.hpp>

class Member1: public MemberBase {
public:
  virtual Member1* clone() const { return new Member1(*this); }

  virtual void accept(Visitor& v);
  virtual void accept(Visitor& v) const;
};

// Member1.cpp
#include <Member1.hpp>
#include <Visitor.hpp>

void Member1::accept(Visitor& v) { v.visit(*this); }
void Member1::accept(Visitor& v) const { v.visit(*this); }

そして最後に、訪問者を実装できます。

// CountVisitor.hpp
#include <Visitor.hpp>

class CountVisitor: public Visitor {
public:
  CountVisitor(): _count(0) {}

  size_t count() const { return _count; }

  virtual void visit(Member1&);
  virtual void visit(Member1 const&);

  virtual void visit(Member2&);
  virtual void visit(Member2 const&);

private:
  size_t _count;
};

// CountVisitor.cpp
#include <CountVisitor.hpp>
//#include <Member1.hpp> // where you would include, but unnecessary here

void CountVisitor::visit(Member1&) { ++_count; }
void CountVisitor::visit(Member1 const&) { ++_count; }

void CountVisitor::visit(Member2&) { ++_count; }
void CountVisitor::visit(Member2 const&) { ++_count; }

そして、あなたはそれを次のように使用します:

// main.cpp
#include <iostream>

#include <Container.hpp>
#include <CountVisitor.hpp>

int main() {
  Container const c = /* something */;

  CountVisitor cv;
  c.accept(cv);

  std::cout << cv.count() << " items in the container\n";
}

この設計の弱点は、visitから直接派生する新しいクラスごとに新しいメソッドを実装する必要があることです。MemberBaseしたがって、MemberBase階層はそれほどオープンではありません。これは、非巡回ビジターを使用して軽減できます。しかし、私はめったにそれを必要としませんでした。

能力を「拡張」するために、実行する「アクション」(消去、複製など)を持っVisitor::visitて返し、実際に持っている2つのループでこれを処理することができます。いくつかのトリックを使用して、1つのループに減らすことができます...MemberBase::accept

于 2012-04-11T10:27:22.837 に答える
2

各メンバーが異なる関数を公開することがわかっている場合は、いつでもEnumerateActions()、CallAction(std :: string action)などのメソッドを作成できます。これはより安全ですが、バズーカでスズメを撃つ場合と比較される場合があります。 。

もう1つのオプションは、インターフェイスIAction1、IAction2を作成し、それらをメンバー派生クラスに実装することです。次に、メンバーがインターフェースを実装して使用しているかどうかを確認します。Containerは特定のメンバークラスを認識しませんが、それらの特定の機能にアクセスすることはできます。

于 2012-04-11T10:04:40.123 に答える
0

私が考えることができる1つのオプションは、各オブジェクトがMemberType1オブジェクトであるかどうかを確認し、条件がtrueの場合は、action2()を呼び出すことができます。それは非常に素朴な解決策ですが、他の誰かがすぐに賢い解決策を思い付くと確信しています:)

于 2012-04-11T09:53:35.130 に答える