Что легче понять: большой логический оператор (довольно сложный) или тот же оператор, разбитый на методы предикатов (много дополнительного кода для чтения)?
Вариант 1, большое логическое выражение:
private static bool ContextMatchesProp(CurrentSearchContext context, TValToMatch propVal)
{
return propVal.PropertyId == context.Definition.Id
&& !repo.ParentId.HasValue || repo.ParentId == propVal.ParentId
&& ((propVal.SecondaryFilter.HasValue && context.SecondaryFilter.HasValue && propVal.SecondaryFilter.Value == context.SecondaryFilter) || (!context.SecondaryFilter.HasValue && !propVal.SecondaryFilter.HasValue));
}
Вариант 2. Условия разбиты на методы предикатов:
private static bool ContextMatchesProp(CurrentSearchContext context, TValToMatch propVal)
{
return MatchesDefinitionId(context, propVal)
&& MatchesParentId(propVal)
&& (MatchedSecondaryFilter(context, propVal) || HasNoSecondaryFilter(context, propVal));
}
private static bool HasNoSecondaryFilter(CurrentSearchContext context, TValToMatch propVal)
{
return (!context.No.HasValue && !propVal.SecondaryFilter.HasValue);
}
private static bool MatchedSecondaryFilter(CurrentSearchContext context, TValToMatch propVal)
{
return (propVal.SecondaryFilter.HasValue && context.No.HasValue && propVal.SecondaryFilter.Value == context.No);
}
private bool MatchesParentId(TValToMatch propVal)
{
return (!repo.ParentId.HasValue || repo.ParentId == propVal.ParentId);
}
private static bool MatchesDefinitionId(CurrentSearchContext context, TValToMatch propVal)
{
return propVal.PropertyId == context.Definition.Id;
}
Я предпочитаю второй подход, потому что я вижу имена методов как комментарии, но я понимаю, что это проблематично, потому что вы должны прочитать все методы, чтобы понять, что делает код, поэтому он абстрагирует намерения кода.
c#
readability
Виллем
источник
источник
if
заявлений в любом блоке кода. Ваш вопрос о булевых выражениях .Ответы:
Последний подход. Это не только легче для понимания, но и для написания, тестирования, рефакторинга и расширения. Каждое необходимое условие может быть безопасно отделено и обработано по-своему.
Это не проблема, если методы названы правильно. На самом деле это будет легче понять, так как имя метода будет описывать цель условия.
Для зрителя
if MatchesDefinitionId()
более объяснительным, чемif (propVal.PropertyId == context.Definition.Id)
[Лично, первый подход ранит мои глаза.]
источник
MatchesDefinitionId()
является пограничным.Если это единственное место, где будут использоваться эти предикатные функции, вы также можете использовать
bool
вместо них локальные переменные:Они также могут быть разбиты дальше и переупорядочены, чтобы сделать их более читабельными, например, с
а затем заменить все экземпляры
propVal.SecondaryFilter.HasValue
. Одна вещь, которая сразу бросается в глаза, это то, чтоhasNoSecondaryFilter
использует логическое И для отрицательныхHasValue
свойств, в то время какmatchesSecondaryFilter
использует логическое И для неотрицанныхHasValue
- так что это не полная противоположность.источник
В общем, последнее является предпочтительным.
Это делает сайт вызова более пригодным для повторного использования. Он поддерживает DRY (то есть у вас меньше мест для изменения при изменении критериев, и вы можете делать это более надежно). И очень часто эти подкритерии являются вещами, которые будут использоваться независимо друг от друга в другом месте, что позволяет вам сделать это.
О, и это делает этот материал намного проще для модульного тестирования, давая вам уверенность, что вы сделали это правильно.
источник
repo
, которое выглядит как статическое поле / свойство, то есть глобальная переменная. Статические методы должны быть детерминированными и не использовать глобальные переменные.Если это между этими двумя вариантами, то последний лучше. Однако это не единственный выбор! Как насчет разбить одну функцию на несколько ifs? Проверьте способы выхода из функции, чтобы избежать дополнительных тестов, грубо эмулируя «короткое замыкание» в тесте с одной линией.
Это легче читать (вам может потребоваться перепроверить логику для вашего примера, но концепция верна):
источник
Мне больше нравится вариант 2, но я бы предложил одно структурное изменение. Объедините две проверки в последней строке условия в один вызов.
Причина, по которой я предлагаю это, состоит в том, что две проверки представляют собой одну функциональную единицу, а вложенные круглые скобки в условных выражениях подвержены ошибкам: как с точки зрения первоначального написания кода, так и с точки зрения человека, читающего его. Это особенно верно, если подэлементы выражения не следуют одному и тому же шаблону.
Я не уверен, что
MatchesSecondaryFilterIfPresent()
это лучшее название для комбинации; но нет ничего лучше, сразу приходит на ум.источник
Хотя в C # код не очень объектно-ориентированный. Он использует статические методы и выглядит как статические поля (например
repo
). Обычно считается, что статика делает ваш код трудным для рефакторинга и тестирования, в то же время затрудняя его повторное использование, и, на ваш вопрос: такое статическое использование менее читабельно и легко поддерживается, чем объектно-ориентированная конструкция.Вы должны преобразовать этот код в более объектно-ориентированную форму. Когда вы это сделаете, вы обнаружите, что есть разумные места для размещения кода, который выполняет сравнение объектов, полей и т. Д. Вполне вероятно, что вы могли бы тогда попросить объекты сравнить себя, что уменьшило бы ваш большой оператор if до простой запрос для сравнения (например
if ( a.compareTo (b) ) { }
, который может включать все сравнения полей)C # имеет богатый набор интерфейсов и системных утилит для сравнения объектов и их полей. Помимо очевидного
.Equals
способа, для начала, посмотрите наIEqualityComparer
,IEquatable
и коммунальные услуги , какSystem.Collections.Generic.EqualityComparer.Default
.источник
Последнее определенно предпочтительнее, я видел случаи с первым способом, и его почти всегда невозможно прочитать. Я сделал ошибку, сделав это первым способом, и меня попросили изменить его, чтобы использовать методы предикатов.
источник
Я бы сказал, что они примерно одинаковы, ЕСЛИ вы добавите несколько пробелов для удобства чтения и некоторые комментарии, чтобы помочь читателю в более неясных частях.
Помните: хороший комментарий говорит читателю, о чем вы думали, когда писали код.
С такими изменениями, которые я предложил, я бы, вероятно, пошел с прежним подходом, так как он менее загроможден и рассеян. Вызовы подпрограмм похожи на сноски: они предоставляют полезную информацию, но нарушают процесс чтения. Если бы предикаты были более сложными, то я бы разбил их на отдельные методы, чтобы концепции, которые они воплощают, можно было собрать в понятные куски.
источник
Что ж, если есть части, которые вы, возможно, захотите использовать повторно, разделение их на отдельные функции с правильными именами, очевидно, является лучшей идеей.
Даже если вы никогда не будете использовать их повторно, это позволит вам лучше структурировать свои условия и дать им ярлык с описанием их значения .
Теперь давайте посмотрим на ваш первый вариант и признаем, что ни ваши отступы и переносы строк не были столь полезны, ни условно структурированная структура:
источник
Первый абсолютно ужасен. Вы использовали || для двух вещей на одной линии; это либо ошибка в вашем коде, либо намерение запутать ваш код.
Это, по крайней мере, наполовину прилично отформатированный (если форматирование сложное, это потому, что условие if сложное), и у вас есть, по крайней мере, шанс выяснить, есть ли здесь что-то бессмысленное. По сравнению с вашим отформатированным мусором, если что-нибудь еще лучше. Но вы, похоже, способны делать только крайности: либо полный беспорядок оператора if, либо четыре совершенно бессмысленных метода.
Обратите внимание, что (cond1 && cond2) || (! cond1 && cond3) можно записать как
что уменьшило бы беспорядок. Я бы написал
источник
Мне не нравится ни одно из этих решений, их сложно рассуждать и трудно читать. Абстракция к меньшим методам только ради меньших методов не всегда решает проблему.
В идеале, я думаю, что вы должны метапрограммически сравнивать свойства, чтобы у вас не было определения нового метода или ветвления каждый раз, когда вы хотите сравнить новый набор свойств.
Я не уверен насчет c #, но в javascript нечто подобное было бы НАМНОГО лучше и могло бы по крайней мере заменить MatchesDefinitionId и MatchesParentId
источник
compareContextProp(propVal, "PropertyId", context.Definition.Id)
было бы легче прочитать, чем булеву комбинацию из ~ 5 сравнений формыpropVal.PropertyId == context.Definition.Id
. Он значительно длиннее и добавляет дополнительный слой, не скрывая сложности с сайтом вызовов. (если это имеет значение, я не понизил)