Last active
March 5, 2018 23:51
-
-
Save polerin/44d3b859a051bcd095742f5894efc98f to your computer and use it in GitHub Desktop.
Pseudo-enum "allowed values" pattern for Javascript
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
/** | |
* New pattern, pseudo-enum. | |
* Benefits: | |
* - no magic strings! | |
* - possible IDE completion | |
* - easier to track down what values are allowable (explicit list location) | |
* - better self documenting (container naming describes the intent of the field) | |
* - easy to inspect the object and see valid states in dev tools | |
* - possibly better on memory? Need to check references | |
* - feels like it would be faster, especially for large sets. Need to verify. | |
* | |
* Drawbacks: | |
* - probably a few more characters | |
* - if using int's you'd need to look up the matching state in the table. | |
* (though if it's not a bitwise flagging thing, use a string?) | |
*/ | |
var NewFooFactory = function() { | |
// You can pass this in if you feel like it. Yay extension! | |
// It's basically an enum. I'm setting this up for bitwise flagging, but | |
// that's just habit. | |
// | |
// You could also make this a property of NewFooFactory to shorten & align | |
// usage, makes it a bit like a class constant. This would make it less | |
// open to extension by mixin/whatever, but that's a different issue. | |
var _allowedStates = { | |
off : 0, | |
open : 1, | |
pendingChange : 2, | |
locked : 4 | |
}; | |
return { | |
// Freezing it to prevent accidental change the of state value | |
allowedStates: Object.freeze(_allowedStates), | |
state : _allowedStates.off, | |
setState: function(newState) { | |
if (typeof this.allowedStates[state] === undefined) { | |
// For larger sets, I suspect this is a lot faster than .includes() | |
console.error("Attempting to set an invalid foo state: " + state); | |
return; | |
} | |
// You could do some bitwise toggling for flagged states, whatever. | |
this.state = state; | |
}, | |
isState: function(queryState) { | |
// Again, you could do bitwise or other logic here, but for simplicity.. | |
return (queryState === this.state); | |
} | |
doAFooThing: function() { | |
if (!this.isState(this.allowedStates.open)) { | |
console.error("Attempting to do a foo thing when object isn't open"); | |
} | |
// do your foo thing. | |
return "a useful value. Because why not?"; | |
} | |
}; | |
}; | |
/** | |
* Old pattern: array of string values | |
*/ | |
var OldFooFactory = function() { | |
// You could still pass this in if you felt like it. | |
var allowedStates = [ | |
"off", | |
"open", | |
"pendingChange", | |
"locked" | |
]; | |
return { | |
// Boo magic string boo! | |
state : "off", | |
setState: function(newState) { | |
if (allowedStates.includes(state)) { | |
console.error("Attempting to set an invalid foo state: " + state); | |
return; | |
} | |
// Could do some bitwise toggling for flagged states, whatever. | |
this.state = state; | |
}, | |
isState: function(queryState) { | |
// Again, you could do bitwise or other logic here, but for simplicity.. | |
return (queryState === this.state); | |
} | |
doAFooThing: function() { | |
// Eewwww magic string. Bad enough inside the class but.. | |
if (!this.isState("open")) { | |
console.error("Attempting to do a foo thing when object isn't open"); | |
} | |
// do your foo thing. | |
return "a useful value. Because why not?"; | |
} | |
}; | |
}; | |
// Usage examples | |
var oldFoo = OldFooFactory(); | |
// Boo magic strings! And outside of its defined context too! | |
oldFoo.setState("open"); | |
oldFoo.doAFooThing(); | |
// Invalid but harder to know w/o autocomplete | |
oldFoo.setState("notReallyAState"); | |
// New and shiny pattern! | |
var shinyFoo = NewFooFactory(); | |
// A bit longer, but feels better? | |
shinyFoo.setState(shinyFoo.allowedStates.open); | |
shinyFoo.doAFooThing(); | |
//invalid, but easier to track down why, also maybe IDE help? | |
shinyFoo.setState(shinyFoo.allowedStates.notARealState); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment