Отслеживание служебных классов - PullRequest
19 голосов
/ 11 апреля 2011

В последнее время я все больше и больше расстраиваюсь из-за проблемы, возникающей в кодовой базе моих проектов.

Я работаю над крупномасштабным Java-проектом, в котором> 1 млн строк кода.Интерфейсы и структура классов разработаны очень хорошо, и инженеры, пишущие код, очень опытны.Проблема состоит в том, что, пытаясь сделать код чище, люди пишут классы Utility всякий раз, когда им нужно повторно использовать некоторые функции, в результате со временем и по мере роста проекта появляется все больше и больше вспомогательных методов.Однако, когда следующий инженер сталкивается с необходимостью в той же функциональности, он не может знать, что кто-то уже реализовал служебный класс (или метод) где-то в коде и реализует другую копию функциональности в другом классе.Результатом является большое дублирование кода и слишком много служебных классов с перекрывающимися функциями.

Существуют ли какие-либо инструменты или принципы проектирования, которые мы, как команда, можем реализовать, чтобы предотвратить дублирование и плохую видимость утилитыклассы?

Пример: инженер А имеет 3 места, в которых ему нужно преобразовать XML в строку, поэтому он пишет вспомогательный класс с именем XMLUtil и размещает статический метод toString(Document)в этом.У инженера Б есть несколько мест, где он сериализует Документы в различные форматы, включая String, поэтому он пишет служебный класс с именем SerializationUtil и имеет статический метод с именем serialize(Document), который возвращает строку.

Примечаниечто это больше, чем просто дублирование кода, так как вполне возможно, что две реализации приведенного выше примера различны (скажем, одна использует API-интерфейс преобразователя, а другая использует Xerces2-J), поэтому это можно рассматривать как «лучшие практики»проблема также ...

Обновление: Я думаю, я лучше опишу текущую среду, в которой мы разрабатываем. Мы используем Hudson для CI, Clover для покрытия кода и Checkstyle для статического анализа кода.Мы используем гибкую разработку, включая ежедневные разговоры и (возможно, недостаточные) обзоры кода.Мы определяем все наши служебные классы в .util, который в силу своего размера теперь имеет 13 подпакетов и около 60 классов в корневом (.util) классе.Мы также используем сторонние библиотеки, такие как большинство jar-файлов apache commons и некоторые jars, из которых состоит Guava.

Я уверен, что мы можем уменьшить количество утилит наполовину, если мы добавим кого-то вЗадача рефакторинга всего пакета, мне было интересно, есть ли какие-либо инструменты, которые могут сделать эту операцию менее дорогостоящей, и есть ли какие-либо методологии, которые могут максимально задержать проблему от повторения.

Ответы [ 9 ]

9 голосов
/ 14 апреля 2013

Хорошее решение этой проблемы - начать добавлять объектную ориентацию.Чтобы использовать ваш пример:

Пример: у инженера А есть 3 места, в которых он должен преобразовать XML в String, поэтому он пишет вспомогательный класс с именем XMLUtil и помещает в него статический метод toString (Document)

Решение состоит в том, чтобы прекратить использование примитивных типов или типов, предоставляемых JVM (String, Integer, java.util.Date, java.w3c.Document), и обернуть их в свои собственные классы, специфичные для проекта.Тогда ваш класс XmlDocument может предоставить удобный метод toString и другие служебные методы.Ваш собственный ProjectFooDate может содержать методы синтаксического анализа и форматирования, которые в противном случае оказались бы в различных классах DateUtils и т. Д.

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

5 голосов
/ 18 апреля 2011

Ваша проблема очень распространена.И реальная проблема тоже, потому что нет хорошего решения.

Мы находимся в такой же ситуации, я бы сказал, что хуже, с 13 миллионами строк кода, оборотом и более 800 разработчиками, работающими надкод.Мы часто обсуждаем ту же проблему, которую вы описываете.

Первая идея, которую ваши разработчики уже использовали, - это рефакторинг общего кода в некоторых служебных классах.Наша проблема с этим решением, даже с парным программированием, наставничеством и обсуждением, состоит в том, что нас просто слишком много, чтобы это было эффективно.Фактически мы растем в подгруппах, где люди делятся знаниями в подгруппах, но знания не переходят между подгруппами.Возможно, мы ошибаемся, но я думаю, что даже парное программирование и разговоры не могут помочь в этом случае.

