c++ 软件设计:课太多了?

wfveoks0  于 2023-01-22  发布在  其他
关注(0)|答案(5)|浏览(162)

我是一个软件设计的外行,我正面临一个"问题",也许可以通过一些众所周知的技术/习惯用法/模式来解决,我想知道这些。
我有一个抽象基类,基本上定义了一个纯虚成员函数,其他的很少。然后我有几个从这个抽象基类派生的类,覆盖了这个虚函数。我现在有半打这样的类,而且数量还在增长。这些类只有几个数据成员(非常少,像一对双精度数或加上一个函数指针),它们的区别主要在于它们执行非常短的计算的方式。我想知道这是否表明一个糟糕的设计,并更好地处理在其他一些方式。
如果合适的话,谁能告诉我一个我应该知道的相关的设计模式或习惯用法。谢谢。

    • 编辑**

为了澄清一些事情,抽象基类没有任何数据成员。不是所有的派生类都有数据成员。我所做的是将积分作为类进行坐标转换。给定的转换只需要几个参数,有时还需要用户提供的函数。

cu6pst1q

cu6pst1q1#

具有一个虚函数的抽象基类听起来几乎完全像包含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::函数是此类的多态适配器。
使用lambdas可能会节省一些输入,但它是否会增加或减少代码的语义表达(这可能更重要)则是另一回事。

js4nwp54

js4nwp542#

如果你的抽象基类没有任何数据成员(如果它有一个纯虚方法,看起来就不应该有),那么确实有一个更好的模式,假设我们有这样的代码:

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; });

如果你需要比抽象基类更精确的控制,用一个虚函数调用是可以的,但是当接口中只有一个函数时,我至少会考虑函数对象。
我不关心你必须拥有的派生对象的数量。

edqdpe6u

edqdpe6u3#

有一种说法:DRY.D不要重复Y自己。除了重写纯虚方法,如果有任何其他形式的重复代码,那么这可能是需要修订的信号。如果你有很多类,并且每个类在功能上都是唯一的,那么这是可以的。

vxf3dgd4

vxf3dgd44#

不看例子就很难给出完整的评论。一般来说,你有很多类,每个类只做一小部分操作,这并不一定意味着设计不好。你应该尽可能地使用DRY(不要重复)和SOLID(请参见this wikipedia article)原则来构建类。

pokxtpni

pokxtpni5#

如果操作可以组合,那么你应该尝试组合对象来组合操作。一个类实际上应该只做一件事,所以你不一定有问题。

相关问题