Note that there are some explanatory texts on larger screens.

plurals
  1. POHow do you write code whose logic is protected against future additional enumerations?
    primarykey
    data
    text
    <p>I'm having a hard time describing this problem. Maybe that's why I'm having a hard time finding a good solution (the words just aren't cooperating). Let me explain via code:</p> <pre><code>// original code enum Fruit { Apple, Orange, Banana, } ... Fruit fruit = acquireFruit(); if (fruit != Fruit.Orange &amp;&amp; fruit != Fruit.Banana) coreFruit(); else pealFruit(); eatFruit(); </code></pre> <p>Now pretend years of development go by with these three types. Different flavors of the above logic propagate throughout stored procedures, SSIS packages, windows apps, web apps, java apps, perl scripts and etc....</p> <p>Finally:</p> <pre><code>// new code enum Fruit { Apple, Orange, Banana, Grape, } </code></pre> <p>Most of the time, the "system" runs fine until Grapes are used. Then parts of the system act inappropriately, pealing and/or coring grapes when it's not needed or desired.</p> <p>What kind of guidelines do you adhere to so these messes are avoided? My preference is for old code to throw an exception if it hasn't been refactored to consider new enumerations.</p> <p>I've come up with a shot in the dark:</p> <p><strong>#1 Avoid "Not In Logic" such as this</strong></p> <pre><code>// select fruit that needs to be cored select Fruit from FruitBasket where FruitType not in(Orange, Banana) </code></pre> <p><strong>#2 Use carefully constructed NotIn() methods when needed</strong></p> <pre><code>internal static class EnumSafetyExtensions { /* By adding enums to these methods, you certify that 1.) ALL the logic inside this assembly is aware of the * new enum value and 2.) ALL the new scenarios introduced with this new enum have been accounted for. * Adding new enums to an IsNot() method without without carefully examining every reference will result in failure. */ public static bool IsNot(this SalesOrderType target, params SalesOrderType[] setb) { // SetA = known values - SetB List&lt;SalesOrderType&gt; seta = new List&lt;SalesOrderType&gt; { SalesOrderType.Allowance, SalesOrderType.NonAllowance, SalesOrderType.CompanyOrder, SalesOrderType.PersonalPurchase, SalesOrderType.Allotment, }; setb.ForEach(o =&gt; seta.Remove(o)); // if target is in SetA, target is not in SetB if (seta.Contains(target)) return true; // if target is in SetB, target is not not in SetB if (setb.Contains(target)) return false; // if the target is not in seta (the considered values minus the query values) and the target isn't in setb // (the query values), then we've got a problem. We've encountered a value that this assembly does not support. throw new InvalidOperationException("Unconsidered Value detected: SalesOrderType." + target.ToString()); } } </code></pre> <p>Now, I can safely, use code like this:</p> <pre><code>bool needsCoring = fruit.IsNot(Fruit.Orange, Fruit.Banana); </code></pre> <p>If this code gets propagated throughout the system, exceptions will be thrown when the Grape comes rolling into town (qa will catch 'em all).</p> <p>That's the plan anyway. The problem seems like it should be very common, but I can't seem to find anything on google (probably my own fault).</p> <p>How are you all handling this?</p> <p><strong>UPDATE:</strong></p> <p>I feel the answer to this problem is create a "catch everything else" mechanism that halts processing and alerts testers and developers to that fact that the new enumeration needs consideration. "switch ... default" is great if you have it.</p> <p>If C# <strong>didn't</strong> have switch ... default, we might right the above code like this:</p> <pre><code>Fruit fruit = acquireFruit(); if (fruit != Fruit.Orange &amp;&amp; fruit != Fruit.Banana) coreFruit(); else if(fruit == Fruit.Apple) pealFruit(); else throw new NotSupportedException("Unknown Fruit:" + fruit) eatFruit(); </code></pre> <p><strong>DISCLAIMER:</strong></p> <p>You really shouldn't use any of the above pseudo code. It may(?) compile or even work, but it's horrible code, really. I saw a lot of nice solutions in this thread if you're looking for an OOP-based approach. A good solution, of course, places all the switching and checking in a centralized method (a factory method is what strikes me). Peer code review on top of that will also be required.</p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
 

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