У нас также есть команда архитекторов.Эта команда отвечает за решение проблем проектирования и архитектуры, а также за создание общих утилит, которые могут нам понадобиться.Эта команда фактически производит то, что мы могли бы назвать корпоративной структурой.Да, это основа, и иногда она работает хорошо.Эта группа также несет ответственность за распространение передового опыта и повышение осведомленности о том, что следует делать или нет, что доступно или что нет.

Хорошая основная разработка Java API является одной из причин успеха Java.Хорошие сторонние библиотеки с открытым исходным кодом тоже очень важны.Даже небольшой хорошо разработанный API позволяет предложить действительно полезную абстракцию и может помочь значительно уменьшить размер кода.Но вы знаете, что создание фреймворка и публичного API - это совсем не то же самое, что просто кодирование служебного класса за 2 часа.Это действительно высокая стоимость.Служебный класс стоит 2 часа для начального кодирования, возможно, 2 дня с отладкой и юнит-тестами.Когда вы начинаете делиться общим кодом в больших проектах / командах, вы действительно создаете API.Вы должны обеспечить отличную документацию, действительно читаемый и поддерживаемый код.Когда вы выпускаете новую версию этого кода, вы должны поддерживать обратную совместимость.Вы должны продвигать его в масштабах компании (или, по крайней мере, в команде).От 2 дней для вашего небольшого служебного класса вы увеличиваете до 10 дней, 20 дней или даже 50 дней для полноценного API.

И ваш дизайн API может быть не таким уж хорошим.Что ж, дело не в том, что ваши инженеры не умны - на самом деле они есть.Но готовы ли вы позволить им поработать 50 дней над небольшим служебным классом, который просто помогает последовательно анализировать число для пользовательского интерфейса?Готовы ли вы позволить им полностью изменить дизайн, когда вы начнете использовать мобильный интерфейс с совершенно другими потребностями?Также вы заметили, как самые яркие инженеры в мире создают API, которые никогда не будут популярны или будут постепенно исчезать?Видите ли, первый веб-проект, который мы сделали, использовал только внутренние фреймворки или вообще не использовал фреймворк.Затем мы добавили PHP / JSP / ASP.Затем в Java мы добавили Struts.Теперь JSF является стандартом.И мы думаем об использовании Spring Web Flow, Vaadin или Lift ...

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

Но главная задача производительности компании-разработчика программного обеспечения - не набирать 10 или даже 50 строк кода при разборе XML.Общий код для этого в любом случае вырастет до тысячи строк кода и воссоздает сложный API, который будет распределен по служебным классам.Когда парень создает вспомогательный класс для разбора XML, это хорошая абстракция.Он дает имя одной дюжине или даже ста строкам специализированного кода.Этот код полезен, потому что он специализирован.Общий API позволяет работать с потоками, URL, строками, чем угодно.У него есть фабрика, поэтому вы можете выбрать реализацию парсера.Полезный класс хорош тем, что работает только с этим парсером и со строками.И потому, что вам нужна одна строка кода для его вызова.Но, конечно, этот служебный код имеет ограниченное использование.Это хорошо работает для этого мобильного приложения или для загрузки конфигурации XML.И именно поэтому разработчик в первую очередь добавил для него служебный класс.

В заключение, вместо того, чтобы пытаться консолидировать код для всей базы кода, я хотел бы разделить ответственность за код по мере роста команд:

  • превратить вашу большую команду, работающую над одним большим проектом, в маленькие команды, работающие над несколькими подпроектами;
  • гарантирует, что взаимодействие хорошо для минимизации проблем интеграции, но пусть команда имеет свой собственный код;
  • внутри этих команд и соответствующих баз кода, убедитесь, что у вас есть лучшие практики.Нет дубликата кода, хорошие абстракции.Используйте существующие проверенные API от сообщества.Используйте парное программирование, надежную документацию API, вики ... Но вы действительно должны позволить различным командам делать свой выбор, создавать свой собственный код, даже если это означает дублирование кода между командами или различные дизайнерские решения.Вы знаете, если проектные решения отличаются, это может быть потому, что потребности разные.

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

4 голосов
/ 11 апреля 2011

