#c #if-statement #refactoring #theory
#c #if-оператор #рефакторинг #теория
Вопрос:
Я пытаюсь сделать это как можно более общим. Давайте предположим, что в if-операторе я проверяю, является ли некоторое логическое выражение A истинным. Допустим, существуют конкретные случаи, когда A является истинным, A.a и A.b, которые являются взаимоисключающими. То же самое верно для другого логического выражения B. Затем рассмотрим следующий код:
if (A) {
if (A.a) {
somethingSpecificToAa();
foo();
}
} else if (B) {
if (B.a) {
somethingSpecificToBa();
foo();
}
} else {
foo();
}
В моем фактическом коде foo()
не одна функция, а несколько длинных строк кода. Повторение их так много раз кажется мне неприятным, поэтому я предполагаю, что некоторый рефакторинг необходим.
Поскольку foo()
выполняется, когда:
- A.a является истинным
- B.a верно
- Ни A, ни B не являются истинными
Я подумал о следующем:
if (A.a) {
somethingSpecificToAa();
} else if (B.a) {
somethingSpecificToBa();
}
if (A.a || B.a || !(A || B)) {
foo();
}
который должен иметь такое же поведение. Это лучший способ сделать это? Обратите внимание, что условие во 2-м операторе if 2-го примера в действительности оказывается чрезвычайно длинным, поэтому мой код по-прежнему выглядит как 1-й пример (я ненавижу разбивать один if
на несколько строк.) Я также думал о создании лямбда-выражения, которое возвращает a bool
, эквивалентное A.a || B.a || !(A || B)
и вместо этого подключает лямбда-выражение ко 2-му оператору if. В качестве альтернативы я мог бы сохранить структуру 1-го примера, но заменить множество строк каждого foo()
из них на ( void
) лямбда-выражение, которое делает то же самое, хотя я не уверен, что это устраняет запах.
Я переусердствовал на данный момент, думая о лямбдах? Какой подход лучше всего подходит для поддержания чистого кода?
РЕДАКТИРОВАТЬ: кажется, я сделал его слишком общим. Я имею дело с контейнерами STL, а не с моими собственными классами, и более «точным» примером будет:
int shirtACleanliness = calculateCleanliness(shirtA);
if (itemsToWash.contains(shirtA)) { //itemsToWash is a std::set
if (shirtA.cleanliness > shirtACleanliness) {
itemsToWash.erase(shirtA);
shirtA.cleanliness = shirtACleanliness;
itemsToWash.insert(shirtA); //the set is ordered on cleanliness, so this re-inserts in the correct position
doSomeOtherStuff(shirtA);
}
} else if (itemsToDry.contains(shirtA)) { //itemsToDry is a std::vector
if (shirtA.cleanliness > shirtACleanliness) {
itemsToDry.erase(shirtA);
shirtA.cleanliness = shirtACleanliness;
itemsToWash.insert(shirtA);
doSomeOtherStuff(shirtA);
}
} else {
shirtA.cleanliness = shirtACleanliness;
itemsToWash.insert(shirtA);
doSomeOtherStuff(shirtA);
}
//am aware aware contains() is not a method for either type
//and std::vector does not have erase() by value, this is just conceptual
Комментарии:
1. Если, например
A
, иB
являются указателями (что означает, что у вас действительно естьA->a
иB->a
), то вы всегда должныA
B
сначала проверять и в любом случае.2. Помните, что если по какой-либо причине вам нужно использовать немного другой
foo()
в ветке, то вам придется создать дополнительныйfoo2()
. Вместо изменения форматированияif
я бы разбилfoo()
его на более мелкие, управляемые функции / классы, чтобы каждаяif
подотрасль казалась достаточно маленькой.3. @Someprogrammerdude Это не указатели, а просто абстрактное представление того, что я пытаюсь сделать. Но да, даже в моем реальном коде я должен проверять
A amp;amp; A.a
andB amp;amp; B.a
, а не простоA.a
andB.a
.4. @Tetix
foo()
согласно моему приведенному выше коду делает то же самое независимо от того, когда он вызывается; Я поместил все данные, относящиеся к конкретной ветке, вsomethingSpecificToAa()
иsomethingSpecificToBa()
. Если я не неправильно понял вашу точку зрения?5. @ampharos Я просто думал о будущем, на всякий случай
foo()
, если один из сценариев будет отличаться таким образом, которыйsomethingspecificA/B
невозможно включить. Затем требуется новыйfoo2()
и т. Д. Вот почему я предложил разбитьfoo()
на более мелкие функции / классы, если это возможно.
Ответ №1:
Согласно вашему недавнему комментарию, вот фрагмент псевдокода, который может соответствовать вашему варианту использования.
Element* element=nullptr;
if(vectorA.contains(X)){
element = vectorA.get(X);
}else if(setB.contains(X)){
element = setB.get(X);
}
if(element != nullptr amp;amp; element->a){
something(element);
}
if(element == nullptr || element->a){
foo();
}
Комментарии:
1. На данный момент это самый чистый и простой для понимания подход. К сожалению, если я не ошибаюсь, нет никакого различия между тем, было ли
element
получено из набора или вектора, мы просто знаем, найдено ли оно в любом из них. Итак, мне не хватает исключительного поведения, которое я различаю с помощьюsomethingExclusiveToAa()
andsomethingExclusiveToBa()
. Будет ли плохо использовать abool
во 2-м блоке (который мы назначаем в 1-м блоке) для выбора между something(element) и somethingElse (element)?2. Я думал
somethingExclusiveToAa()
, иsomethingExclusiveToBa()
мы просто получали элемент, но если это не так, вы можете удалитьif(element != nullptr amp;amp; element->a){ something(element);}
и поместить свои данные послеget(X);
. Похоже, что ваши конкретные случаи затрудняют выражение чего-то более общего. На данный момент я бы сказал, что мы недостаточно знаем, чтобы двигаться вперед.3. Теперь я отредактировал вопрос, добавив немного более конкретный пример, чтобы, когда я приму ваш ответ, его актуальность была очевидна без просмотра комментариев. Я также внес правку в ваш ответ в соответствии с вашим предложением. Спасибо!
Ответ №2:
Поскольку там есть какой-то общий код, почему бы не разбить его на функцию? У вас может быть что-то вроде
template<typename T, typename F>
bool doSomething(T constamp; t, F f)
{
if (t amp;amp; t.a)
{
f();
foo();
}
return static_cast<bool>(t);
}
И используйте его как
if (!doSomething(A, amp;somethingSpecificToAa) amp;amp; !doSomething(B, amp;somethingSpecificToBa))
{
foo();
}
Комментарии:
1. Мне это действительно нравится. Может быть, я просто медлю, но как я могу в этом случае определить исключительное поведение для вызова функтора
f()
? Должно быть по-другому, смотрим ли мы на A или B.2. Просто определите общедоступную функцию f() в A и B и замените f(); на t.f() . Это означает, что вам не нужен второй параметр в функции шаблона, предоставленной @Some programmer dude .