Reputation: 196539
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?
Upvotes: 7
Views: 482
Reputation: 18061
@Jonathan
My point about errors in your method of calculations is as follows:
In the code you wrote, the following sequence of events occurs.
given a valid state at some point of time.
[a = 1, b = 3, c = 3]
call your setter with b = 4, which by your code will then calculate a and c:
1. first sets b b = 4
2. first calculates a a = c / b = 3 / 4 = 3/4
3. then calculates c c = a * b = 3/4 * 4 = 3
the final tuple is
[a = 3/4, b = 4, c = 3]
In essence, for your class (as currently written)
setting b alters a -> leaves c unaffected.
setting c alters a -> leaves b unaffected.
setting a alters b -> leaves c unaffected
Calculation (3) is useless because if A
was already derived from C
and B
, re-deriving C
from A
and B
will not change the value of C
, ever.
Setting A
or B
should calculate C
, and there is a special case where setting C
calculates A
or B
without using a boolean to know which one. (the question as posted)
Upvotes: 0
Reputation: 6843
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!
Upvotes: 3
Reputation: 546055
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
Upvotes: 4
Reputation: 7285
Are you looking for something like this (thanks to nickf for taking the trouble to sovle the equation for each value):
class Foo
{
// Mark these as obsolete so that
// you don't use them by mistake
// in the class.
[Obsolete]
private double _a;
[Obsolete]
private double _b;
[Obsolete]
private double _c;
#pragma warning disable 0612
public double A
{
get
{
return _a;
}
set
{
_a = value;
// You may want to change
// this to simply _a.
_b = _c / (_a == 0 ? 1 : _a);
_c = _a * _b;
}
}
public double B
{
get
{
return _b;
}
set
{
_b = value;
_a = _c / (_b == 0 ? 1 : _b);
_c = _a * _b;
}
}
public double C
{
get
{
return _c;
}
set
{
_c = value;
_a = _c / (_b == 0 ? 1 : _b);
_b = _c / (_a == 0 ? 1 : _a);
}
}
#pragma warning restore 0612
}
Upvotes: 0
Reputation: 93348
Short answer is no.
You could create an adapter to handle the logic you're describing. You'd be moving the complexity out of this class and into another though.
Upvotes: 0
Reputation: 4277
create a private method to do the following calculation
private void calculateFields(){
if (_b != INIT_VALUE && _c != INIT_VALUE)
_a = _c / _b;
if (_a != INIT_VALUE && _c != INIT_VALUE)
_b = _c / _a;
if (_a != INIT_VALUE && _b != INIT_VALUE)
_c = _a * _b;
}
public double A
{
get{
return _a;
}
set{
_a = value;
calculateFields();
}
}
Do the same for property B and C.
Upvotes: 0