我有一个非常基本的编程问题。想象一下,我有两个函数,它们的定义实际上是相同的,只是它们只在内部二进制条件下变化。两个函数中的其余代码几乎相同。
为了拥有易读且易于维护的代码,我想知道除了使用选择这些函数的操作模式的bool参数之外是否还有更好的解决方案?那有design-pattern
吗?
在下面的代码中,我用两个名为doA()
和doB()
的函数来说明我的疑问。点(....
)对应于两个函数完全相同的代码。
我创建了一个带有附加布尔参数的新doNew()
函数来选择适当的功能。但请注意,尽管这是一种可能的解决方案,但由于if
条件的主体内部存在重复的代码,因此效率仍然低下。
void doA( ..... ){
.....
.....
if(x!=y){
....
....
....
}
.....
}
void doB( ..... ){
.....
.....
if(x==y){
....
....
....
}
.....
}
void doNew( ....., bool selectionMode ){
.....
.....
if(selectionMode == true){
if(x==y){
....
....
....
}
}
else{
if(x!=y){
....
....
....
}
}
.....
}
答案 0 :(得分:4)
我还会使用一个布尔参数来区分它们。对于那个简单的程序,我不会使用复杂的模式。
我会用
void doIt(..., BOOL is_equal) {
...
if((a == b) == is_equal) { // or: is_equal ^ (a == b)
...
}
...
}
最终减少冗余。我还会定义独立的名字
void doA(...) {
doIt(..., true);
}
void doB(...) {
doIt(..., false);
}
因为我认为API中的标志参数不好。
答案 1 :(得分:3)
我想说你将do
作为模板函数,将二元函数作为函数的函子参数。类似的东西:
template <class BinaryFunctor>
void do(...,BinaryFunctor f)
这个二元仿函数会返回bool
,您可以在do
内使用。
仿函数的示例代码:
struct Equals
{
Equals(int x , int y) : m_x(x), m_y(y){}
bool operator()() const { return m_x == m_y;}
private:
int m_x;
int m_y;
};
struct NotEquals
{
NotEquals(int x , int y) : m_x(x), m_y(y){}
bool operator()() const { return m_x != m_y;}
private:
int m_x;
int m_y;
};
template<class BinaryFunctor>
void doSomething(BinaryFunctor f)
{
if(f())
{
//Condition satisfied
}
}
int main ()
{
doSomething(Equals(10,11));
doSomething(NotEquals(10,11));
return 0;
}
答案 2 :(得分:3)
您可以将其写为:
if(selectionMode && x==y || !selectionMode && x!=y)
//....
还有其他方法可以做到这一点。您可以传递一个仿函数(例如:一个函数指针)进行比较。您可以使用仿函数模板参数编写模板,并使用模板定义这两个函数。
更新
人们提供了几个示例,因此我将向您展示另一个示例,这个示例使用普通函数指针,并且不会传递两个值进行比较,但会传递比较结果。 (注意:其他示例是首选,但您看到的示例越多越好)
//type of the function pointer
typedef bool (*dofuncptr)(bool);
bool do_istrue(bool b) {
return b;
}
//negates the input
bool do_isfalse(bool b) {
return b;
}
void doX(dofuncptr fun) {
//...
if (fun(x == y)) {
//....
}
//...
}
int main() {
//you can use it like this:
doX(&do_istrue);
}
答案 3 :(得分:3)
许多人建议使用仿函数,这是一个很好的建议。对你来说最棒的是,==和!=的算子等价物和其他这样的比较运算符已经作为标准C ++库的一部分存在。我就是这样做的:
#include <functional>
#include <iostream>
template <class T, template <class T> class BinaryFunctor>
void doSomething(T x, T y, BinaryFunctor<T> f) {
if (f(x, y)) {
std::cout << "True" << std::endl;
} else {
std::cout << "False" << std::endl;
}
}
int main(int argc, const char* argv[])
{
doSomething(5, 5, std::equal_to<int>());
return 0;
}
doSomething
模板函数接受两个相同类型T
的参数和一个类型为BinaryFunctor<T>
的参数。请注意,T出现在所有三个参数中,因此在所有传递的参数中必须相同。因此传递两个int
和一个std::equal_to<int>
很好(正如我在示例中所做的那样),因为T
可以实例化为int
而BinaryFunctor
可以{ {1}}。
std::equal_to
是标准库的一个比较函数对象(或仿函数)的示例。它只是一个覆盖std::equal_to
的类,因此可以使用operator()
类型的对象,就像它是一个真正的函数一样。因此,当std::equal_to
类型的对象作为std::equal_to
参数传递给函数时,您可以像f
一样使用它。
现在,如果您想将比较运算符更改为f(someInt, anotherInt)
,则只需将函数调用更改为!=
,它将按预期工作。您还可以找到其他仿函数,例如(省略doSomething(5, 5, std::not_equal_to<int>());
命名空间):std
,greater
,less
,greater_equal
等。
答案 4 :(得分:1)
对于动态解决方案,我会使用指针函数。
void(*doNew)(...);
doNew = &doA;
doNew(...);//now calls doA
doNew = &doB;
doNew(...);//now calls doB
对于静态解决方案...... 我会使用值类型模板函数。 防爆。
template<bool TMode>
void doNew( .....);
template<>
void doNew<TRUE>( .....);
{
.....
.....
if(x==y){
....
....
....
}
.....
.....
}
template<>
void doNew<FALSE>( .....);
{
.....
.....
if(x!=y){
....
....
....
}
.....
.....
}
然后你就可以像......一样使用它。
doNew<FALSE>(...); // is equivalant to doNew(..., false);
答案 5 :(得分:0)
你不需要一个模式,你需要一种重构技术。
我倾向于写四个函数,以便你得到
DoA()
{
Part1();
SpecificForA();
Part2();
};
DoB()
{
Part1();
SpecificForB();
Part2();
};
您可能需要在它们之间共享一些变量作为参数。这些功能可能是有用的,可重复使用的功能。更有可能的是,您发现需要进行更多的重构。现在这更容易了,因为在提取这些功能之后,您可以更清楚地了解它们的作用。