Давайте посмотрим на вашу проблему с другой точки зрения. При разработке тела кода я стараюсь применять следующий принцип:
- Сделайте это правильно.
- Проясни.
- Сделайте это лаконичным.
- Сделай это быстро.
... в таком порядке.
Все это в той или иной степени субъективно. Тем не менее, разумные люди склонны находить общий язык - и часто есть больше согласия относительно их противоположностей. Но это в сторону ...
Первым приоритетом здесь является обеспечение правильной работы вашего кода. Ясно, что есть несколько реализаций, которые достигают этого - но я также добавил бы, что важно, чтобы было легко продемонстрировать что реализация правильная . Один из способов добиться этого - сделать код более похожим на спецификацию (подробнее об этом позже).
Вторым приоритетом является обеспечение того, чтобы в будущем , когда разработчик (включая первоначального автора!) Посмотрел на этот код, он мог сразу понять, что он делает. Чем сложнее (читай: fancy ) реализация, тем сложнее разработчику сразу понять, что делает код.
Третий приоритет - короткий, лаконичный код, который частично противоположен первым двум. Желание сделать код более лаконичным может привести к использованию более сложных конструкций, чем на самом деле необходимо решить проблему. Хотя важно, чтобы код был коротким, мы не должны делать это, делая его непостижимо плотным.
Последний приоритет - производительность - имеет значение только тогда, когда это важно. Под этим я подразумеваю, что вам не следует усложнять реализацию с точки зрения производительности, если вы не выполнили профилирование и не определили его как узкое место в вашей системе.
Итак, теперь, когда мы рассмотрели принципы, которые должны определять наши решения, давайте применим их к рассматриваемой проблеме. Вы предоставили очень четкую спецификацию поведения кода. , Давайте попробуем придерживаться их:
void YourMethod( int oldValue, int newValue )
{
bool oldValueNonZero = oldValue != 0;
bool newValueNonZero = newValue != 0;
if( oldValueNonZero ) { X(); }
if( newValueNonZero ) { Y(); }
if( oldValueNonZero && newValueNonZero ) { Z(); }
}
Так почему же мне нравится эта конкретная реализация? Давайте разберемся с этим.
Сначала , обратите внимание, что я решил создать временное логическое значение, чтобы получить результат проверки старого / нового значения на предмет того, являются ли они ненулевыми. Получая эти значения, я избегаю выполнения вычислений более одного раза, а также делаю код более читабельным (см. Ниже).
Второе , выбрав описательные имена oldValueNonZero
и newValueNonZero
Я делаю реализацию четко указать мои ожидания. Это улучшает читаемость кода и четко передает мои намерения будущим разработчикам, которые должны его прочитать.
Третий , обратите внимание, что тело теста if()
заключено в скобки {
и }
- это уменьшает вероятность того, что будущие изменения в реализации нарушат поведение - на например, случайно включив новый случай. Использование однострочного ifs
- рецепт для будущих проблем.
Наконец , я не пытаюсь замкнуть сравнение и рано выйти из функции. Если производительность была чрезвычайно важна, ранний выход может быть полезным. Но в противном случае проще понять поведение метода, если существует только одна точка выхода (1) .
Этот код выполняет то, что написано в спецификации? Я так считаю.
Легко ли читать и понимать. По крайней мере, на мой взгляд, я бы сказал, да.
Является ли этот код наиболее компактным или изощренным способом короткого замыкания логики? Почти наверняка нет ... но, на мой взгляд, другие качества более чем восполняют это.
Нравится ли вам эта конкретная структура кода или нет, это в некоторой степени вопрос вкуса и стиля. Но я надеюсь, что принципы, которые я изложил о том, как я решил организовать это, могут помочь вам принять такие решения в будущем.
Вы указали, что иногда вы сталкиваетесь с похожими проблемами с «логической сеткой», но те, где число случаев более многочисленное. Проблемы такого рода могут усложняться по двум отдельным причинам:
- Количество значений, которые параметры могут принимать при увеличении - они могут принимать общий вид
MxN
.
- Количество измерений увеличивается - другими словами, в правилах есть еще переменные:
MxNxOxP...xZ
.
Одним из обобщенных решений проблемы (как указывает другой ответ) является кодирование решения в виде многомерной матрицы - и определение набора действий для каждого случая. Тем не менее, вполне возможно, что правила перекрываются - и, вероятно, желательно свести эквивалентные случаи вместе для простоты.
Мой ответ на общий случай ... это зависит. Если условия могут быть уменьшены до некоторого очень небольшого числа случаев, то крайне важно, чтобы логика if / else по-прежнему оставалась лучшим способом решения проблемы. Если число условий очень велико, то может иметь смысл использовать декларативный подход, при котором вы используете некую таблицу поиска или матрицу для кодирования случаев.
1> - Одним из распространенных исключений из принципа наличия только одной точки выхода из метода являются предварительные условия. Лучше избежать вложенности и перевернутой логики, сначала проверив все / любые предварительные условия и не выполнив (выйдя) из метода, если они нарушены.