设计“求解器”类的通用处理程序

时间:2016-01-22 00:54:22

标签: c++ oop design-patterns

“问题”在标题中不应成为问题的情况。

我想为一组问题(类问题的所有子代)实现一个求解器(类Solver),它或多或少地共享同一组方法。我目前的设计是这样的:

solver.h

template<class P>
class Solver{
public:
    P* p;
    Solver(P* problem) : p(problem) {}
    void justDoIt(){
        p->step1();
        p->step2();
        p->step3();
        p->step4();
        p->step5();
    }
}

main.cpp中:     #include“solver.h”

class A {
public:
    void step1() {}
    void step2() {}
    void step3() {}
    void step4() {}
    void step5() {}
};

class B: public A {
public:
    void step2() {}
    void step4() {}
};

class C: public A {
public:
    void step3() {}
    void step4() {}
    void step5() {}
};

int main(){
    B b;
    C c;
    Solver<B> sb(&b);
    Solver<C> sc(&c);
    sb.justDoIt();
    sc.justDoIt();
    return 0;
}

如果我想扩展Solver以获得新的问题类型,比如C,那么

  1. step1();
  2. 中不执行任何操作
  3. step2.5()step2()
  4. 之间step3()

    现在调用C c; Solver<C> sc(&c); c.justDoIt(),我需要先修改ABSolver::justDoIt()

    是否有可扩展的设计界面,为A添加新的问题类型(Solver的所有孩子)?

    PS:我即将修改的当前代码库有47种类型的问题都使用相同的Solver类。最小的改变是首选。

    我怎样才能做得更好?

2 个答案:

答案 0 :(得分:1)

至少在我看来,这种设计似乎是一种(原谅技术术语)混乱。

目前,SolverProblem的内部有深入的了解。此外,Solver似乎无法在没有对Problem内部的深入了解的情况下完成其工作

至少在我的估计中,你所谓的Solver::justDoIt()应该是Problem::operator()。如果您Problem中显示的step1() step5()使用SolverProblem,您可以在class Problem { protected: virtual void step1() {} // ... virtual void step5() {} public: virtual void operator()() { step1(); step2(); step3(); step4(); step5(); } }; class B : public Problem { protected: void step2() {} void step4() {} }; class C : public Problem { protected: virtual void step3() {} virtual void step4() {} virtual void step5() {} }; 中默认提供该实现{1}}本身,那些需要覆盖它的人将提供他们自己的实现:

int main() { 
    B b;
    C c;

    b();
    c();
}

然后主要看起来像这样:

int main() { 
    B()();
    C()();
}

或者,如果您更喜欢更短的代码:

operator()

这将创建每种类型的临时对象,然后在该临时对象上调用var data = { "vehicle": { ["id", "name", "description", "ip_address"], "records": [ ["1", "lamborghini1", "gsdgsdgsdgsd", "112.206.114.21"], ["2", "honda civic", "murahun na kotse", "194.153.205.26"], ["3", "toyata", "gsdhgkhsdkjghsdgsdgs", "198.153.205.28"] ] } }] } }); }); var output = "<ul>"; for (var i in data.records) { output += "<li>" + data.records[i].name + "</li>"; } output += "</ul>"; document.getElementById("placeholder").innerHTML = output; 。我并不是特别喜欢它,但有些人认为这很棒。

答案 1 :(得分:0)

虚拟功能:

应该考虑的第一个选项是使用虚函数:

重新定义您的问题类以包含纯虚函数(这意味着:每个孩子都需要重新实现它):

class Problem{
  public:
  virtual void allSteps()=0;
};

重新定义您的解算器以调用此特殊功能:

class Solver{
  public:
  Problem* p;

  Solver(Problem* prob):p(prob){}

  void solve(){
    p->allSteps();
  }
};

在每个子类中添加一个实现:

class MyProblem: public Problem{
public:
  void step1(){
    std::cout << "step1\n";
  }
  void step2(){
    std::cout << "step1\n";
  }
  void stepx(int x){
    std::cout << "step"<<x<<"\n";
  }

  void allSteps(){
    step1();
    step2();
    stepx(3);
    stepx(4);
  }
};

像以前一样使用你的主要功能:

int main() {
  MyProblem myP;
  Solver s(&myP);
  s.solve();
  return 0;
}

在此处试试:http://ideone.com/NOZlI6

函数指针/对象

这是一个稍微复杂一点的解决方案,但根据您的需求(例如只执行一步,然后执行其他操作),它可能更适合您的需求。

每当你看到类似&#34; foo1&#34;,&#34; foo2&#34;,&#34; foo3&#34;,...时,你应该想到一个数组或一个向量。同样可以应用于您的问题:

首先,重新定义您的问题&#34; class使用任意数量的函数指针 - 或使用c ++,函数对象:

class Problem{
public:
  std::vector<std::function<void(void)>> functions;
};

然后你的Solver需要做的就是遍历Problems类中的函数对象:

class Solver{
public:
  Problem* p;

  Solver(Problem* prob):p(prob){}

  void solve(){
    for(auto func : p->functions)
    func();
  }
};

为了正确注册你的类函数,你需要记住成员函数有一个额外的&#34;隐藏&#34;参数&#34;这个&#34;这是指向类本身的指针。但是我们可以使用std :: bind从我们拥有的任何函数中创建一个void(void)函数。另一种方法是使用静态函数,但由于这很容易理解,我将在这里使用更复杂的方法:

class MyProblem: public Problem{
public:
  void step1(){
    std::cout << "step1\n";
  }
  void step2(){
    std::cout << "step1\n";
  }
  void stepx(int x){
    std::cout << "step"<<x<<"\n";
  }

MyProblem(){
    functions.push_back(std::bind(&MyProblem::step1,this));
    functions.push_back(std::bind(&MyProblem::step2,this));
    functions.push_back(std::bind(&MyProblem::stepx,this,3));
    functions.push_back(std::bind(&MyProblem::stepx,this,4));
}

};

您的主要功能将不受影响:

int main() {
  MyProblem myP;
  Solver s(&myP);
  s.solve();
  return 0;
}

在此处试试:http://ideone.com/BmIYVa