Статьи

Модульные функции вашего старого приложения для удовольствия и прибыли

Пользователь хочет, чтобы я изменил / исправил ошибку в определенной функции, но какие именно классы я должен изменить? Задавать себе этот вопрос на регулярной основе? Читать дальше!

В этой статье я хотел бы познакомить вас с новым инструментом на основе платформы NetBeans, названным Featureous, и познакомить вас с новыми функциями и некоторыми частями реализации будущей версии 3.0. Подробнее об этом инструменте вы можете услышать на JavaOne этого года в сессиях «Модуляризация функций вашего старого приложения для удовольствия и получения прибыли» и «Инструкции по миграции старых приложений на модульные системы».

Цель Featureous — облегчить понимание того, как унаследованные программы реализуют свои наблюдаемые пользователем функции. Проще говоря, если вы когда-либо были в одной из следующих ситуаций, то вы уже знаете мотивы, стоящие за Featureous:

  • «Пользователь хочет, чтобы я изменил / исправил ошибку в этой функции, но какие именно классы я должен изменить?»
  • «Мне нужно повторно использовать часть реализации существующей функции, но как мне найти соответствующие классы?»
  • «Я исправил ошибку, изменив класс Foo, на какие функции могло повлиять это изменение?»
  • «Я хочу распутать свои функции, чтобы их можно было гибко добавлять и удалять из приложения. Как разделить базу кода, чтобы добиться этого?»

 Featureful помогает решить эти вопросы, расширяя IDE NetBeans механизмом отслеживания выполнения функций. Полученные следы могут быть затем измерены и исследованы под различными углами в ряде аналитических обзоров:

Подробнее о отслеживании функций с помощью Featureous, его аналитических представлений и связанных с ними исследовательских целей можно найти на веб-сайте проекта: http://featureous.org .

Основное внимание в недавних разработках проекта было уделено тому, чтобы Featureful стал не только инструментом анализа, но и инструментом реструктуризации. Святой Грааль этого состоит в том, чтобы облегчить программистам ответ на вопрос «где реализована функция Foo?». Мы хотим отвести программистов от обычного ответа в духе «ну, вы знаете, это сложно … Итак, в этом пакете есть класс Bar, а затем куча других классов в некоторых других местах …» в направлении просто сказать «у него есть свой специальный пакет». Шаг, который мы сделали для достижения этой цели, — это модуль «Remodularization workbench», который обогащает инструмент множеством новых функций, связанных с реструктуризацией. Разумеется,Реализация этих новых функций довольно интересна (как мы увидим чуть позже).

Основной утилитой ремодуляризации является возможность автоматического перемещения классов между пакетами в соответствии с набором целей проектирования. Набор задач, доступных на данный момент, включает традиционные понятия сплоченности и сцепления, а также критерии, ориентированные на особенности, которые направлены на локализацию и распутывание функций. Для любопытных механизм под капотом, называемый «генетическим алгоритмом многоцелевой группировки», позволяет произвольно смешивать и сопоставлять имеющиеся цели и оптимизировать их множество одновременно.

Визуальная библиотека + редактор NetBeans

Чтобы позволить программисту просматривать структуры, предложенные рабочей средой ремодуляризации, мы использовали API визуальной библиотеки NetBeans. Одна из интересных вещей, которые мы здесь делаем, позволяет программисту перетаскивать классы между пакетами на диаграмме, чтобы вручную настроить результирующую структуру программы. Хотя реальная реструктуризация источников происходит только после завершения всех ручных корректировок, мы постоянно информируем программиста о влиянии ее действий на качество исходного кода, представляя постоянно обновляемые метрики связности, связи, рассеяния и запутывания.

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

