“问题”在标题中不应成为问题的情况。
我想为一组问题(类问题的所有子代)实现一个求解器(类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,那么
step1()
; step2.5()
和step2()
step3()
醇>
现在调用C c; Solver<C> sc(&c); c.justDoIt()
,我需要先修改A
,B
和Solver::justDoIt()
。
是否有可扩展的设计界面,为A
添加新的问题类型(Solver
的所有孩子)?
PS:我即将修改的当前代码库有47种类型的问题都使用相同的Solver
类。最小的改变是首选。
我怎样才能做得更好?
答案 0 :(得分:1)
至少在我看来,这种设计似乎是一种(原谅技术术语)混乱。
目前,Solver
对Problem
的内部有深入的了解。此外,Solver
似乎无法在没有对Problem
内部的深入了解的情况下完成其工作。
至少在我的估计中,你所谓的Solver::justDoIt()
应该是Problem::operator()
。如果您Problem
中显示的step1()
step5()
使用Solver
到Problem
,您可以在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