你如何编写代码的逻辑是对未来更多的枚举保护? [英] How do you write code whose logic is protected against future additional enumerations?

查看:116
本文介绍了你如何编写代码的逻辑是对未来更多的枚举保护?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在描述这个问题很难。也许这就是为什么我有一个很难找到一个很好的解决方案(的话就是不配合)。让我通过代码解释:

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:

// original code
enum Fruit
{ 
    Apple,
    Orange,
    Banana,
}

...

Fruit fruit = acquireFruit();
if (fruit != Fruit.Orange && fruit != Fruit.Banana)
    coreFruit();
else
    pealFruit();
eatFruit();

现在假装多年的发展与这三种类型去了。上述逻辑的不同口味的传播在整个存储过程,SSIS包,的Windows应用程序,Web应用,Java应用程序,Perl脚本等....

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....

最后:

// new code
enum Fruit
{ 
    Apple,
    Orange,
    Banana,
    Grape,
}

在大多数情况下, 系统,直到葡萄是用来运行正常。然后不适当的时候是没有必要的系统行为的部分墙皮和/或取心的葡萄,或期望的。

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.

你坚持什么样的指导方针,以使这些混乱是可以避免的? 。我的选择是对旧代码抛出,如果它没有被重构考虑新的枚举异常

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.

我已经想出了一个在黑暗中拍摄:

I've come up with a shot in the dark:

#1避免不是逻辑像这样

// select fruit that needs to be cored
select Fruit from FruitBasket where FruitType not in(Orange, Banana)

#2使用精心构造NotIn(需要时)方法

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<SalesOrderType> seta = new List<SalesOrderType>
        {
            SalesOrderType.Allowance,
            SalesOrderType.NonAllowance,
            SalesOrderType.CompanyOrder,
            SalesOrderType.PersonalPurchase,
            SalesOrderType.Allotment,
        };
        setb.ForEach(o => 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());
    }
}

现在,我可以放心地,使用这样的代码:

Now, I can safely, use code like this:

bool needsCoring = fruit.IsNot(Fruit.Orange, Fruit.Banana);

如果这个代码在整个系统中传播,当葡萄来擀成镇(异常将被抛出QA将赶上'全身所有)。

If this code gets propagated throughout the system, exceptions will be thrown when the Grape comes rolling into town (qa will catch 'em all).

这是计划反正。这个问题似乎应该是很常见的,但我似乎无法找到对谷歌(可能是我自己的错)任何东西。

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).

你是如何处理所有这?

How are you all handling this?

更新:

我觉得回答这个问题是建立一个赶上一切机制,将暂停处理,并提醒测试人员和开发人员的事实,即新的枚举需要考虑。 开关...默认是伟大的,如果你拥有它。

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.

如果C#的没有有开关...默认情况下,我们可能右上面的代码是这样的:

If C# didn't have switch ... default, we might right the above code like this:

Fruit fruit = acquireFruit();
if (fruit != Fruit.Orange && fruit != Fruit.Banana)
    coreFruit();
else if(fruit == Fruit.Apple)
    pealFruit();
else
    throw new NotSupportedException("Unknown Fruit:" + fruit)
eatFruit();



免责声明:

您真的不应该使用任何上面的伪代码。它可能(?)编译或甚至工作,但它是可怕的代码,真的。我看到了很多不错的解决方案,在这个线程,如果你正在寻找一个基于面向对象的方法。一个很好的解决方案,当然,将所有的开关和一个集中的方法检查(工厂方法是什么令我)。重要的是,也需要顶级同行代码审查。

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.

推荐答案

如果我理解正确你的问题,最常见的做法是抛出一个引发NotSupportedException NotImplementedException

If I understood your question correctly, the most common practice is to throw an NotSupportedException or NotImplementedException.

switch (fruit.Kind) {
case Fruit.Apple:
    Bite(fruit);
    break;
case Fruit.Banana:
    FeedToMonkey(fruit);
    break;
default: throw new NotSupportedException("Unknown fruit.");
}



至于加入该会破坏现有的新枚举值的如果-不-is 的逻辑,我相信使用枚举是在这种情况下,一个糟糕的选择。您的项目显然有一个明显不同的行为,他们不喜欢如颜色。也许是最好的做的选项负责决定他们应该如何处理。然后,你应该替换多态性枚举。

As for adding new enum values which would break existing if-not-is logic, I believe using enum is a poor choice in this case. Your items clearly have a distinctively different behavior, they're not like e.g. colors. Perhaps it is best to make the options responsible for deciding how they should be treated. Then you should replace enums with polymorphism.

这篇关于你如何编写代码的逻辑是对未来更多的枚举保护?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