Сообщение, которое я хочу подчеркнуть здесь, заключается в том, что эту функцию было чрезвычайно легко создать, просто комбинируя некоторые из существующих функций платформы NetBeans, а именно Visual Library и Editor API. Этот пример является хорошим примером того, как существующая инфраструктура позволила чему-то, что Хел Вариан назвал бы «комбинаторными инновациями» (
http://www.youtube.com/watch?v=hqaA-fgdXEE ).

Ниже вы можете найти фрагмент кода, который демонстрирует основные принципы, лежащие в основе (a) использования LevelOfDetailsWidget, (b) создания локального экземпляра (который не будет отображаться в качестве новой вкладки редактора в IDE) полноценного редактора NetBeans , (c) и оборачивая его в ComponentWidget для взаимодействия с остальной частью сцены Visual Library.

public class UMLClassWidget extends Widget{
...
   Scene scene = ...;
   setLayout(LayoutFactory.createOverlayLayout());
   //Create uml widget
   Widget uml = new LevelOfDetailsWidget(scene, 0.0, 0.1, 3.0, 4);
   uml.addChild(classHeader);

   addChild(uml);

   //Create an NB editor
   FileObject javaFile = ...;
//Get Document out of FileObject
   DataObject od = DataObject.find(file);
EditorCookie ec = (EditorCookie) od.getCookie(EditorCookie.class);
Document doc = ec.openDocument();
doc.putProperty(Language.class, JavaTokenId.language());
doc.putProperty("mimeType", "text/x-java");

   BaseKit javaKit = BaseKit.getKit(JavaKit.class);
   JEditorPane pane = new JEditorPane();
   pane.setEditorKit(javaKit);

   pane.setDocument(doc);

   EditorUI editorUI = Utilities.getEditorUI(pane);
   JComponent editor = editorUI.getExtComponent();
   Widget compWidget = new ComponentWidget(scene, editor);
   Widget lod = new LevelOfDetailsWidget(scene, 4, 4, 15.0, 15.1);
   lod.addChild(compWidget);

   addChild(lod);
}

Реструктуризация исходного кода

Другой частью рабочей среды ремодуляризации, которая значительно выиграла от базовой инфраструктуры NetBeans, являются процедуры физической реструктуризации исходного кода Java. Здесь мы просто повторно использовали существующий рефакторинг «move class», чтобы перераспределить классы в их пакеты назначения. Поскольку в определенный момент мы наблюдали, что иногда операторы импорта получающихся классов могут быть неправильными (либо из-за особого способа, которым мы называем рефакторинг, либо из-за его ограничений), мы также повторно использовали действие «исправить все импорты» для публикации — обработать полученный исходный код.

Ниже вы можете найти наш рецепт для программного запуска этих рефакторингов. (Обратите внимание, что для того, чтобы компилировать это, вам нужно будет создать зависимости для реализации некоторых модулей NetBeans.)

DataObject javaObject = ...; //Java source file
String targetPkg = ...;
FileObject projectSrcDir = ...;

MoveRefactoring refactoring = new MoveRefactoring(Lookups.fixed(javaObject.getPrimaryFile(), new TreePathHandle[]{}));
refactoring.getContext().add(RetoucheUtils.getClasspathInfoFor(javaObject.getPrimaryFile()));

URL srcDirUrl = URLMapper.findURL(projectSrcDir, URLMapper.EXTERNAL);
refactoring.setTarget(Lookups.singleton(new URL(srcDirUrl.toExternalForm() + URLEncoder.encode(targetPkg.replace('.', '/'), "utf-8"))));

refactoring.checkParameters();
refactoring.preCheck();

RefactoringSession rs = RefactoringSession.create("Move class");
refactoring.prepare(rs);
rs.doRefactoring(true);

// Fix imports and save
FileObject javaFile = javaObject.getPrimaryFile();
JavaFixAllImports.getDefault().fixAllImports(javaFile);
SystemAction.get(SaveAllAction.class).performAction();

Инкапсуляция полученной программы в виде модулей NetBeans

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

Приведенный ниже довольно плотный код демонстрирует, как мы программно (а) создаем набор модулей и настраиваем его, (б) создаем модуль, (в) объявляем некоторые из его пакетов общедоступными, (г) добавляем зависимости между модулями.

String suiteDir = ...;

//Create suite and setup branding
SuiteProjectGenerator.createSuiteProject(new File(suiteDir), NbPlatform.getDefaultPlatform().getID(), true);
SuiteProject suite = (SuiteProject)ProjectManager.getDefault().findProject(FileUtil.toFileObject(new File(suiteDir)));
SuiteProperties suiteProps = new SuiteProperties(suite, suite.getHelper(), suite.getEvaluator(), SuiteUtils.getSubProjects(suite));
suiteProps.getBrandingModel().setBrandingEnabled(true);
suiteProps.getBrandingModel().setName("SuiteName");
suiteProps.getBrandingModel().store();
suiteProps.storeProperties();

//Create a module in the suite
String moduleDir = ...;
NbModuleProjectGenerator.createSuiteComponentModule(new File(moduleDir), codeBaseID, moduleName, "bundle.conf", "layer.xml", suite.getProjectDirectoryFile(), false, false);
NbModuleProject mod = (NbModuleProject)ProjectManager.getDefault().findProject(FileUtil.toFileObject(new File(moduleDir)));
mod.getPrimaryConfigurationData(); // force creation of config files

//Add public package declaration
ProjectXMLManager xmlMan = ProjectXMLManager.getInstance(mod.getProjectDirectoryFile());
PackageExport[] pes = xmlMan.getPublicPackages();
Set<String> pkgs = new HashSet<String>();
for (PackageExport pe : pes) {
pkgs.add(pe.getPackage());
}
pkgs.add("org.foo.bar");
xmlMan.replacePublicPackages(pkgs);

//Add dependency on another module destMod
NbModuleProject destMod = ...;
ModuleDependency dep = new ModuleDependency(destMod.getModuleList().getEntry(destMod.getCodeNameBase()), null, null, true, false);
xmlMan.addDependency(dep);

Подводя итог, это демонстрирует некоторые части реализации, включенные в предстоящий Featureous 3.0. Очень скоро вы сможете сами опробовать новые функции, просмотреть полный исходный код и добавить свои собственные модули и патчи в инструмент, так что следите за обновлениями. Запланированная дата релиза — 30 сентября.

Наконец, если вы будете в JavaOne, не стесняйтесь заходить и сообщать нам о ваших впечатлениях, опыте и идеях относительно Featureous.