方法指针和继承//一种策略模式(C ++)

时间:2011-08-05 12:55:21

标签: c++ inheritance strategy-pattern

在我的设计中,有一个类从文件中读取信息。读取信息表示作业(为简单起见,它是一个整数,即“作业ID”)。文件读取器类可以接受可以处理此类作业的对象。现在我的想法是,制作一个接口,例如“IJobHandler”有一个纯虚函数“DoJob()”然后你可以调用类似

的东西
FileReader fr;
Class1 c1; // has a base class IAcceptor with virtual method HandleJobId()
Class2 c2; // has a base class IAcceptor with virtual method HandleJobId()
fr.Register(c1);
fr.Register(c2);
fr.doJob(1); // calls c1.HandleJobId()
fr.doJob(2); // class c2.HandleJobId()

这样可以正常工作。但是,如果某个班级可以处理两个或更多工作ID,会发生什么?但是这个类只能实现一种方法(HandleJobId())。以下不是很好: fr.Register(c1, c1::Handle_1())或类似的东西?

也许我现在的意图不是很清楚。但是你将在下面更大的代码示例中看到它。对于大代码块感到抱歉,但我不知道如何解释它......

class IAcceptable
{
public:
    // interface; implementors should return map of job-ids (int)
    // and a kind of pointer to a method which should be called to
    // handle the job.
    virtual std::map<int, SOME_KIND_OF_FUNCTION_POINTER> GetJobIds() const = 0;
};

class Class12 : public IAcceptable
{
public:
    void Handle_1(){} // method to handle job id 1
    void Handle_2(){} // method to handle job id 2

