TE
科技回声
首页24小时热榜最新最佳问答展示工作
GitHubTwitter
首页

科技回声

基于 Next.js 构建的科技新闻平台,提供全球科技新闻和讨论内容。

GitHubTwitter

首页

首页最新最佳问答展示工作

资源链接

HackerNews API原版 HackerNewsNext.js

© 2025 科技回声. 版权所有。

The death of if/else cascades, a lightweight alternative

85 点作者 kyleburton超过 15 年前

13 条评论

DrJokepu超过 15 年前
Forgive my ignorance but how is this different from a switch statement? Besides the slightly different syntax, of course. Many modern languages interpret switch statements as lookup tables. And that in compiled languages the lookup table is generated at compile time and this approach generates the lookup table at runtime, possibly wasting expensive CPU cycles.<p>Basically we're dealing here with a special set of condition-action pairs. The property that makes them special is that the conditions are mutually exclusive, that is, the order of condition checks is not important. Of course, most of the time, non-exclusive conditions can be restructured to exclusive conditions as long as the conditions checks themselves don't have side effects (that it, they don't modify the state). This is a solved problem in software construction. For binary conditions, you use if statements (or the equivalent in Your Favourite Language), for simple multiple conditions, you use a switch statement and for complex state-condition check you create a class/closure that takes the state and determines if the condition evaluates to true and then another function executes the action and then you loop through the condition checkers. We're talking about concepts that have been around for almost half a century. There's no need to reinvent the wheel. Instead, read some books and learn how developers solved these basic software construction problems in the past.<p>The problem is, many developers are too smart for their own good. They come up with new "design patterns" while ignoring the mountains of experience and knowledge past programmers have built up. More often than not these "groundshaking new ideas" lead to huge pains one way or other later down the codebase lifecycle. This is why C++ is so hard; you can come up with many "innovative" ways to do stuff only to discover a year later that your "innovation" made your codebase completely unmaintainable. Don't get me wrong, innovation is really cool and I support it but it doesn't hurt to consult the existing knowledge (either by research or asking more experienced developers) before going down your path.
评论 #979855 未加载
评论 #979863 未加载
评论 #979805 未加载
评论 #979839 未加载
评论 #979889 未加载
iamaleksey超过 15 年前
This is much better handled in languages with pattern matching. And you can match on arbitrary fields.<p>Erlang:<p><pre><code> surf(Channels) -&#62; lists:foreach(fun process/1, Channels). process(#channel{genre = "football"} = Channel) -&#62; record(Channel); process(#channel{genre = "comedy", repeat = false} = Channel) -&#62; record(Channel); process(#channel{genre = "crime", show_title = "Cops"}) -&#62; skip; process(#channel{genre = "crime"} = Channel) -&#62; record(Channel); process(_) -&#62; skip. </code></pre> Full gist: <a href="http://gist.github.com/250304" rel="nofollow">http://gist.github.com/250304</a>
评论 #980094 未加载
评论 #980074 未加载
barrkel超过 15 年前
Two points.<p>First, there's the distinction between essential complexity and non-essential complexity. What the OP is talking about is trying to reduce the non-essential complexity by using a more appropriate abstraction. Nested if-then-else can introduce non-essential complexity merely by having dead nested cases which are actually obviated by outer cases; looking at a complex version of this code, it can become quite difficult to see where exactly the code is supposed to flow under what circumstances, as there can seem to be conflicting assumptions in different areas.<p>Secondly, once upon a time I invented a scheme to solve this problem in the best way I thought possible, and called it "matrix inheritance". The problem with inheritance and subclassing is that it only handles a single dimension of customization. Suppose you have two dimensions, genre and type, such as [comedy, drama] and [movie, series]. If you were to try and classify any given thing under a classical type breakdown, you could subclass by one axes or the other, but you would need to duplicate subclassing for the remaining axes. So, you could end up with Programme, Comedy &#60;: Programme, Drama &#60;: Programme, but then you'd need ComedyMovie, ComedySeries, DramaMovie, DramaSeries, duplicating the kind axis in the two different branches.<p>The matrix inheritance concept basically takes the cartesian product of all option axes, essentially modelling it as an n-dimensional space, and then applies behaviour to sub-spaces of this space. So, you could apply conditions to [drama, <i>] and [</i>, series], with these two representing slices of the example 2-dimensional space described above. The advantage of modelling things this way is that it is declarative: you can analyse overlaps and identify un-covered space.
评论 #980630 未加载
评论 #979987 未加载
评论 #979942 未加载
amix超过 15 年前
This is a common practice in Python and most of the implementations I have seen (and used) use it in a following way:<p><pre><code> handler = { 'football': handle_football, 'comedy': handle_comedy, 'crime': hendle_crime }.get handler('crime')(...)</code></pre>
评论 #979695 未加载
评论 #979688 未加载
评论 #979690 未加载
thaumaturgy超过 15 年前
I must be turning into a cranky oldpants programmer.<p>As I see it, this eliminates a large block of logic, but it doesn't actually make the program less complex.<p>That is, the program is still making the same decisions, and you still have the same amount of fragmented business logic handling all of your special cases; it's just no longer dropping into those special cases by way of a monolithic if/else statement.<p>On the other hand, this is potentially adding a dangerous bug in the program; if your dispatch table gets corrupted for some reason, or an OBO gets introduced (that <i>never</i> happens!), you could end up trying to jump to a random pointer address.<p>EDIT: Higher level languages, like Javascript, will handle it somewhat more gracefully of course. But, it would still cause an error of some kind.
评论 #980144 未加载
domnit超过 15 年前
Schematic tables [<a href="http://subtextual.org/OOPSLA07.pdf" rel="nofollow">http://subtextual.org/OOPSLA07.pdf</a>] are an extension of this idea that can handle arbitrarily complex conditionals. Unfortunately, they only work in Subtext [<a href="http://subtextual.org/" rel="nofollow">http://subtextual.org/</a>], an unreleased research language. Schematic tables take advantage of the 2-dimensional visual layout of Subtext (as opposed to linear text, like most languages); it would be very interesting to see something similar in text.
randallsquared超过 15 年前
&#62; New behavior can be injected without changing any core code.<p>While it probably is easier to change the dispatching part of this code now, I'd hesitate to call the dispatching part the "core" code; it seems like the functions that actually do the work have a better claim to that, so what this does is to move the core code away from the calling location, and removes the name, so the core code has to be found by tracing or simulation, rather than by reading. Of course, a comment pointing at the file and line of the actions table would be nice, but might get out of date, and since the table is passed rather than global, at some point there's going to be more than one actions table that do other things as well...<p>This code will be a nightmare to debug when there's a few thousand lines of similar redirections. :/
评论 #979870 未加载
DannoHung超过 15 年前
This technique is ancient for anyone that has an associative array and function objects.
评论 #980353 未加载
zaphar超过 15 年前
Erlangs pattern matching is a superior form of this concept. Clojures multimethods also implement a similar style to erlangs pattern matching dispatch. Both of those avoid some of the objections described in other comments here. The code is still in core and is easier to read than a dispatch table.
xtho超过 15 年前
Instead of mimicing method dispatch, shouldn't this be handled by a factory that returns an appropriate action object?<p>Okay, the author writes:<p>&#62; Skeptics may say, that problem has been solved in the Object Oriented world by [...] Wow, that is a lot of work, and think about the number of classes and lines of code you will end up with!<p>First, you don't have classes in a prototype-based language. Secondly, do it right or don't do it at all.
Nycto超过 15 年前
This looks like a functional (The paradigm, not the adjective) version of the Visitor pattern: <a href="http://en.wikipedia.org/wiki/Visitor_pattern" rel="nofollow">http://en.wikipedia.org/wiki/Visitor_pattern</a>
clistctrl超过 15 年前
So this strikes me as a fairly common problem. If I was going to solve it I don't think i would use a "dispatch table" I wold prefer to use polymorphism. In C# maybe i would have an interface with the common method. An abstract class to provide a default implementation. Then several classes which either implement the interface directory, or inherit the abstract class.
clistctrl超过 15 年前
if/else cascades will live as long as long as junior developers themselves. Jeez some of the code I've personally written when I was younger...