Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>If your code is required to do "A" for case "a" and "B" for case "b" and "C" for case "c", then regardless of what solution you pick, somewhere in the code has to exists something that decides whever or not case 'a/b/c' occurs, and something must exist that will dispatch correct 'A/B/C' action for that case, and of course those A/B/C actions have to be written somewhere in the code, too.</p> <p>Step outside of the code and think about it. If it is specified and must happen - it has to be coded somewhere. You cannot escape that. Now, if the cases/actions are trivial and typical, you might find some more-or-even-more configurable library that accidentally allows you to configure such cases and actions, and off you go, you have it "with no code" and no "clutter". But looking formally, the code is deep there in the library. So, the decider, dispatcher and actions are coded. Just not by you - by someone that guessed your needs.</p> <p>But if your needs are nontrivial and highly specific, for example, if it require your various conditions to decide which a/b/c case is it - then most probably you will have to code the 'decider' part for yourself. That means lots of tree-of-IFs, nested-switches, rules-n-loops, or whatever you like or feel adequate. After this, you are left with dispatch/execute phase, and this can be realized in a multitude of ways - i.e. strategy pattern - it is exactly it: dispatch (by concrete class related to case) and execute (the concrete strategy has the concrete code for the case).</p> <p>Let's try something-like-OO approach:</p> <p>For example, if you have cases a/b/c for UserTypes U1,U2,U3, you could introduce three classes:</p> <pre><code>UserType1 inherits from abstract UserType or implements "DoAThing" interface UserType2 inherits from abstract UserType or implements "DoAThing" interface UserType3 inherits from abstract UserType or implements "DoAThing" interface UserType1 implements virtual method 'doTheThing' that executes actionA UserType2 implements virtual method 'doTheThing' that executes actionB UserType3 implements virtual method 'doTheThing' that executes actionC your Users stop keeping "UserType" of type "int" equal to '1/2/3' - now their type is an object: UserType1, UserType2 or UserType3 </code></pre> <p>whenever you must do the thing for a given user, you now just:</p> <pre><code>result = user.getType().doTheThing( ..params..) </code></pre> <p>So, instead of iffing/switching, you use OO: <code>tell, don't ask</code> rule. If the action-to-do is dependent solely on UserType, then let the UserType perform it. The resulting code is as short as possible - but at the cost of number of classes to create and, well, ...</p> <p>... the decider, dispatcher and actions are still in the code. Actions - obvious - in the various usertype clasess. Dispatch - obvious - virtual call by common abstract base method. And decider..? Well: someone at some point <strong>had to choose and construct the correct UserType object for the user</strong>. If user was stored in the database, if "usertype" is just an integer 1/2/3, then somewhere in your ORM layer those 1/2/3 numbers had to be decoded and translated into UserType1/2/3 classes/objects. That means, that you'd need there a tree-of-ifs or a switch or etc. Or, if you have an ORM smart enough - you just set up a bunch of rules and it did it for you, but that's just again delegating part of the job to more-or-even-more configurable library. Not mentioning that your UserType1/2/3 classes in fact became somewhat .. strategies.</p> <p>Ok, let's attack the 'choose' part.</p> <p>You can build a tree of ifs or switches somewhere to decide and assign, but imperative seems to smell. Or, with OO, you can try to polymorphize something so that "it will just do the right thing", but it will not solve anything since again you will have to choose the object type somewhere. So, let's try data-driven: let's use lookups.</p> <pre><code>we've got five implementations of an action create a hash/dictionary/map add usertype1-&gt;caseA to the map add usertype2-&gt;caseC to the map add usertype3-&gt;caseB to the map add usertype4-&gt;caseA to the map add usertype5-&gt;caseE to the map .... </code></pre> <p>now, whenever you have a user and need to decide, just look it up. Instead of a "case" you may hold a ready to use object of a strategy. Or a callable method. Or a typename. Or whatever you need. The point is that instead of writing</p> <pre><code>if( user.type == 1) { ... } else if( user.type == 2) ... </code></pre> <p>or switching, you just look it up:</p> <pre><code>thing = map[ user.type ] if ( thing is null ) ??? </code></pre> <p>but, mind that without some care, you might sometimes NOT find a match in the map. And also, the map must be PREDEFINED for ALL CASES. So, simple <code>if X &lt; 100</code> may turn up into a <strong>hundred</strong> of entries 0..99 inside the map. </p> <p>Of course, instead of a map, you may use some rule-engine and you could define a mapping like</p> <pre><code>X&lt;100 -&gt; caseA X&gt;=100 -&gt; caseB </code></pre> <p>and then 'run' the rules against your usertype and obtain a 'result' that will tell you "caseA".</p> <p>And so on.</p> <p>Each of the parts - decide, dispatch, execute - you may implement in various ways, shorter or longer, more or less extensible, more or less configurable, as OO/imperative/datadriven/functional/etc - but you cannot escape them:</p> <ul> <li>you have to define the discriminant of the cases</li> <li>you have to define the implementation of the actions</li> <li>you have to define the mapping case-to-action</li> </ul> <p>How to do them, is a matter of your aesthetics, language features, frameworks, libraries and .. time you want to spend on creating and mantaining it.</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload