4

我正在处理这些方面的事情:

我有一个非常复杂的类和一个成员,它依赖于一些在类初始化时未设置的东西,或者在旅途中设置。即使没有设置该成员,该类的对象也有意义。它也可以根据对其他成员所做的其他更改进行重置。

现在,假设这个“特殊”成员的设置计算成本很高,所以我会根据要求延迟计算它。

所以:

class Class
{
    X x;
    Y y;
    SpecialClass specialObject;
public:
    void setX(const X& newX);
    void setY(const Y& newY);

    //----
    SpecialClass getSpecialObject() /*const*/
    { 
        computeSpecialObject();
        return specialObject();
    }
private:
    void computeSpecialObject()
    {
        //specialObject depends on x and y
        //and is expensive to compute
        //this method is a bottleneck
    }
};

我不想compute每次更改时都调用该方法,x或者y因为它很昂贵,所以我陷入了两难境地:

  • 删除const?从逻辑上讲,getter 应该是const,但它不能。还有一个缺点是它不能在const对象上调用。
  • 我可以做specialObject mutable,但这似乎不是正确的做法。
  • 抛弃一贯性?再次,看起来很腥。
  • 在获取之前调用computeSpecialObject?- 如果有人忘记了怎么办?他们会得到一个过时的结果。

有没有处理这个问题的设计模式?一个好的方法?还是课程设计错了?(我倾向于最后一个,但改变课程并不是一个真正的选择)

注意:我已经做了会员mutable,想知道是否有更好的解决方案。

4

4 回答 4

4

我可以制作 specialObject mutable,但这似乎不是正确的做法。

为什么这样?这正是mutable存在的原因:允许一个const函数在逻辑上 const不需要物理保持对象不变(如果你制作了 object mutable,请记住确保线程安全- 我相信你知道我的意思)。

只要对象的初始化SpecialClass不会改变对象的逻辑状态,这是正确的,因为这是const承诺不会做的事情。

在这种情况下,函数本身根本就不是const本质上的,它可能应该被命名为不同的东西,而不仅仅是getSpecialObject():computeAndReturnSpecialObject()可能是一个候选者。

于 2013-02-22T22:24:38.933 回答
3

我会留下constand要么制作specialObject mutable或保留指向the的指针specialObject,而不仅仅是将其“嵌入”到类中。

我还将添加一个bool dirty标志,mutable并在做出使计算无效的更改时设置它。然后我会检查里面的标志computeSpecialObject,只有当它被设置时才做这项工作。delete使用指针,只要更改使现有计算无效,您甚至可以使用旧的计算对象,但这会打开一个完整的“另一罐蠕虫”。

还是我错过了什么?

于 2013-02-22T22:24:33.610 回答
0

这总是一条很好的路线,当你不需要时不调用它,而不是在调用者中引入漏洞,这意味着它可能不是并且返回不正确的结果。

我将计算移动为特殊对象的方法,并将此类视为该类计算方法的参数的包装器。一个奖励球是您可以对计算进行单元测试。

那么这只是一个决定何时需要再次调用 SpecialObject.Compute(x,y) 或简单地返回最后一个结果的问题。如果可以的话,我可能会考虑的另一件事是,如果 X 已更改但 Y 没有更改,我是否可以简化计算。即保留一些中间结果。

不知道它对你有多适用,但我经常做的一件事是注入一个做计算的东西,所以我倾向于默认使用这种模式。

于 2013-02-22T22:46:04.680 回答
0

有两个方向你可以去,更多OOP或更多Functional。一种涉及较少关心状态操作,而是关心行为,另一种完全忘记了行为,关心返回的状态。

面向对象

对我来说,一个关键OOP原则是Tell, Don't Ask,或write no getters or setters

设计您的对象以被告知要做什么,使其具有自主性。不要要求它返回一些您可以用来做某事的对象。只需告诉它首先做你想做的事。如果你告诉一个对象做某事,那么你可能期望它改变状态,而它是不正确的const

SpecialClass可以提供一些服务doService()。您可以改为告诉Classto doSpecialService(),这是正确的可变的。

另一种方法是创建此对象以使用其他对象进行创建。所以一个函数可以是 const 但带一个非 const 参数:

class Class {
public:
    void doService(ServiceProvider& serviceProvider) const {
        serviceProvider.doService(x, y);
    }
};

有了这个,您将传入 a SpecialServiceProvider&,它将SpecialClass为给定的Xand创建正确的值Y。这将是可变的。在提供服务时修改状态似乎是正确的。也许您可以为 ( , ) 对设置地图缓存SpecialClass对象。XY

功能性

另一个方向是让你的对象不可变。每当您想要一些新状态时,使用旧状态作为基础来创建它。这可能会产生连锁反应,直到您(几乎)一直有海龟:

class SpecialBuilder {
public:
    SpecialBuilder withX(const X& newX) const;
    SpecialBuilder withY(const Y& newY) const;
    SpecialClass build() const;
};

SpecialBuilder specialBuilder;
SpecialClass special = specialBuilder.withX(x).withY(y).build();

您可以在每个返回的数据之间共享数据,SpecialBuilder因为它是不可变的。

于 2013-02-22T22:46:05.233 回答