    virtual std::map<int, SOME_KIND_OF_FUNCTION_POINTER> GetJobIds() const
    {
        std::map<int, SOME_KIND_OF_FUNCTION_POINTER> intToMethodMap;
        // return map, which says: "I can handle job id 1, by calling Handle_1(), so I give you c12 pointer to this method"
        // (same thing for job id 2 and Handle_2())
        intToMethodMap.insert(std::pair<int, SOME_KIND_OF_FUNCTION_POINTER>(1, POINTER_TO_Handle_1);
        intToMethodMap.insert(std::pair<int, SOME_KIND_OF_FUNCTION_POINTER>(2, POINTER_TO_Handle_2);
        return intToMethodMap;
    }
};

class Class34 : public IAcceptable
{
    void Handle_3(){} // method to handle job id 3
    void Handle_4(){} // method to handle job id 4
    virtual std::map<int, SOME_KIND_OF_FUNCTION_POINTER> GetJobIds() const
    {
        std::map<int, SOME_KIND_OF_FUNCTION_POINTER> intToMethodMap;
        // return map, which says: "I can handle job id 3, by calling Handle_3(), so I give you c12 pointer to this method"
        // (same thing for job id 4 and Handle_4())
        intToMethodMap.insert(std::pair<int, SOME_KIND_OF_FUNCTION_POINTER>(3, POINTER_TO_Handle_3);
        intToMethodMap.insert(std::pair<int, SOME_KIND_OF_FUNCTION_POINTER>(4, POINTER_TO_Handle_4);
        return intToMethodMap;
    }
};

class FileReader
{
public:
    // register an IAcceptable
    // and add its handlers to the local list
    void Register(const IAcceptable& acc)
    {
        m_handlers.insert(acc.GetJobIds());
    }

    // if some job is to do, search for the job id and call 
    // the found function
    void doSomeJob(int i)
    {
        std::map<int, SOMEFUNCTION>::iterator specificHandler = m_handlers.find(i);
        // call here (specificHandler->second)()
    }
private:
    std::map<int, SOMEFUNCTION> m_handlers;
};


int main()
{
    Class12 c12;   // can handle job id 1 and 2
    Class34 c34;   // can handle job id 3 and 4

    FileReader fr;
    fr.Register(c12);
    fr.Register(c34);

    fr.doSomeJob(1);  // should lead to this call: c12->Handle_1()
    fr.doSomeJob(2);  // c12->Handle_2();
    fr.doSomeJob(3);  // c34->Handle_3();
    fr.doSomeJob(4);  // c34->Handle_4();
}

好吧,也许设计是我的问题,有人可以给我一个如何让它变得更好的提示:)

5 个答案:

答案 0 :(得分:1)

这是一个完整的例子:

class IAcceptable; 

class DelegateBase
{
public: 
    virtual void Call() = 0; 
};

template <class Class> class Delegate: public DelegateBase
{
public: 
    typedef void (Class::*Function)(); 
    Delegate(Class* object, Function f): func(f) {}
    virtual void Call() { (object->*func)(); }

private: 
    Class* object; 
    Function func; 
}; 



class IAcceptable
{
public:
    // interface; implementors should return map of job-ids (int)
    // and a kind of pointer to a method which should be called to
    // handle the job.
    virtual std::map<int, DelegateBase*> GetJobIds() = 0;
};

class Class12 : public IAcceptable
{
public:
    void Handle_1(){} // method to handle job id 1
    void Handle_2(){} // method to handle job id 2

    virtual std::map<int, DelegateBase*> GetJobIds()
    {
        std::map<int, DelegateBase*> intToMethodMap;
        // return map, which says: "I can handle job id 1, by calling Handle_1(), so I give you c12 pointer to this method"
        // (same thing for job id 2 and Handle_2())
        intToMethodMap.insert(std::pair<int, DelegateBase*>(1, new Delegate<Class12>(this, &Class12::Handle_1)));
        intToMethodMap.insert(std::pair<int, DelegateBase*>(2, new Delegate<Class12>(this, &Class12::Handle_2)));
        return intToMethodMap;
    }
};

class Class34 : public IAcceptable
{
    void Handle_3(){} // method to handle job id 3
    void Handle_4(){} // method to handle job id 4
    virtual std::map<int, DelegateBase*> GetJobIds()
    {
        std::map<int, DelegateBase*> intToMethodMap;
        // return map, which says: "I can handle job id 3, by calling Handle_3(), so I give you c12 pointer to this method"
        // (same thing for job id 4 and Handle_4())
        intToMethodMap.insert(std::pair<int, DelegateBase*>(3, new Delegate<Class34>(this, &Class34::Handle_3)));
        intToMethodMap.insert(std::pair<int, DelegateBase*>(4, new Delegate<Class34>(this, &Class34::Handle_4)));
        return intToMethodMap;
    }
};

class FileReader
{
public:
    // register an IAcceptable
    // and add its handlers to the local list
    void Register(IAcceptable& acc)
    {
        std::map<int, DelegateBase*> jobIds = acc.GetJobIds(); 
        m_handlers.insert(jobIds.begin(), jobIds.end());
    }

    // if some job is to do, search for the job id and call 
    // the found function
    void doSomeJob(int i)
    {
        std::map<int, DelegateBase*>::iterator specificHandler = m_handlers.find(i);
        specificHandler->second->Call(); 
    }
private:
    std::map<int, DelegateBase*> m_handlers;
};


int _tmain(int argc, _TCHAR* argv[])
{
    Class12 c12;   // can handle job id 1 and 2
    Class34 c34;   // can handle job id 3 and 4

    FileReader fr;
    fr.Register(c12);
    fr.Register(c34);

    fr.doSomeJob(1);  // should lead to this call: c12->Handle_1()
    fr.doSomeJob(2);  // c12->Handle_2();
    fr.doSomeJob(3);  // c34->Handle_3();
    fr.doSomeJob(4);  // c34->Handle_4();

    return 0;
}
  1. 要调用成员函数,我们需要一个对象;所以你的地图不应该只包含方法指针,而是包含一个可以封装完整调用的东西:一个对象+一个方法指针。那是代表在这里的东西。

  2. 为了确保方法被正确调用,即使它是在子类中定义的,我们需要正确地存储派生对象和方法指针(没有转换)。因此,我们将Delegate作为模板,将派生类作为其参数。

  3. 这意味着基于不同子类方法的委托是不兼容的,不能放入地图中。为了解决这个问题,我们引入了一个公共基类DelegateBase和虚函数Call()。可以在不知道存储对象/方法的确切类型的情况下调用Call(),并将其分派给类型正确的实现。现在我们可以在地图中存储DelegateBase *指针。

  4. 同时查看boost :: function和boost :: bind,它们提供了上述的概括,我认为它们也可以用于您的目的。

答案 1 :(得分:0)

这类问题有几种解决方案。

如果您有一个可以处理多个不同作业的课程,请单独进行 函数,最简单的解决方案是包装它,有几种类型,例如:

class JobsOneAndTwo
{
public:
    void doJobOne();
    void doJobTwo();
};

class JobOne : public AbstractJob, JobsOneAndTwo
{
public:
    virtual void doJob() { doJobOne(); }
};

class JobTwo : public AbstractJob, JobOneAndTwo
{
public:
    virtual void doJob() { doJobTwo(); }
};

如果这通常发生在作业集中,您可以创建一个模板(over 两个或moer成员函数指针)来生成单独的包装器 功能

或者,您可以分派该类的数据成员:

class JobOneAndTwo : public AbstractJob
{
    int myJob;
public:
    JobOneAndTwo(int id) : myJob( id ) {}
    void JobOne();
    void JobTwo();
    virtual void doJob()
    {
        switch ( myJob ) {
        case 1:
            JobOne();
            break;

        case 2:
            JobTwo();
            break;
    }
};

在这种情况下,每次传递一次时都会实例化两次 构造函数的不同参数。

在我见过的大多数情况下,当一个班级可以处理两份工作时,就是这样 因为这两个工作只在一些参数上有所不同;这真的很公正 上面第二个解决方案的变体,除了你不切换到 调用不同的成员函数,你只需使用参数(传递 在基本功能中进入构造函数。

更一般地说,不要忘记你的具体工作类可以有 数据及其行为可以通过这些数据进行修改。你可以 使用不同的数据注册单个类的多个实例。

答案 2 :(得分:0)

typedef void (IAccaptable::*SOME_KIND_OF_FUNCTION_POINTER)(); 
...
Register(1, (SOME_KIND_OF_FUNCTION_POINTER)(&Class12::Handle1)); 

警告:此C样式转换仅适用于单继承。 (好吧,实际上,使用多继承也可以编译转换器,但是当使用指向非第一个基类的成员函数的funcPtr调用(derivedObject->*funcPtr)()时,将调用它而不使用derivedObject指针经过适当调整以指向属于该基础的适当子对象,最有可能导致崩溃。)

更好但更复杂的解决方案是注册小调用者对象而不是成员函数指针。在调用处理函数时,这些调用者对象可以适当地转换目标对象。

class CallerBase
{
public: 
    virtual void Call(Base* object) = 0; 
}; 

template <class Derived>
struct Caller: public CallerBase
{
public: 
    typedef void (Derived::*Function)(); 
    Caller(Function f): func(f) {}
    virtual void Call(Base* object) 
    {
        Derived* derived = static_cast<Derived*>(object); 
        (derived->*func)(); 
    }

private: 
    Function func; 
}; 

Register(1, new Caller<Derived>(&Derived::F)); 

然后你的地图将包含CallerBase *指针,一旦找到合适的调用者,你就会caller->Call(object)。如果此调用中的object是Derived *,则它将隐式转换为Base *,但虚拟Caller<Derived>::Call()函数会在实际调用该方法之前将其强制转换为Derived *。

答案 3 :(得分:0)

所以你说你有很多处理程序,每个处理程序可以处理任意数量的作业ID,你想要注册任意数量的处理程序,并让所有处理程序处理给定的作业。

为此,让每个处理程序实现此接口:

struct Handler
{
  virtual bool canHandle(job_id_t id) const = 0;
  virtual void doJob(job_it_t id) = 0;
};

要注册处理程序,只需将指针存储在容器中:

std::vector<Handler*> handlers;

然后,如果您需要做一份工作,请迭代容器并发送:

handleJob(job_it_t id)
{
  for (std::vector<Handler*>::iterator it = handlers.begin(), end = handlers.end(); it != end; ++it)
  {
    if ((*it)->canHandle(id))
      (*it)->doJob(id);
  }
}

答案 4 :(得分:-1)

方法指针可以很有趣。

我不想自我宣传,但请查看我在学校写回的指南。

http://nicolong.com/code-examples/menu-object-tutorial

可能会帮助一点。