I'm surprised there was no mention of the Specification pattern. https://en.wikipedia.org/wiki/Specification_pattern
Anti-if: The Missing Patterns
61–70 of 74 posts
Re: Anti-if: The Missing Patterns
#62Earlier quoted context omitted.
> The point of passing boolean params instead of named functions is that most of the time there is shared code between the two paths and not literally all the code is enclosed in either the if or the else block If you have this method(bool arg) { // block A if (arg) { // block B1 } else { // block B2 } // block C } you can replace it with method1() { A(); B1(); C(); } method2() { A(); B2(); C(); } where A(), B1(), B2…
> This makes the code clearer, IMO. Depends on how much state you need to pass between A, B1, B2 and C.
Re: Anti-if: The Missing Patterns
#63Earlier quoted context omitted.
> I don't really see how using an Optional type would remove the if(null) checks. It just makes the meaning explicit, which is good but not remedying the original problem. If you always use `Option` (or whatever equivalent your language/base library provides) and banish `null` from your source code you've effectively avoided all null pointer exceptions and can therefore remove all null checks.
Well, no, you still need the null checks. You've eliminated null pointer exceptions by guaranteeing that null is always checked for before the value is used -- you're still checking, but you're now immune to unanticipated crashes. (That would have resulted from null dereferences, that is. You can still have other bugs.)
Re: Anti-if: The Missing Patterns
#64Misko Hevery did a Google Tech Talk about this some years ago, he also use a very similar example with switching on the birds. https://www.youtube.com/watch?v=4F72VULWFvc
Re: Anti-if: The Missing Patterns
#65I'm afraid the article has left me unconvinced. I'm open to having my mind changed on the matter, though. The point of passing boolean params instead of named functions is that most of the time there is shared code between the two paths and not literally all the code is enclosed in either the if or the else block. If the author was intending just to restrict to that one specific case where there was no overlap whatso…
The cases where there's a lot of commonality are cases for polymorphism instead. If you have something like:
def doSomething(useCache: Boolean) = {
//long and complicated function
if(useCache) cache.lookup(foo) else calculate(foo)
//more long and complicated stuff
}
then it's probably worth breaking that out as trait FooProvider { def get(foo: String): Foo }
object CachedFooProvider extends FooProvider {
def get(foo: String) = cache.lookup(foo)
}
object CalculatingFooProvider extends FooProvider {
def get(foo: String) = calculate(foo)
}
and passing the FooProvider instead - that way you pass something with a clear semantic meaning rather than a bare Boolean.> I'd say that at least this trivial example would be best handled by some data-driven approach. Look up salaries in a database instead.
Very much disagree. Every time I've seen a program do logic based on what was in the database it's been very hard to debug or reason about. Anything you can possibly do to make it unit-testable instead of needing to test against a prod database dump is worth it.
> Surely this is easier to misread than the if statement example, which in my mind closely matches how I think.
Disagree. The if looks like control flow when it's not actually control flow. If you're just doing Boolean logic, make it look like Boolean logic.
> But even then, that only works when the actual return type is boolean and there are no state changes made.
Yes, that's exactly the point. It's well worth forcing those things into different functions, so that you can inspect what's going on. Particularly when debugging, that means you can step over the conditional and see what the result was, rather than having to step through each if because you never know when one of the branches might actually do something.
> I don't really see how using an Optional type would remove the if(null) checks. It just makes the meaning explicit, which is good but not remedying the original problem.
Optionals have polymorphic methods so you can express most common use cases directly rather than having to branch, i.e. rather than:
val user = if(null == userId) null else userRepository.lookup(userId)
you can just do val user = userId flatMap userRepository.lookup
map/flatMap/foreach each have their own specific semantics so it's easy to see what's going on, whereas an "if(null == userId)" could mean anything. (If your logic really doesn't fit into any of the standard use cases then you may need to use an if even with an optional, but such cases will stand out when reading the code - as they should).Re: Anti-if: The Missing Patterns
#66If you want extreme case of removing all ifs, see how ifTrue: and friends are implemented in Smalltalk (ie. true and false are instances of different classes), although essentially all compiler implementations turn that into normal conditional branches in generated bytecode.
[1] - http://git.savannah.gnu.org/gitweb/?p=smalltalk.git;a=blob;f...
Re: Anti-if: The Missing Patterns
#67I'm disappointed that of all the patterns presented, most of them actually increase the complexity to some extent, and none of them are the use of a lookup table/array. IMHO that is the "real anti-if pattern" here, and it can immensely simplify code. I've taken long, convoluted nested if/else chains with plenty of duplication and turned them into a single array lookup. A bonus is that performance and size often benef…
Midway between nested conditionals and lookups is using flags and a tablesque series of conditionals. Redundant but easily understood (and therefore testable).
I first learned about this technique from Code Complete (linked in the TFA). IIRC, McConnell used the term "truth tables".
"...since the code is far less branchy."
Code construction GoF style Design Patterns just introduce another level of indirection. That's it. (Insert cliche here.)
"Design Patterns" are overused, especially in the Java world (eg J2EE/Spring, Service Providers, Strategies, DI, IoC).
The "design" part is figuring out the least complicated balance between conditional branches and call stack depth, between composition and inheritance.
Rant over.
Re: Anti-if: The Missing Patterns
#68Earlier quoted context omitted.
Efficiency-wise I agree, but if I don't care about efficiency then I often prefer an if-chain (or better still, a switch-case). The point is that the desired mapping is known not just at compile time, but is explicitly known to the programmer. So it is clearer if it right there in the executable code rather than hidden away in some moving part (the mapping data structure).
switch-case is often compiled to a table lookup, but if your expressions are constant (at least for the part where the table is being used), the latter is definitely less verbose. So it is clearer if it right there in the executable code rather than hidden away in some moving part (the mapping data structure). Following several levels of nested if/else is "clearer" than going to the right array index? I don't know wh…
The moving appart that gets hidden away is the table (or more often, Hack mandictionary) that is stored somewhere in memory. If you are lucky, it is stored statically. It is never defined in quite the same place as the decision that it implements.
Re: Anti-if: The Missing Patterns
#69Good luck doing that in a production environment, and not while "theorycrafting". You have waaaay too much free time.
Re: Anti-if: The Missing Patterns
#70> Patten 3: NullObject/Optional over null passing The solution here disperses the responsibility of keeping sumOf safe to its callers, all over the place, instead of a single location in sumOf.
Agreed. The sumOf function is now made more brittle by that fact, and would be unworkably so in a publicly-available API. This is easily fixed by a try-catch block if one finds that they are indeed allergic to if statements, although that too is a form of flow control. If you are programming in Go, you're going to have to bite the bullet and use an if. (I suspect the author would prefer jumping in a lake over program…