Мне сложно описать эту проблему. Возможно, поэтому мне трудно найти хорошее решение (слова просто не сотрудничают). Позвольте мне объяснить через код:
// original code
enum Fruit
{
Apple,
Orange,
Banana,
}
...
Fruit fruit = acquireFruit();
if (fruit != Fruit.Orange && fruit != Fruit.Banana)
coreFruit();
else
pealFruit();
eatFruit();
Теперь делайте вид, что годы развития проходят с этими тремя типами. Различные варианты вышеуказанной логики распространяются во всех хранимых процедурах, пакетах SSIS, приложениях для Windows, веб-приложениях, java-приложениях, perl-скриптах и т.д....
Наконец:
// new code
enum Fruit
{
Apple,
Orange,
Banana,
Grape,
}
В большинстве случаев "система" работает нормально до тех пор, пока не будет использован Виноград. Затем части системы действуют ненадлежащим образом, пилинг и/или скручивание винограда, когда это не нужно или желательно.
Какие руководящие принципы вы придерживаетесь, чтобы избежать этих беспорядков? Мое предпочтение заключается в том, что старый код генерирует исключение, если он не был реорганизован для рассмотрения новых перечислений.
Я придумал выстрел в темноте:
# 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());
}
}
Теперь я могу безопасно использовать код следующим образом:
bool needsCoring = fruit.IsNot(Fruit.Orange, Fruit.Banana);
Если этот код будет распространяться по всей системе, будут выбрасываться исключения, когда Виноград скачет в город (qa поймает их все).
Это план в любом случае. Проблема кажется, что она должна быть очень распространенной, но я не могу найти что-либо в google (возможно, моя собственная ошибка).
Как вы справляетесь с этим?
UPDATE:
Я чувствую, что ответ на эту проблему создает механизм "поймать все остальное", который останавливает обработку и предупреждает тестеров и разработчиков о том, что новое перечисление нуждается в рассмотрении. "switch... default" отлично, если он у вас есть.
Если у С# не было есть переключатель... по умолчанию, мы могли бы изменить приведенный выше код следующим образом:
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();
ОТКАЗ:
Вы действительно не должны использовать какой-либо из вышеуказанных псевдокодов. Он может (?) Компилировать или даже работать, но это ужасный код, действительно. Я видел много хороших решений в этом потоке, если вы ищете подход на основе ООП. Хорошее решение, конечно, помещает все переключения и проверки в централизованный метод (метод factory - это то, что меня поражает). Кроме того, потребуется также просмотр кода коллегиального обзора.