Как я могу уменьшить цикломатическую сложность этого?

У меня есть метод, который получает объект и что-то делает в зависимости от того, какой тип объекта он обнаруживает:

void receive(Object object) {
    if (object instanceof ObjectTypeA) {
        doSomethingA();
    }
    else {
        if (object instanceof ObjectTypeB) {
            doSomethingB();
        }
        else {
            if (object instanceof ObjectTypeC) {
                doSomethingC();
            }
            else {
                if (object instanceof ObjectTypeD) {
                    doSomethingD();
                }
                else {
                    // etc...
                }
            }
        }
    }
}

Как я могу уменьшить цикломатическую сложность? Я поискал, но не нашел ничего слишком полезного.


person Nathan    schedule 02.05.2011    source источник
comment
Почему бы не перегрузить метод? Эти ссылки на instanceof всегда заставляют меня содрогаться.   -  person Andrew Thompson    schedule 02.05.2011
comment
@ Андрей - Это должен был быть ответ. Это было бы правильно.   -  person Justin Morgan    schedule 04.05.2011
comment
@Justin: Это было бы правильно. Я сомневаюсь, что ОП так подумал, хотя меня не впечатлил их выбор «правильного ответа». ;)   -  person Andrew Thompson    schedule 04.05.2011


Ответы (4)


Разве вы не можете использовать для этого объектно-ориентированный подход? Создать интерфейс с методом doSomething(), а затем создать подклассы, реализующие желаемое поведение? Тогда вызов object.doSomething() приведет к соответствующему поведению?

person laz    schedule 02.05.2011
comment
Это сработает, но если это только для уменьшения цикломатической сложности, это плохо. Из примера кажется, что это обратный вызов слушателя. Обычно объект и не содержит одного метода в зависимости от возможного использования конкретного слушателя. - person Nicolas Bousquet; 02.05.2011
comment
Я не подходил к этому с точки зрения цикломатической сложности, поэтому в этом смысле я не ответил на вопрос. Однако, когда я вижу много условных выражений, использующих instanceof, я думаю, что следует серьезно подумать о полиморфизме, хотя бы ради создания более удобного в сопровождении кода. Вот для чего в любом случае должны помочь измерения цикломатической сложности. - person laz; 02.05.2011
comment
Хорошо подумайте о любой внешней библиотеке, например. У вас есть случай, зависящий от типа любой иерархии классов, которую вы используете (например, тип виджета в SWING). Было бы очень плохо модифицировать для этого внешнюю библиотеку. И внешняя библиотека не может заранее знать все, что кто-либо когда-либо будет с ней делать. Таким образом, он определяет общий прослушиватель, и вы можете проверить тип события или источника. Даже если это ваш собственный код, разделение принципа беспокойства позволит вам избежать добавления слишком большого количества несвязанных методов к объектам только для того, чтобы удалить безобидный оператор switch. - person Nicolas Bousquet; 02.05.2011
comment
снижение цикломатической сложности уменьшает количество ошибок. научный факт: en.wikipedia.org/wiki/ - person davidjnelson; 01.04.2012
comment
@devadvocate: Так ли это? - Прочтите второй абзац: Однако исследования, которые контролируют размер программы (...), как правило, менее убедительны, и многие не обнаруживают значительной корреляции ... - person mattnz; 15.06.2012
comment
@davidjnelson из вашей ссылки Суть этого наблюдения заключается в том, что в более крупных программах обычно больше дефектов. Это означает, что Cyclomatic Complexity вполне может иметь только такую ​​же предсказательную способность, как и строки кода. - person Seph; 24.09.2013

Цикломатическая сложность - это мера, основанная на графической структуре кода. В частности, он основан на количестве возможных путей прохождения кода; см. здесь для получения дополнительных сведений. Хотя существует корреляция между CC и тем, что типичный программист считает сложностью кода, это не одно и то же. Например:

  • CC не принимает во внимание семантику кода; например что делает вызываемый такой-то метод или математические свойства алгоритма.

  • CC не принимает во внимание шаблоны дизайна и кодирования. Итак, то, что CC считает сложным, вполне может быть простым для того, кто разбирается в используемом шаблоне.

Можно сказать, что взаимосвязь между CC и реальной сложностью кода подобна взаимосвязи между IQ и реальным интеллектом.

Таким образом, Cyclomatic Complexity следует рассматривать как индикатор того, где находятся сложные части вашего кода ... не как истинную меру сложности или качества кода. В самом деле, очень сложный код не обязательно плохого качества. Часто сложность присуща, и попытки избавиться от нее только усугубляют ситуацию.


В этом конкретном примере высокий показатель CC не соответствует тому, что могло бы вызвать у типичного программиста какие-либо трудности. Лучший ответ (ИМО) - оставить метод в покое. Считайте это ложным срабатыванием.

