This happens to me every once in a while and I always end up solving it the same way and then wishing for a cleaner way.
I start out with a calls to related utility functions, followed by and update call.
SynchA();
SynchB();
UpdateLastTime();
Then later I add check boxes so I have:
if(synchA.Checked)
{
SynchA();
}
if(synchB.Checked)
{
SynchB();
}
BUT now I only want to call UpdateLastTime() of ONE OR BOTH the two executed so invariably I end up with:
bool synchHappened = false;
if(synchA.Checked)
{
SynchA();
synchHappe开发者_JAVA百科ned = true;
}
if(synchB.Checked)
{
SynchB();
synchHappened = true;
}
if(synchHappened)
{
UpdateLastTime();
}
That final step always bothers me because I'm spreading this one bool around to three branches of logic.
Is there some obvious better approach to the above logic/scenario that I could use?
The main goal would be - each time as logic has changed - code should be affected at least as possible. So you've to structure such things once and then it will work for you.
In your particular case I would suggest to Keep It Simple (no Strategy Pattern, and so on), so extract and encapsulate a logic of switches into the properties. So each time as requirements will be changed - you've to update either logic of particular switch or main logic itself.
Switches with encapsulated rules:
bool IsUpdateLastTime
{
get
{
// logic here even can be fully or partially injected
// as Func<bool>
return this.IsSyncA || this.IsSyncB;
}
}
bool IsSyncA { get { return synchA.Checked; } }
bool IsSyncB { get { return synchB.Checked; } }
Main logic:
if (this.IsUpdateLastTime)
{
this.UpdateLastTime();
}
This kind of problem is where Rx is really useful, as you can combine several events into a single one. It can be done with something like this.
(Assuming winforms for this example, but it's similar with WPF etc, just by changing the event names/generic type arg for FromEvent.)
var synchAchecked = Observable.FromEvent<EventArgs>(synchA.CheckedChanged);
var synchBchecked = Observable.FromEvent<EventArgs>(synchB.CheckedChanged);
var merged = Observable.Merge(synchAchecked, synchBchecked);
synchAchecked.Subscribe(x => SynchA());
synchBchecked.Subscribe(x => SynchB());
merged.Subscribe(x => UpdateLastTime());
One might consider this pattern more compact (and possibly readable) although it still requires the separate variable.
bool syncHappened = false;
if(syncHappened |= synchA.Checked) SynchA();
if(syncHappened |= synchB.Checked) SynchB();
if(syncHappened) UpdateLastTime();
Some may not like this, but I find it very useful and clean. It's a bit odd, so the decision to use this is up to you.
UpdateLastTime(Either(SynchA(synchA.Checked), SynchB(synchB.Checked)));
private bool Either(bool first, bool second)
{
return first || second;
}
This requires that SynchA()
, SynchB()
and UpdateLastTime()
be modified to do no work if shouldRun is false, and return true or false depending whether the synch occurred.
A bit C centric, but:
if (checked_syncs & SYNC_A)
SyncA();
if (checked_syncs & SYNC_B)
SyncB();
if (checked_syncs)
UpdateLastTime();
This has the advantage that the last check doesn't have to change (unless you run out of bits, in which case you can switch to a larger primitive type, or just use more than one). It also has the advantage of effectively doing all the ORs for UpdateLastTime() in parallel, so it is also a fast solution.
NOTE: Of course SYNC_A and SYNC_B have to be unique powers of two, and yes - this may break your encapsulation a bit, and assumes you can |= the condition when the check occurs (may not be possible, or beneficial over setting a boolean if you are talking about a specific GUI toolkit).
精彩评论