Существует несколько методов Agile / XP, которые вы можете использовать для решения этой проблемы, например:

  • общение друг с другом (например, во время ежедневного совещания в режиме ожидания)обзор кода

Затем создайте, документируйте и протестируйте один или несколько проектов библиотек утилит, на которые можно ссылаться.Я рекомендую использовать Maven для управления зависимостями / версиями.

3 голосов
/ 11 апреля 2011

Вы могли бы подумать о том, чтобы все служебные классы были помещены в хорошо организованную структуру пакета, такую ​​как com.yourcompany.util..Если люди хотят хорошо называть подпакеты и классы, то, по крайней мере, если им нужно найти утилиту, они знают, где искать.Я не думаю, что здесь есть какой-нибудь серебряный ответ.Общение важно.Возможно, если разработчик отправит простое электронное письмо остальным сотрудникам разработчиков, когда они напишут новую утилиту, этого будет достаточно, чтобы донести ее до людей.Или общая вики-страница, где люди могут перечислять / документировать их.

1 голос
/ 11 апреля 2011
  1. Командное общение (выкрикивая: «эй, у кого-то есть документ toString?»)
  2. Сохраните служебные классы до абсолютного минимума и ограничьте их одним пространством имен
  3. Всегда думайте: как я могу сделать это с объектом.В вашем примере я бы расширил класс Document и добавил к нему те методы toString и serialize.
0 голосов
/ 14 апреля 2011
  1. стандартная прикладная утилита проекта.создайте флягу с ограниченной областью расширяемости и пакетом, основанным на функциональности.
  2. используйте общие утилиты, такие как apache-commons или коллекции google, и предоставьте абстракцию
  3. для поддержки базы знаний и документации и отслеживания JIRAошибки и улучшения
  4. эволюционный рефакторинг
  5. findbugs и pmd для поиска дублирования кода или ошибок
  6. обзор и тестирование утилит для повышения производительности
  7. util karma!попросите членов команды внести свой вклад в базу кода, если они найдут ее в существующем коде джунглей или нуждаются в новых.
0 голосов
/ 14 апреля 2011

Вы ищете решение, которое поможет вам справиться с этой неизбежной проблемой, тогда я могу предложить инструмент:

  • TeamCity : удивительный простой в использовании продукт, который управляет всей вашей автоматической сборкой кода из вашего репозитория и запускает модульные тесты и т. Д.
    Это даже бесплатный продукт для большинства людей.
    Еще лучше : в него встроено обнаружение дублирования кода по всему вашему коду .

Больше материала для чтения:

0 голосов
/ 12 апреля 2011

Довольно сложно создать инструмент, который распознает «ту же функциональность». (Теоретически это на самом деле невозможно, и там, где вы можете сделать это на практике, вам, вероятно, понадобится доказатель теоремы).

Но часто случается, что люди клонируют, что близко к тому, что они хотят, и затем настраивают это. Этот код можно найти, используя детектор клонов.

Наш CloneDR - это инструмент для обнаружения точного и почти пропущенного клонированного кода на основе использования параметризованных синтаксических деревьев. Он соответствует проанализированным версиям кода, поэтому его не смущают разметка, измененные комментарии, измененные имена переменных или, во многих случаях, вставленные или удаленные операторы. Существуют версии для многих языков (C ++, COBOL, C #, Java, JavaScript, PHP, ...), и вы можете увидеть примеры прогонов обнаружения клонов на предоставленной ссылка на сайт. Обычно он находит дублированный код на 10-20%, и если вы абстрагируете этот код в библиотечные методы на религиозной основе, ваша кодовая база может фактически сжаться (что произошло с одной организацией, использующей CloneDR).

0 голосов
/ 11 апреля 2011

Эта проблема помогает при объединении функций IDE «автозавершение кода» с языками, которые поддерживают расширения типов (например, C # и F #). Чтобы вообразить, что в Java есть такая особенность, программист может легко изучить все методы расширения класса в среде IDE, например:

Document doc = ...
doc.to //list pops up with toXmlString, toJsonString, all the "to" series extension methods

Конечно, у Java нет расширений типов. Но вы можете использовать grep для поиска в вашем проекте «всех статических открытых методов, которые принимают SomeClass в качестве первого аргумента», чтобы получить аналогичное представление о том, какие служебные методы уже были написаны для данного класса.

...