person Stephen C    schedule 02.05.2011
comment
Я согласен с вашим общим определением CC. Однако я частично не согласен с вашим конкретным ответом на этот вопрос. Код действительно легко читается средним программистом. Однако использование интерфейса, заявленного laz, очистит код. Вы не сможете увидеть, что происходит, с одного взгляда. Но это типично для рефакторинга больших методов в более мелкие. - person Torsten; 22.07.2013
comment
@Torsten - Ты неправильно понял, о чем я говорю. Я не говорю, что здесь нельзя уменьшить CC. Этот пример не должен вызывать у программиста со средними способностями никаких проблем, что бы ни говорил CC. А поскольку настоящая цель - сделать код читабельным, никаких изменений не требуется. Очищать код стоит только в том случае, если есть проблемы с читабельностью. - person Stephen C; 30.09.2013

У меня никогда не было цели «уменьшить цикломатическую сложность», хотя были времена, когда мне платили LOC.

Ваш код «достаточно хорош». Я натыкаюсь на скобки, поэтому я немного пожертвовал производительностью и сделал следующее (при условии, что типы A, B и т. Д. Не находятся в одной иерархии):

receive(Object object) {
    if (object intanceof ObjectTypeA) doSomethingA();
    if (object instanceof ObjectTypeB) doSomethingB();
    ...

или (если они находятся в одной иерархии):

receive(Object object) {
    if (object intanceof ObjectTypeA) { doSomethingA(); return; }
    if (object instanceof ObjectTypeB) { doSomethingB(); return; }
    ...

Не знаю, уменьшит ли это цикломатику, да и наплевать.

person Vladimir Dyuzhev    schedule 02.05.2011
comment
снижение цикломатической сложности уменьшает количество ошибок. научный факт: en.wikipedia.org/wiki/ Кроме того, если вы хотите увидеть Посмотрите, как качество кода влияет на прибыль бизнеса: ibm.com /developerworks/rational/library/4995.html - person davidjnelson; 01.04.2012
comment
Наука по вашей ссылке, как они сами говорят, безрезультатна. - person Vladimir Dyuzhev; 04.04.2012
comment
@davidjnelson - Вот что сейчас говорит цитируемый вами авторитет (Википедия): Таким образом, НЕ доказано, что уменьшение цикломатической сложности кода снижает количество ошибок или ошибок в этом коде. - person Stephen C; 07.06.2018

Зачем нужно уменьшать сложность? Это достаточно простой шаблон, чтобы любой компетентный разработчик увидел в нем тривиальную функцию.

Я бы, наверное, так написал

    if (object instanceof ObjectTypeA) 
    {
        doSomethingA();
    }
    else if (object instanceof ObjectTypeB) 
    {
        doSomethingB();
    }
    else if (object instanceof ObjectTypeC) 
    {
        doSomethingC();
    }

Если это необходимо для удовлетворения некоторой эзотерической потребности «CC должен быть меньше x», то всеобъемлющее правило, согласно которому стандарты существуют для обеспечения поддерживаемого кода, будет означать, что это приемлемо независимо от того, насколько высока CC.

person mattnz    schedule 02.05.2011
comment
Если код написан таким образом, будет сложно написать кейсы модульных тестов. Еще один тип данных, который вы добавите, приведет к добавлению еще одного шага в лестничной диаграмме и еще одного теста. OO позволяет абстракции позаботиться об этом, если вы хотите использовать ваш подход, лучше писать на C вместо Java. - person Vinod R; 02.05.2011
comment
Vinod R, ООП добавил новый выбор, используя полиморфизм. Это не значит, что это всегда лучшее решение. Когда возможные методы фиксированы (и малы), а подтипы могут сильно различаться, использование ООП и полиморфизма является хорошим решением. Но если, наоборот, ваш набор подтипов ограничен, но возможные действия, которые вы хотите с ними делать, неограниченны, лучше использовать вариант переключения. На этом языке лучше всего подходят с сопоставлением с образцом, а не специально ООП. Мы могли бы извлечь выгоду из перечисления через. Что касается модульных тестов, независимо от решения, количество проверяемых случаев остается неизменным. - person Nicolas Bousquet; 02.05.2011
comment
Vinof R - Согласен. В некоторых случаях использование чистого OO-раствора предпочтительнее, но невозможно. Вопрос не содержал достаточно информации, чтобы указать, что можно было бы использовать чистое объектно-ориентированное решение. Закрытие переключателя / case с помощью else / default позволяет обнаруживать нереализованные варианты. - person mattnz; 03.05.2011
comment
это могло быть приемлемо в прототипе, где никому другому никогда не приходилось читать код, и ошибки были допустимы. в противном случае используйте полиморфизм. - person davidjnelson; 01.04.2012