I have a data object with three fields, A, B and C. The problem is that the user can set any of them because:
A * B = C
So if a user starts off by setting A & B, C will be calculated. but then if a user set C, A gets recalculated, so there is an implicit anchoring that is happening based off the last field that the user set.
i want to avoid a solution with a lot of flag member variables. Any best practices on how i can code this class up without having a lot of stuff like this below
public class Object
{
private double _A;
private double _B;
private double _C;
private bool _enteredA;
private bool _enteredB;
private bool _enteredC;
public double A
{
get
{
if (_enteredC)
{
return _C / _B;
}
else
{
return _A;
}
}
}
should the logic of setting A, B, and C always be on the "get" or the "set".
Is there any cleaner way to doing this?
So your rules are:
A = C / B
B = C / A
C = A * B
What you'd need to do is remember the last two fields that the user entered, and from that, calculate the third. Therefore if they entered A and then B, then you calculate C. If they then change C, then recalculate A, and so on.
If you want to code it without using boolean flags, there's a couple of different ways you could do that. A queue, or just a simple array/map would work:
editOrder = {A : 0, B : 0, C : 0 } // (or just use 0, 1, 2)
editCount = 0;
every time any field is edited :
editCount = editCount + 1
editOrder[field] = editCount
if two fields have an editOrder > 0
recalculate the field with the lowest editOrder
Example usage:
Field changed editOrder recalculate field
A 1,0,0 -
A 2,0,0 -
B 2,3,0 C
C 2,3,4 A
C 2,3,5 A
B 2,6,5 A
A 7,6,5 C
You state that your properties have the relationship "A * B = C". So, your class should express this. A and B are independent variables, so they have simple setters and getters that merely get and set a filed. C is a Dependant variable and so it should only have a getter and perform the calculation based on A and B:
class Foo
{
private double m_a;
public double A
{
get { return m_a; }
set { m_a = value; }
}
private double m_b;
public double B
{
get { return m_b; }
set { m_b = value; }
}
public double C
{
get { return A * B; }
}
}
An editor can be written that allows A and B to edited trivially. It can also allow the user to edit C if (and only if) the user has edited A or B already. The view would store some state to indicate which (if any) field was last edited and can therefore apply the appropriate change to A or B when the conceptual C is edited.
Now, if the View is unaware of the calculation of C, you can add methods to the Foo class to "SetAGivenC()" and "SetBGivenC()":
class Foo
{
... as above ...
public void SetAGivenC( double newC )
{
A = newC/B;
}
public void SetBGivenC( double newC )
{
B = newC/A;
}
}
Now your GUI just calls one of these methods when "C" is edited.
I am confused as to what "last field that the user set" means. Imagine that there are two editors up on the screen (both bound to the same Foo instance) and the user edits A in the first editor, then B in the second, then C in the first. What gets adjusted when C is set? A or B? If you can answer this question, you will know which class should track the "last field that the user set" - should it be the model or the view? I expect that A would be adjusted in this case and the view tracks the last edited field. But that's an issue for another day!
Edit:
Jeepers - I even stuffed up my own pathological case!
I'll try that last paragraph again:
I am confused as to what "last field that the user set" means. Imagine that there are two editors up on the screen (both bound to the same Foo instance) and the user edits A then B in the first editor, the user then edits B then A in the second, the user then edits C in the first. What gets adjusted when C is set? A or B? If you can answer this question, you will know which class should track the "last field that the user set" - should it be the model or the view? I expect that A would be adjusted in this case and the view tracks the last edited field. But that's an issue for another day!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With