Last active
May 25, 2016 22:20
-
-
Save michaelbartnett/834d0b7dabdd130d538a to your computer and use it in GitHub Desktop.
Justification of ternary operator
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // 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