Como você escreve código cuja lógica está protegida contra futuras enumerações adicionais?

Estou com dificuldade para descrever esse problema. Talvez seja por isso que estou tendo dificuldade em encontrar uma boa solução (as palavras simplesmente não estão cooperando). Deixe-me explicar via código:

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

...

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

Agora finja que anos de desenvolvimento passam com esses três tipos. Diferentes sabores da lógica acima se propagam por procedimentos armazenados, pacotes SSIS, aplicativos Windows, aplicativos Web, aplicativos Java, scripts Java e etc ...

Finalmente:

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

Na maioria das vezes, o "sistema" funciona bem até que as uvas sejam usadas. Em seguida, partes do sistema agem de maneira inadequada, descascando e / ou descascando uvas quando não é necessário ou desejado.

Que tipo de diretrizes você segue para que essas bagunças sejam evitadas? Minha preferência é que o código antigo gere uma exceção se não tiver sido refatorado para considerar novas enumerações.

Eu vim com um tiro no escuro:

# 1 Evite "Not In Logic" como este

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

# 2 Use métodos NotIn () cuidadosamente construídos quando necessário

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());
    }
}

Agora, posso usar com segurança código como este:

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

Se esse código for propagado por todo o sistema, serão lançadas exceções quando o Grape chegar à cidade (o qa os capturará).

Esse é o plano de qualquer maneira. O problema parece que deve ser muito comum, mas não consigo encontrar nada no google (provavelmente por minha própria culpa).

Como vocês estão lidando com isso?

ATUALIZAR:

Sinto que a resposta para esse problema é criar um mecanismo "pegar tudo o resto" que interrompe o processamento e alerta os testadores e desenvolvedores para o fato de que a nova enumeração precisa de consideração. "switch ... default" é ótimo se você o tiver.

Se c #não tem switch ... padrão, podemos corrigir o código acima desta forma:

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();

AVISO LEGAL:

Você realmente não deve usar nenhum dos pseudo-códigos acima. Pode (?) Compilar ou até funcionar, mas é realmente um código horrível. Eu vi muitas soluções legais nesse segmento, se você está procurando uma abordagem baseada em OOP. Uma boa solução, é claro, coloca toda a troca e verificação em um método centralizado (um método de fábrica é o que me impressiona). A revisão de código por pares também será necessária.

questionAnswers(10)

yourAnswerToTheQuestion