Skip to content

Instantly share code, notes, and snippets.

@michaelbartnett
Last active May 25, 2016 22:20
Show Gist options
  • Select an option

  • Save michaelbartnett/834d0b7dabdd130d538a to your computer and use it in GitHub Desktop.

Select an option

Save michaelbartnett/834d0b7dabdd130d538a to your computer and use it in GitHub Desktop.
Justification of ternary operator
// I posted this because I remembered a small twitter conversation about ternary operator vs if, and randomly
// found myself purposefully choosing to use ternary and wanted to highlight my reasons in case there's
// any other weirdoes like me think it's fun to talk about this sort of thing.
//
// The important part is what value is assigned to tutorialNumber, and I think that ternary operator better
// highlights where and how that changes than an if statement does.
private void ShowReminder(Reminder reminder)
{
Assert.AreNotEqual(reminder, Reminder.None, "Can't show the 'None' reminder");
const int CancelShowReminderTutNumber = 0;
int tutorialNumber = -1;
switch (reminder) {
case Reminder.ActivateBoost:
// The fact that this is an if-else statement means that it's entirely
// possible to forget to assign tutorialNumber, or for future readers
// to miss that the important thing here is the assignment to
// tutorialNumber when skimming.
//
// This is a simple if statement but even so, being nested in a case statement
// with a bunch of other things going on means that I'm more likely go just
// think of this as "a place where maybe some things happen" when skimming the
// code in the future instead of instantly recognizing the important part.
if (ShouldShowBoostReminder()) {
tutorialNumber = TutorialConst.ActivateBoostTutorialNumber;
} else {
tutorialNumber = CancelShowReminderTutNumber;
}
break;
case Reminder.UsePowerUp:
// Here, it's unambiguous that the entire purpose of
// the ShoudlShowPowerUpReminder() condition is to
// determine what value to assign tutorialNumber.
//
// The absence of if/else keywords and square brackets
// immediately makes me think "Expression! There's a
// value here that's important".
tutorialNumber = ShouldShowPowerUpReminder()
? TutorialConst.UsePowerUpTutorialNumber
: CancelShowReminderTutNumber;
break;
default:
throw new NotImplementedException("no case for " + reminder);
}
// Now we act on the value of tutorialNumber. If a function
// feels hairy, I like to push side effects offunctions down
// to the bottom, so I naturally look here for changes to the
// game state.
//
// So when skimming in the future I'm going to skip down to
// this if statement, then probably look back up to see where
// tutorialNumber was assigned.
//
// Not pushing the assignment into a deeper scope with an if
// statement feels to me like it will be easier to find in the
// future.
//
// Probably very particular to my code reading habits, but
// that's my story and I'm sticking to it.
if (tutorialNumber == CancelShowReminderTutNumber) {
HideReminder(resetLastHideTime: false);
} else {
Assert.IsTrue(tutorialNumber > 0);
waitingOn = reminder;
StartCoroutine(ShowReminder_Coro(tutorialNumber));
}
}
//// Here's another scenario, fading audio cues.
// Version 1, grossness
if (cue.IsFading) {
float newVolume = CalcNewVolume(cue.fade);
if (cue.IsFadingIn) {
if (newVolume >= cue.fade.targetVolume) {
newVolume = cue.fade.targetVolume;
cue.ClearFadeFlags();
}
} else {
if (newVolume <= cue.fade.targetVolume) {
newVolume = cue.fade.targetVolume;
cue.ClearFadeFlags();
}
}
}
cue.audioSource.volume = newVolume;
// Version 2, make the conditions intermediate to reduce redundant code structure
if (cue.IsFading) {
float newVolume = CalcNewVolume(cue.fade);
bool fadeFinished;
if (cue.IsFadingIn) {
fadeFinished = newVolume >= cue.fade.targetVolume;
} else {
fadeFinished = newVolume <= cue.fade.targetVolume;
}
if (fadeFinished) {
newVolume = cue.fade.targetVolume;
cue.ClearFadeFlags();
}
cue.audioSource.volume = newVolume;
}
// Version 3, ternaries are nice, indicates that fadeFinished will always be assigned, prevents
// a temporarily-uninitialized state for fadeFinished
if (cue.IsFading) {
float newVolume = CalcNewVolume(cue.fade);
bool fadeFinished = cue.IsFading && cue.IsFadingIn
? newVolume >= cue.fade.targetVolume
: newVolume <= cue.fade.targetVolume;
if (fadeFinished) {
newVolume = fade.targetVolume;
cue.ClearFadeFlags();
}
cue.audioSource.volume = newVolume;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment