我在软件设计方面是一个非专业人士。我正面临一个"问题"这可能是通过我想要讲述的一些众所周知的技术/成语/模式来解决的。
我有一个抽象基类,基本上定义了一个纯虚拟成员函数,而其他几乎没有。然后我有几个派生自这个的类并覆盖所述虚函数。我现在有六个这样的课程,而且这个数字正在增长。这些类只有几个数据成员(很少,就像几个双精度或加上一个函数指针),它们的区别主要在于它们执行非常短的计算。我想知道这是否表明设计不好,并且更好地以其他方式处理。
如果合适,有人可以指出我应该知道的相关设计模式或习语。感谢。
修改
为了澄清事情,抽象基类没有任何数据成员。并非所有派生类都有数据成员。我正在做的是将积分的坐标转换作为类。给定的转换只需要几个参数,有时需要用户提供的功能。
答案 0 :(得分:3)
一个带有一个虚函数的抽象基类听起来几乎就像一个包含lambda的std::function<>
。
例如:
#include <functional>
#include <vector>
#include <iostream>
int main()
{
using op = std::function<int()>;
int x = 7;
int y = 5;
auto a = [x, y]() -> int { return x + y; };
auto b = [x, y]() -> int { return x - y; };
auto ops = std::vector<op> { a, b };
for (const auto& o : ops)
{
std::cout << o() << std::endl;
}
}
最后,lambda只是编写一个类的简写形式,它捕获(复制)一些对象或对象的引用并公开一个调用操作符。
std :: function是这类的多态适配器。
使用lambdas可以节省一些打字。它是否增加或减少代码的语义表达(这可能更重要)是另一回事。
答案 1 :(得分:2)
如果你的抽象基类没有任何数据成员(如果它有一个纯虚方法似乎不应该),那么确实有一个更好的模式。假设我们有这个代码:
struct AbstractBase {
virtual double calc(double) = 0;
virtual ~AbstractBase() = default
}
现在,你必须继承这个,以便在其他地方动态使用:
struct Derived : public AbstractBase { ... }
void BaseUser(AbstractBase& ab) { ... };
一个耦合度较低的解决方案就是将您的类编写为函数对象并使用std::function
。
struct Derived {
double operator()(double x) { ... };
}
void User(std::function<double(double)> f);
User(Derived{}); // Calls user with Derived routine.
这也有其他优点,例如,如果你的某些派生类实际上不需要状态,那么你可以将它们作为普通函数编写,并在std::functions
中传递它们。另一个好处是你现在也可以内联编写短函数,因为lambda是一个函数对象:
User([] (double x) { return 2*x; });
如果你需要更多的精确控制而不是一个虚拟函数调用的抽象基类,那么当界面中只有一个函数时,我至少会考虑查看函数对象。 / p>
我不会担心你必须拥有的派生对象的数量。
答案 2 :(得分:1)
有一种说法:干。 D R epeat Y 我们自己。除了覆盖纯虚方法之外,如果存在任何其他形式的重复代码,那么这可能是需要修订的标志。如果你有很多类,每个类的功能都是独一无二的,那就没关系。
答案 3 :(得分:1)
如果没有看到一个例子,很难完全评论。一般来说,你有很多类,每个类只执行一小部分(一组)操作,这并不一定表明设计不好。您应该尽可能多地尝试使用DRY(不要重复自己)和SOLID(请参阅this wikipedia article)原则来构建类。
答案 4 :(得分:0)
如果可以编写操作,那么您应该尝试编写对象以组成操作。一个班级应该只做一件事,所以你不一定有问题。