Код с запашком
Код с запашком — это распространённые дефекты дизайна программного обеспечения, обозначающие плохие практики проектирования, которые зачастую приводят к ошибкам и проблемам сопровождения[1][2]. Такие дефекты часто возникают вследствие неудачных решений при реализации или проектировании и ведут к усложнению исходного кода, его поддержки и доработки. В отличие от антипаттернов, «код с запашком» не обязательно является ошибкой — такие конструкции могут продолжать существовать в программе длительное время без непосредственного влияния на её функциональность. Для устранения «запаха кода» используют рефакторинг — изменение кода программы без изменения её внешнего поведения. Показателями наличия кода с запашком служат такие качества, как читаемость кода, простота его модификации, способность к развитию с изменением требований, повторное использование в разных контекстах, тестируемость и надёжность[3].
Примеры кода с запашком
Подобно шаблонам проектирования, в научной литературе описано множество типовых примеров кода с запашком. Один из самых известных списков был составлен Мартином Фаулером с рекомендациями по подходящему рефакторингу[4].
Дублирование кода (Duplicated Code)
Антипаттерн «Дублирование кода»[4] — классический пример кода с запашком, выражающийся в наличии одинаковых фрагментов кода в разных местах приложения. Дублирование бывает и внутри одного класса (разных методов), и между разными классами.
Зависть к данным (Feature Envy)
Антипаттерн «Зависть к данным»[4] — ситуация, когда метод активно обращается к методам и данным других классов, чаще всего — к геттерам, чтобы получить нужные данные. Это может указывать на то, что метод находится не в том классе.
Глобальный объект (BLOB)
Также известен под названиями «божественный класс» или «Winnebago»; антипаттерн «Blob» — это класс, чрезмерно концентрирующий ответственность, обладающий множеством полей и зависящий от большого числа других классов с большими наборами данных.
Длинный список параметров (Long Parameter List)
Антипаттерн «Длинный список параметров»[4] — пережиток ранней эры процедурного программирования, когда функциям передавалось множество параметров вместо использования глобальных переменных. В современных ООП-языках такой подход ухудшает читаемость и затрудняет рефакторинг.
Длинный метод (Long Method)
Реализация чрезмерно длинных методов — типичная ошибка начинающих разработчиков. Такие методы излишне сложны, хотя их можно и нужно декомпозировать[4] — разбиение упрощает чтение и последующую поддержку.
Крупный класс (Large Class)
Антипаттерн «Large Class»[4] подразумевает класс с большим количеством переменных. Это часто приводит к обособленному использованию разных переменных и порождает дублирование кода.
Выявление и устранение кода с запашком
Методы обнаружения
Для каждого типа кода с запашком существуют более или менее формализованные способы обнаружения, а также практики для устранения или предотвращения ошибки. Ниже представлены методы выявления и рекомендации по исправлению для шести наиболее распространённых случаев.
Дублирование кода (Duplicated Code)
Обнаружение кода типа «Duplicated Code» заключается в оценке процента дублированных строк в программе. Простое полное совпадение выявляется легко, но дублирование часто завуалировано переименованиями переменных или небольшими изменениями.
Например, в языке Java:
Integer var = 5;
String str = String.valueOf(var);
является дублированием кода по отношению к:
Integer nombre = 5;
String chaine = String.valueOf(nombre);
Чтобы не упустить такие случаи, алгоритм должен учитывать возможные переименования и незначительные отличия. Также встречается случай, когда два разных алгоритма решают одну и ту же задачу; такую ошибку часто допускают новички при копировании кода. Это не только увеличивает объём программы, но и усложняет сопровождение. Исправление — вынесение общего кода в отдельный метод и использование его вместо дубликатов. Для предотвращения подобных ошибок важно:
- Факторизовать код на максимально ранних этапах.
- Работать по принципам разработки через тестирование.
Зависть к данным (Feature Envy)
Запах кода «Feature Envy» (буквально «зависть к функционалу») проявляется, когда метод обращается к элементам других классов чаще, чем к своим собственным. Исправить это можно несколькими способами:
- Переместить метод в другой класс, если он использует больше данных из него, чем из текущего.
Например, в классе Rectangle метод perimetre() может быть логично перемещён в другой класс:
public class Rectangle {
...
public Double perimetre() {
return 2 * AutreClasse.largeur + 2 * AutreClasse.longueur;
}
}
- Вынести часть метода, если она активнее использует данные другого класса. Например, если метод в классе лодка
preparer_bateau()преимущественно манипулирует заглушкой SPI:
public class Bateau {
private SPI spi;
private Reserve reserve;
...
public void preparer_bateau() {
this.laver();
this.verifier_coque();
reserve.verifier_provisions();
spi.regler_hauteur();
spi.regler_largeur();
spi.verifier_voile();
}
}
Корректная практика — внедрить новый метод в SPI, который агрегирует нужные действия, и вызывать его из preparer_bateau().
Для обнаружения таких случаев оценивается так называемая степень связанности методов с внешними классами. Lanza и Marinescu предложили специальную метрику[5]:
Где:
- LAA (Locality of Attribute Accesses) — доля методов, использующих больше атрибутов сторонних классов, чем собственных;
- FDP (Foreign Data Providers) — доля атрибутов, предоставляемых другими классами и редко используемых ещё где-либо;
- ATFD (Access to Foreign Data) — доля методов, которые получают доступ к чужим данным.
Реализации этой методики есть в инструментах iPlasma, inFusion, JDeodorant и др[6]. Плагин Eclipse Metrics[7] также помогает обнаруживать такие случаи.
Глобальный объект (BLOB)
Эта ошибка связана с нежеланием или неумением проектировать сложные архитектуры. Она приводит к снижению скорости и ухудшению сопровождаемости. Для профилактики BLOB-объектов рекомендуется:
- Использовать принцип «разделяй и властвуй»;
- Активно применять абстракцию и наследование.
Исправление предполагает реорганизацию кода и разделение обязанностей:
- Переместить методы, не относящиеся по смыслу к данному классу, в корректные классы;
- Убрать нелогичные поля и размещать их в соответствующих классах;
- Устранить ненужные связи между классами.
Обнаружение производится с помощью метрик[8]:
DECOR определяет BLOB через величину LCOM5 (слабая связность методов, Lack of Cohesion Of Methods) — если она превышает 20, а число методов и полей в классе больше 20, а также по подозрительным названиям классов (Process, Control, Manage, System и др.)[9][10].
Для дальнейшего анализа применяется кластеризация (разделение на кластеры по метрике Жаккара), например как реализовано в JDeodorant и других инструментах[11].
Длинный список параметров (Long Parameter List)
Антипаттерн возникает в случаях, когда метод/класс перегружен параметрами, обычно вследствие объединения нескольких алгоритмов или чрезмерного усложнения интерфейсов. Корректировка заключается в:
- Замене группы параметров на передачу объекта;
- Сокращении числа параметров (
Mesures mesuresвместо трёх отдельных аргументов).
Инструменты анализа, такие как PMD и Checkstyle, обычно устанавливают порог на максимально допустимое количество параметров (10 и 7 соответственно).
Длинный метод (Long Method)
Запах «Long Method» (слишком длинный метод) встречается при отсутствии декомпозиции. Его профилактика — TDD, разбиение на небольшие функции, повышение читаемости. Для обнаружения используют не только число строк, но и такие показатели, как цикломатическая сложность и метрики Хэлстеда[12]. Многие инструменты считают длинной функцию в 100–150 строк и более.
Крупный класс (Large Class)
Запах «Large Class» — класс, сочетающий в себе слишком много обязанностей. NASA Software Assurance Technology Center публиковал рекомендуемые пороги:
- меньше 20 методов в классе;
- суммарная «масса» методов (длина) — не более 100;
- суммарное число методов и вызываемых ими функций — не более 100;
- число классов, на которые ссылается текущий, — не более 5.
PMD и Checkstyle считают класс крупным при превышении 1000–2000 строк. Исправляется проблема выделением отдельных классов для локальных обязанностей.
Автоматические средства
Существует ряд инструментов, реализующих автоматическое выявление кода с запашком.
DECOR
DECOR[9] (Detection&Correction) — комплекс, выполняющий выявление и автоматическую корректировку дефектов. Процесс построен как система правил на специальном языке высокого уровня, задаваемых пользователем для поиска и (опционально) исправления. DECOR фиксирует 19 различных паттернов, включая Blob, Long Method и Large Class[13].
inFusion
Коммерческое решение, базирующееся на платформе iPlasma; работает с Java, C, C++. Определяет более 20 дефектов, среди которых Duplicated Code, Blob, Long Method и Large Class.
JDeodorant
Плагин для Eclipse, созданный в Университете Конкордии (Канада) и Университете Македонии (Греция)[14]. Позволяет находить и устранять основные дефекты типа Feature Envy, Type-Checking, Long Method, Blob, Duplicated Code[15].
Stench Blossom
Stench Blossom[16] — инструмент, предлагающий визуальное представление анализа, где каждый вид дефекта отмечен в виде «лепестков». Чем больше проблема, тем больше лепесток.
Плагин для Eclipse, распознающий 8 разновидностей дефектов в Java-коде.
HIST
HIST (Historical Information for Smell deTection)[17] — инструмент, ориентированный на анализ истории изменений в системах контроля версий. Он выявляет закономерности частых изменений проблемных классов (например, BLOB) при рефакторинге. HIST обнаруживает Divergent Change, Shotgun Surgery, Parallel Inheritance, Blob, Feature Envy. По сравнению с другими средствами, обеспечивает более высокую точность, но требует большего количества исходных данных и работы с историей изменений[18].
Прочие
Существуют и другие инструменты, такие как Checkstyle, PMD.
Примечания
- ↑ bliki: CodeSmell (англ.). martinfowler.com. Дата обращения: 16 мая 2020. Архивировано 8 августа 2022 года.
- ↑ Détection et analyse de l’impact des défauts de code dans les applications mobiles (фр.). Université Lille-I (диссертация). Université Lille 1 : Sciences et Technologies ; Université du Québec à Montréal (30 ноября 2016). Дата обращения: 16 мая 2020. Архивировано 21 января 2021 года.
- ↑ Girish Suryanarayana, Ganesh Samarthyam, Tushar Sharma (2015). “Technical Debt”. Refactoring for Software Design Smells [англ.]. Elsevier: 1—7. Дата обращения 2023-12-01.
- ↑ 1 2 3 4 5 6 Martin Fowler. Refactoring: Improving the Design of Existing Code : [англ.]. — Addison-Wesley, 1999. — P. 63–73.
- ↑ Michele Lanza, Radu Marinescu. Object-oriented metrics in practice: using software metrics to characterize, evaluate, and improve the design of object-oriented systems : [англ.]. — Springer Science & Business Media, 2007. — P. 85–86.
- ↑ Nikolaos Tsantalis, Alexander Chatzigeorgiou (2009). “Identification of Move Method Refactoring Opportunities”. IEEE Transactions on Software Engineering [англ.]. 35: 347—367. DOI:10.1109/TSE.2009.1. Дата обращения 2023-09-01.
|access-date=требует|url=(справка) - ↑ Feature Envy (англ.). sourceforge.net. Дата обращения: 22 мая 2023. Архивировано 30 марта 2023 года.
- ↑ Michele Lanza, Radu Marinescu. Object-oriented metrics in practice: using software metrics to characterize, evaluate, and improve the design of object-oriented systems : [англ.]. — Springer Science & Business Media, 2007. — P. 80–81.
- ↑ 1 2 Naouel Moha, Yann-Gaël Guéhéneuc, Laurence Duchien, Anne-Françoise Le Meur (2010). “DECOR: A Method for the Specification and Detection of Code and Design Smells”. IEEE Transactions on Software Engineering [англ.]. 36 (1): 20—36. DOI:10.1109/TSE.2009.50.
- ↑ Fabio Palomba, Gabriele Bavota, Rocco Oliveto, Andrea De Luzia (2014). “Anti-Pattern Detection: Methods, Challenges and Open Issues”. Advances in Computers, Chapter 4 [англ.]. 95: 3—5.
- ↑ Marios Fokaefs, Nikolaos Tsantalis, Alexander Chatzigeorgiou, Jörg Sander (2009). “Decomposing object-oriented class modules using an agglomerative clustering technique”. IEEE International Conference on Software Maintenance [англ.]: 93—101. DOI:10.1109/ICSM.2009.5306332.
- ↑ M.V. Mäntylä (2005). “An experiment on subjective evolvability evaluation of object-oriented software: explaining factors and interrater agreement”. IEEE International Symposium on Empirical Software Engineering [англ.]. DOI:10.1109/ISESE.2005.1541837.
- ↑ Naouel Moha, Yann-Gaël Guéhéneuc, Laurence Duchien, Anne-Françoise Le Meur (2010). “DECOR: A Method for the Specification and Detection of Code and Design Smells”. IEEE Transactions on Software Engineering [англ.]. 36 (1): 15. DOI:10.1109/TSE.2009.50.
- ↑ JDeodorant. Eclipse Plugins, Bundles and Products - Eclipse Marketplace. Дата обращения: 6 декабря 2015. Архивировано 16 марта 2022 года.
- ↑ Nikolaos Tsantalis, Théodoros Chaikalis, Alexander Chatzigeorgiou (2009). “JDeodorant: Identification and Removal of Type-Checking Bad Smells”. IEEE European Conference on Software Maintenance and Reengineering [англ.] (1534–5351): 329—331. DOI:10.1109/CSMR.2008.4493342.
- ↑ Emerson Murphy-Hill, Andrew P. Black (2010). “Decomposing object-oriented class modules using an agglomerative clustering technique”. IEEE International Symposium on Software Visualization [англ.]: 5—14. DOI:10.1145/1879211.1879216.
- ↑ Fabio Palomba, Rocco Oliveto, Gabriele Bavota, Andrea De Lucia, Massimiliano Di Penta, Denys Poshyvanyk (2013). “Detecting Bad Smells in Source Code using Change History Information”. IEEE International Conference on Automated Software Engineering [англ.]: 268—278. DOI:10.1109/ASE.2013.6693086.
- ↑ Fabio Palomba, Rocco Oliveto, Gabriele Bavota, Andrea De Lucia, Massimiliano Di Penta, Denys Poshyvanyk (2013). “Detecting Bad Smells in Source Code using Change History Information”. IEEE International Conference on Automated Software Engineering [англ.]: 274—275. DOI:10.1109/ASE.2013.6693086.
Литература
- Fowler, Martin. Refactoring: Improving the Design of Existing Code. Addison-Wesley, 1999.
- Lanza, Michele; Marinescu, Radu. Object-oriented metrics in practice: using software metrics to characterize, evaluate, and improve the design of object-oriented systems. Springer Science & Business Media, 2007.
- Palomba, Fabio; Bavota, Gabriele; Oliveto, Rocco; De Luzia, Andrea. Anti-Pattern Detection: Methods, Challenges and Open Issues, Advances in Computers, Chapter 4, 2014, т. 95.
- Moha, Naouel; Guéhéneuc, Yann-Gaël; Duchien, Laurence; Le Meur, Anne-Françoise. DECOR: A Method for the Specification and Detection of Code and Design Smells. IEEE Transactions on Software Engineering, 2010, т. 36, № 1, с. 20–36, doi: 10.1109/TSE.2009.50.
- Fokaefs, Marios; Tsantalis, Nikolaos; Chatzigeorgiou, Alexander; Sander, Jörg. Decomposing object-oriented class modules using an agglomerative clustering technique. IEEE International Conference on Software Maintenance, 2009, с. 93–101, doi: 10.1109/ICSM.2009.5306332.
- Marinescu, Cristina; Marinescu, Radu; Mihancea, Petru Florin; Ratiu, Daniel; Wettel, Richard. iplasma: An integrated platform for quality assessment of object-oriented design. International Conference on Software Maintenance, 2005, с. 77–80.
- Tsantalis, Nikolaos; Chaikalis, Théodoros; Chatzigeorgiou, Alexander. JDeodorant: Identification and Removal of Type-Checking Bad Smells. IEEE European Conference on Software Maintenance and Reengineering, 2009, с. 329–331, doi: 10.1109/CSMR.2008.4493342.
- Murphy-Hill, Emerson; Black, Andrew P. Decomposing object-oriented class modules using an agglomerative clustering technique. IEEE International Symposium on Software Visualization, 2010, с. 5–14, doi: 10.1145/1879211.1879216.
- Palomba, Fabio; Oliveto, Rocco; Bavota, Gabriele; De Lucia, Andrea; Di Penta, Massimiliano; Poshyvanyk, Denys. Detecting Bad Smells in Source Code using Change History Information. IEEE International Conference on Automated Software Engineering, 2013, с. 268–278, doi: 10.1109/ASE.2013.6693086.
- Palomba, Fabio; Oliveto, Rocco; Bavota, Gabriele; De Lucia, Andrea; Di Penta, Massimiliano; Poshyvanyk, Denys. Mining Version Histories for Detecting Code Smells. IEEE Transactions on Software Engineering, 2015, т. 41, с. 462–489, doi: 10.1109/TSE.2014.2372760.
- Mann, Ankita; Dalal, Sandeep; Chhillar, Dhreej. An Effort to Improve Cohesion Metrics Using Inheritance. International Journal of Engineering and Advanced Technology, 2013, т. 2.
- Fontana, Francesca Arcelli; Braione, Pietro; Zanoni, Marco. Automatic detection of bad smells in code: An experimental assessment. Journal of Object Technology, 2012, т. 11.
- Walter, Bartosz; Martenka, Pawel. Looking for Patterns in Code Bad Smells Relations. IEEE International Conference on Software Testing, Verification and Validation Workshops, 2011, с. 465–466, doi: 10.1109/ICSTW.2011.89.
- Mäntylä, M.V. An experiment on subjective evolvability evaluation of object-oriented software: explaining factors and interrater agreement. IEEE International Symposium on Empirical Software Engineering, 2005, doi: 10.1109/ISESE.2005.1541837.