提问人:Luchian Grigore 提问时间:2/23/2013 更新时间:2/24/2013 访问量:354
如何解决同时设置成员的 getter 问题?
How to work around a getter that also sets a member?
问:
我正在处理以下几点:
我有一个非常复杂的类,并且该成员依赖于一些在初始化类时未设置或在旅途中设置的内容。即使未设置该成员,该类的对象也有意义。还可以根据对其他成员所做的其他更改来重置它。
现在,假设这个“特殊”成员的设置计算成本很高,因此我根据请求延迟计算它。
所以:
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
- 删除 ?从逻辑上讲,getter 应该是 ,但不能。还有一个缺点是不能在对象上调用它。
const
const
const
- 我可以做,但这似乎不是正确的做法。
specialObject
mutable
- 抛弃恒常?再次,看起来很腥。
- 在获取之前打电话?- 如果有人忘记了怎么办?他们会得到一个过时的结果。
computeSpecialObject
有没有处理这个问题的设计模式?一个好的方法?还是班级设计错了?(我倾向于最后一个,但改变班级并不是一个真正的选择)
注意:我已经制作了成员,想知道是否有更好的解决方案。mutable
答:
我会留下 并制作或保留指向 的指针,而不仅仅是将其“嵌入”到类中。const
specialObject
mutable
specialObject
我还会添加一个标志,并在进行使计算无效的更改时设置它。然后我会检查里面的标志,只有在设置好后才做工作。使用指针,每当更改使现有计算失效时,您甚至可以使用旧的计算对象,但这会打开一整套蠕虫。bool dirty
mutable
computeSpecialObject
delete
还是我错过了什么?
我可以制作 specialObject ,但这似乎不是正确的做法。
mutable
为什么会这样?这正是存在的原因:允许函数在逻辑上,而不需要在物理上保持对象不变(如果你创建对象,请记住确保线程安全 - 我相信你明白我的意思)。mutable
const
const
mutable
当然,只要对象的初始化不会改变对象的逻辑状态,这是正确的,因为这是承诺不会做的事情。SpecialClass
const
在这种情况下,函数本身根本不是本质上的,它可能应该被命名为不同的名称,而不仅仅是:可以是一个候选者。const
getSpecialObject()
computeAndReturnSpecialObject()
评论
mutable
specialObject
specialObject
mutable
mutable
const
x
y
mutable
这始终是一条细线,在不需要时不调用它,而不是在调用器中引入漏洞,这意味着它可能没有并返回不正确的结果。
我会将计算移动到特殊对象的方法,并将此类视为该类的计算方法参数的包装器。一个奖励球是你可以对计算进行单元测试。
然后,只需决定何时需要再次调用 SpecialObject.Compute(x,y) 或仅返回最后一个结果。 如果可以的话,我可能会考虑的另一件事是,如果 X 发生了变化,但 Y 没有,我是否可以简化计算。即保留一些中间结果。
不确定它对你有多适用,但我经常做的一件事就是注入一个执行计算的东西,所以我倾向于默认陷入这种模式。
你可以去两个方向,或多或少.一个涉及的不是状态操纵,而是行为,另一个完全忘记了行为,而关心返回的状态。OOP
Functional
哎呀
对我来说,一个关键原则是 或 。OOP
Tell, Don't Ask
write no getters or setters
将你的对象设计成被告知该做什么,成为自主的。不要要求它返回一些对象,然后你可以用它来做某事。只要告诉它做你首先想做的事情。如果你告诉一个对象做某事,那么你可能会期望它改变状态,而它不适合。const
您可以提供一些服务。您可以改为告诉 ,这是正确的可变性。SpecialClass
doService()
Class
doSpecialService()
另一种方法是创建此对象,以使用其他对象进行创建。因此,函数可以是 const,但采用非 const 参数:
class Class {
public:
void doService(ServiceProvider& serviceProvider) const {
serviceProvider.doService(x, y);
}
};
有了这个,您将传入一个 which 将为给定的 和 创建正确的 。它将是可变的。在提供服务时修改状态似乎是正确的。也许你可以有一个地图缓存 (, ) 对的对象。SpecialServiceProvider&
SpecialClass
X
Y
SpecialClass
X
Y
功能的
另一个方向是使对象不可变。每当您需要一些新状态时,请使用旧状态作为基础创建它。这可能会产生连锁反应,直到你(几乎)一直有海龟:
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
评论