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层次结构不是那么开放。这可以使用Acyclic Visitor来缓解;但是,我很少需要它。

要“扩展”能力,您可以拥有Visitor::visitMemberBase::accept返回要执行的“动作”(擦除、克隆等),并在您实际拥有的两个循环中处理此问题。可以使用一些技巧将其简化为一个循环...

于 2012-04-11T10:27:22.837 回答
2

如果您知道每个成员将发布不同的函数,您可能总是会创建 EnumerateActions()、CallAction(std::string action) 等方法 - 这会更安全,但可能与用火箭筒射击麻雀相比.

另一种选择是制作接口:IAction1、IAction2 并在成员派生类中实现它们。然后你只需检查一个成员是否实现了一个接口并使用它。Container 不会知道特定的成员类,但您仍然可以访问它们的特定功能。

于 2012-04-11T10:04:40.123 回答
0

我能想到的一个选项是检查每个对象是否为 MemberType1 对象,如果条件为真,则可以调用 action2()。这是一个非常幼稚的解决方案,但我相信其他人很快就会想出一个聪明的解决方案:)

于 2012-04-11T09:53:35.130 回答