How to work with getter, which also sets a member?

I am dealing with something that goes along these lines:

I have a class that is quite complex and is a member that depends on some things that are not set when the class is initialized or installed on the go. Objects of this class make sense even if this element is not specified. It may also be reset depending on other changes made to other members.

Now suppose this "special" member is computationally expensive to install, and so I delay it on request.

So:

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

I do not want to call the compute method every time x or y changes, because it is expensive, so I remain with a dilemma:

  • delete const ? Logically, the getter must be const , but it cannot. There is also the disadvantage that it cannot be called on const objects.
  • I can make specialObject mutable , but that doesn't seem to be the right thing.
  • discard the constellation? Again, it looks suspicious.
  • call computeSpecialObject before receiving? - What if someone forgets? They will get an outdated result.

Is there a design template that deals with this? Good approach Or is the class design just wrong? (I would lean toward this last one, but changing the class is actually not an option)

Note. I made a mutable member, I want to know if there is a better solution.

+4
source share
4 answers

I can make specialObject mutable , but that doesn't seem to be the right thing.

Why is that? That’s why mutable exists: letting the const function be logically const without having to physically leave the object unchanged (and if you create the mutable object, remember to ensure thread safety - I'm sure you know what I mean).

This is true if the initialization of the SpecialClass object SpecialClass not something that changes the logical state of the object, of course, because what const promises do not.

In this case, the function itself is simply not const in nature, and it should probably be called something other than just getSpecialObject() : computeAndReturnSpecialObject() can be a candidate.

+4
source

I would leave const and either make specialObject mutable , or save a pointer to specialObject instead of just "embedding" it in the class.

I would also add the bool dirty flag, which is mutable , and set it whenever a change is made that invalidates the calculation. Then I checked the flag inside computeSpecialObject and did the job only if it was set. Using a pointer, you can even delete old computation object whenever a change invalidates an existing computation, but it opens the whole black worm worm.

Or am I missing something?

+3
source

It is always a thin line, this one, without naming it when you do not need it, and introduces holes in the caller, which means that it may not have been, and return incorrect results.

Me I would move the calculation as a method of a special object and process this class as a wrapper for the arguments of the calculation method of this class. Bonus ball: you can calculate unit test.

Then it is just a decision question when you need to call SpecialObject.Compute (x, y) again or just return the last result. Another thing that I could take a look at if I could, would be if X changes, but Y cannot simplify the calculation. those. save some intermediate results.

Not sure how applicable this is for you, but one of the things I do regularly is injecting something that does the calculation, so I'm leaning towards this template by default.

0
source

There are two areas you could go, more OOP or more Functional . One of them implies less concern for the manipulation of the state, but rather about the behavior, the other completely forgets about the behavior and cares about the returned state.

Oop

For me, the key principle of OOP is Tell, Don't Ask or write no getters or setters .

Create your objects so that they say what to do in order to be autonomous. Do not ask him to return some object that you can then use to do something. Just say to do what you want in the first place. If you tell the object to do something, you probably expect it to change state, and it is not that it was const .

Your SpecialClass may provide some doService() . Instead, you can specify a Class in doSpecialService() , which is correctly modified.

An alternative is to create this object to use some other object to create. Thus, the function can be const, but take no const parameter:

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

With this, you will go to SpecialServiceProvider& , which would create the correct SpecialClass for the X and Y data. That would be volatile. It would be correct to change the state when providing the service. Perhaps you could cache a map of SpecialClass objects for pairs ( X , Y ).

Functional

Another direction is to make your objects immutable. Whenever you want a new state to create it, using the old state as the basis. This can cause a hit until you have turtles (almost) to the end:

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

You can exchange data between each returned SpecialBuilder , as it is immutable.

0
source

All Articles