Рубрика:
Наука и технологии
|
Facebook
Мой мир
Вконтакте
Одноклассники
Google+
|
САЛИБЕКЯН С.М., к.т.н., доцент Национального исследовательского университета «Высшая школа экономики», ssalibekyan@hse.ru
ЛЕОХИН Ю.Л., д.т.н., профессор Национального исследовательского университета «Высшая школа экономики», yleokhin@hse.ru
Как обойти рефакторинг?
В статье предлагается способ повышения технологичности разработки программного обеспечения, при котором, в частности, отсутствует необходимость в использовании процедуры рефакторинга. Это обеспечивается с помощью нового принципа организации структуры данных и вычислительного процесса, основанного на парадигме вычислительного процесса с управлением потоком данных
Современные разработчики сложных программных продуктов сталкиваются с необходимостью рефакторинга – частичное или полное переписывание программы в целях улучшения ее читабельности или внутренней структуры [1, 2]. Этанеобходимость возникает при разработке программного обеспечения (ПО) в несколько этапов по так называемой спиральной модели – каждый «виток» означает «рождение» новой версии программы. При этом с расширением функциональности в программе могут появляться новые внутренние противоречия, которые сложно обнаружить в процессе отладки. Как правило, они связаны с неполным формированием условий решения и некорректной постановкой задачи, с неполной информацией о реальных процессах, происходящих в источниках и потребителях информации. Другой причиной таких трудно обнаруживаемых ошибок может являться некорректность или неполнота программных спецификаций, являющаяся следствием отсутствия полной информации о реальных процессах, проходящих, например, в объектах управления. Со временем эти противоречия достигают такого уровня, что начинают значительно усложнять и даже делать невозможной дальнейшую разработку программы. В этот момент назревает необходимость рефакторинга, т.е. оптимизации кода или переписывания программы заново. Такая процедура необходима, например, для программ, написанных в объектно-ориентированной парадигме. Естественно, рефакторинг значительно повышает стоимость и время разработки ПО, однако без него нельзя обойтись. Или можно? Свойство программы, позволяющее ей не терять свою целость при ее модификации, назовем изоморфизмом. В статье предлагается повысить изоморфизм программы посредством применения объектно-атрибутного (ОА) подхода к организации вычислительного процесса и структур данных. Разработка ОА-подхода ведется в Московском институте электроники и математики Национального исследовательского университета «Высшая школа экономики».
Причины применения рефакторинга
В настоящее время наибольшую популярность для разработки больших программ приобрел объектно-ориентированный подход (ООП) [3]. Главным его преимуществом является абстракция данных, позволяющая эффективно выполнить декомпозицию задачи, представить данные и программный код в удобном для человека виде – классы и объекты. Однако ООП свойственно и множество недостатков [4], в частности, следует отметить необходимость рефакторинга, возникающую при разработке большой программы, для которой отсутствует полная информация о реальных процессах, происходящих, например, в объектах управления. Например, при использовании спиральной модели жизненного цикла разработки ПО [5], где после завершения каждого «витка» появляется новая версия программы, которая передается пользователям. Так как в структуре программы заранее не учитывались будущие ее модификации, с каждой новой версией структура программы становится все более не оптимальной. И в итоге множество внесенных изменений в программный код делает дальнейшую разработку программы практически невозможной. Единственным выходом из этого тупика становится рефакторинг. Подобная ситуация может возникнуть и в случае разработки программной системы управления объектом, структура и функции которого меняются. Например, фирма разрабатывает семейство автомобилей: сначала выпускается простая модель, потом на ее базе разрабатывается модель, у которой по сравнению со старой добавились новые агрегаты и функции. Следовательно, необходимо модернизировать ПО, оставшееся от старой модификации. Затем выпускается новая модификация и т.д. Можно сделать вывод, что ООП не обладает свойством изоморфизма программы.
Каковы же причины необходимости рефакторинга, ведь в ООП существуют механизмы наследственности и полиморфизма, которые должны повышать гибкость программы? Причин несколько. Во-первых, классы, входящие в программу, имеют жесткую структуру, радикальное изменение которой приводит к «краху» программы. На устранение ошибок, возникших вследствие этого, затрачивается намного больше времени, чем на само внесение изменений.
Во-вторых, эффективной модификации программы в парадигмах процедурного и объектно-ориентированного программирования препятствует жесткий формат интерфейса подпрограмм/методов. Если на определенном этапе создания программы некоторые поля такого интерфейса оказываются ненужными (например, обработка данных была передана другому методу/подпрограмме) или возникает необходимость ввести новые поля, то интерфейс подпрограммы приходится менять. А в том случае, когда в коде программы имеется множество вызовов этого метода/подпрограммы, целостность программы нарушается. Для преодоления этого недостатка применяется специальный вид классов – абстрактные классы, представляющие собой интерфейс, с помощью которого создаются производные классы, выполняющие вычислительную работу. Абстрактный класс частично решает проблему интерфейса, однако делает программу более громоздкой.
Разработчиками была предпринята попытка преодолеть и первый недостаток ООП с помощью паттерного программирования [6]. Паттерн – это заранее сделанная заготовка программы (шаблон), которая «дорабатывается» для решения конкретной прикладной задачи. Количество паттернов значительно меньше (несколько десятков), чем стандартных библиотечных классов, которые в продвинутых средах программирования исчисляются десятками тысяч (например, в C# ихнасчитывается около 50 тысяч). Паттерны не собираются в жесткую структуру, как объект, а действуют автономно. Паттерны, реализованные в виде объектов, представляют собой среду, в которой элементы обмениваются между собой сообщениями и могут достаточно легко изменять информационные связи между собой. Такая программа обладает гибкостью и, следовательно, изоморфизмом. Единственным ограничением является жесткий интерфейс методов. Практически любой паттерн-объект может взаимодействовать с любым другим при соблюдении единственного условия – совпадение интерфейсов. Авторы предлагают объектно-атрибутный (ОА) принцип программирования, который позволит снять и это ограничение.
Изоморфизм программы в объектно-атрибутном программировании
ОА-подход применим как к аппаратному, так и к программному обеспечению вычислительной системы. В статье внимание, естественно, уделяется программному обеспечению. ОА-подход относится к классу dataflow (вычисления суправлением потоком данных), поэтому программа в ОА-подходе представляет собой совокупность функциональных устройств (ФУ), обменивающихся между собой данными, оформленными в виде токенов (операнд, снабженных служебной информацией). ФУ представляет собой процедуру. ОА-подход, таким образом, весьма напоминает акторное программирование [7], однако имеет от него существенные отличия. Так, ФУ – это подпрограмма со стандартным интерфейсом (у акторов интерфейсы разные). Этот интерфейс состоит только из трех полей, и через него в один момент времени передается только один операнд. Если операндов необходимо передать несколько, тогда они поступают впроцедуру последовательно и их нужно сохранять. Сохранение операндов происходит в контексте ФУ – область памяти, где хранятся промежуточные данные, необходимые ФУ для вычислений. Когда в контексте накапливаются всенеобходимые для операции данные, ФУ производит вычисление результата и его последующую выдачу другим ФУ или выдачу по запросу другого ФУ (в этом случае результат вычислений записывается в регистр контекста). Данные в ОА-системе передаются в виде токенов простого формата. Он состоит из двух полей: атрибут и нагрузка. Атрибут служит для того, чтобы ФУ могло идентифицировать пришедшие к нему данные или указатель на ячейку памяти, хранящийся внагрузке. Теперь можно объяснить предназначение трех полей интерфейса подпрограммы, выполняющей роль ФУ. Первое поле – ссылка на контекст ФУ, где хранятся промежуточные данные, второе – атрибут нагрузки, третье – указатель нанагрузку. В подпрограмме ФУ выделяются фрагменты кода для обработки данных с определенным атрибутом. Обработка может включать: запись данных в контекст, вычисление, запись результата вычислений в контекст ФУ, выдачу результата вычислений (запись данных в ячейку памяти, указатель на которую находится в нагрузке токена), выдачу результата, оформленного в виде токена, другим ФУ. Вычислительный процесс в программе, организованной по ОА-принципу, представляет собой обмен данными между ФУ. Данные для ФУ также могут вводиться и потребляться оператором: исходные данные, управляющее воздействие, сбор и выдача результатов работы программы.
Что дает такая организация программы? Она как раз и обеспечивает изоморфизм программы. Во-первых, универсальный интерфейс и последовательная передача операндов позволяют произвольным образом менять количество и состав операндов ФУ. Допустим, изначально подпрограмма обрабатывала определенный набор операндов, затем в ходе модификации возникла необходимость добавления новых операндов. С этой целью в подпрограмму работы ФУ вводится функционал обработки новых атрибутов, идентифицирующих новые операнды. Если оказалось, что некоторые операнды не должны влиять на логику работы программы, тогда программные блоки, ответственные за обработку данных ссоответствующими атрибутами, удаляются из ФУ-подпрограммы. В итоге если приходит бесполезный операнд, то ФУ его игнорирует. Универсальный интерфейс помогает наращивать функциональность ФУ без нарушения целостности всех программы. Это можно обеспечить тремя способами. Способ первый: добавление у ФУ возможности обрабатывать данные с новыми атрибутами при сохранении функциональности старых атрибутов. Тогда целостность старой части программы с добавлением новой функциональности ФУ не нарушается. Способ второй – применение двух режимов работы ФУ, которые можно назвать «по-старому» и «по-новому». Когда требуется применить новую функциональность, сначала на ФУ присылается токен с командой установки нового режима, затем передаются все необходимые операнды, считывается результат, и в завершение передается токен с командой восстановления старого режима. В результате корректность уже написанного кода не будет нарушена. И последний прием – применение ФУ-интерпретатора. Прием используется в том случае, когда какая-то подпрограмма (ФУ) оказывается ненужной (например, ее функционал передается другими ФУ) и ее необходимо удалить из программы, однако в программе присутствует множество ее вызовов. Тогда ненужное ФУ заменяется на ФУ-интерпретатор, играющий роль «заглушки». Он принимает токены вместо удаленного ФУ, преобразует их в поток токенов таким образом, чтобы вычислительная задача была решена с помощью имеющихся ФУ (задача перераспределяется между другими ФУ). Затем ФУ-интерпретатор получает от других ФУ результат вычислений и аналогично ФУ передает его ФУ-потребителю.
Изоморфизм структур данных в объектно-атрибутной парадигме программирования
ОА-подход в программировании позволяет обеспечить изоморфизм не только программы, но и структур данных. Это обеспечивается с помощью информационной структуры, называемой ОА-графом (или ОА-сетью) (см. рис. 1). Она представляет собой совокупность информационных пар (ИП), аналогично токену имеющих два поля: атрибут и нагрузка. Атрибут является идентификатором данных, а нагрузка хранит данные или указатель на ячейку оперативной памяти. ИП могут объединяться во множества, называемые информационными капсулами (ИК). Так как в нагрузке ИП может присутствовать указатель на другую ИК, то с помощью таких указателей множество ИК объединяется в ОА-граф.
Рисунок 1. ОА-граф
Каким же образом ОА-граф обеспечивает изоморфизм структур данных? Это обеспечивается благодаря способности ОА-графа к динамическому изменению. Для того чтобы перестроить его структуру, достаточно добавить или удалить некоторые ИП из ИК.
Покажем изоморфизм на примере организации списков. Для этого используется понятие ОА-списка (см. рис. 2). ОА-список представляет собой набор ИК, объединенных с помощью корневой ИК, где находятся ссылки на ИК списка (назовем ИК, входящие в ОА-список, линиями списка). Линия списка содержит набор данных, которые идентифицируются по атрибутам ИП. Кроме констант, в нагрузках ИП могут находиться и указатели на переменные или на другие ИК. Допустим, вначале был сформирован определенный ОА-список (список 1 на рис. 2). Затем его необходимо дополнить новыми линиями, которые могут привести к нарушению целостности старого списка. В этом случае формируется новый ОА-список (список 2 на рис. 2), корневая капсула которого включает ссылки на старые линии и ссылки на новые. Таким образом, удается сохранить целостность данных, синхронизировать их изменение и избежать дублирования данных. Подобным образом можно поступать и с данными произвольной структуры.
Рисунок 2. ОА-список
Однако в процессе разработки программ может возникнуть потребность добавить новые ИП в ИК. Причем такое добавление может происходить динамически непосредственно во время вычислительного процесса. ОА-подход и здесь позволяет сохранить целостность данных благодаря наличию атрибута у ИП. Поэтому старый код программы при поиске в ИК воспринимает только ИП с определенными атрибутами и игнорирует ИП с неизвестными ему атрибутами.
В заключение следует отметить, что, несмотря на ряд достоинств ОА-подхода к программированию, обеспечивается изоморфизм программы и данных, сокращаются время разработки и стоимость ПО, имеются и недостатки. Во-первых, необходимость представлять структуры данных в виде динамических конструкций, где данные объединены с помощью ссылок. Это приводит к излишнему расходу памяти и замедлению обработки данных. Во-вторых, обратной стороной изоморфизма является наличие большого числа неиспользуемых конструкций данных и программного кода, которые не влияют на правильность работы программы, однако потребляют лишние вычислительные ресурсы.
- Фаулер М., Бек К., Брант Д., Робертс Д., Апдайк У. Рефакторинг: улучшение существующего кода = Refactoring: Improving the Design of Existing Code (2000). – СПб: Символ-Плюс, 2009. – 432 с.
- Джошуа Кериевски. Рефакторинг с использованием шаблонов = Refactoring to Patterns. – М.: Вильямс, 2008. – 400 с.
- Gabriel, R. Objects Have Failed: Notes for a Debate. (retrieved 17 May 2009). URL – http://www.dreamsongs.com/Files/ObjectsHaveFailed.pdf.
- Буч Г., Максимчук Р.А., Энгл М.У., Янг Б.Дж., Коналлен Дж., Хьюстон К.А. Объектно-ориентированный анализ и проектирование с примерами приложений, 3-е изд./Пер. с англ. – М.: Вильямс, 2008. – 720 с.
- Брауде Э. Технология разработки программного обеспечения. – СПб.: Питер, 2004. – 655 с.
- Гамма Э., Хелм Р., Джонсон Р., Влиссидес Дж. Приемы объектно-ориентированного проектирования. Паттерны проектирования. – СПб: Питер, 2001. – 368 с.
- Федотов И. Модели параллельного программирования. – М.: Солон-Пресс, 2012.
Ключевые слова: рефакторинг, вычислительный процесс с управлением потоком данных, разработка программного обеспечения, объектно-атрибутный принцип организации вычислительного процесса и структур данных.
Нow to avoid refactoring?
Salibekyan S.M., Ph.D., Associate Professor of the National Research University «Higher School of Economics», ssalibekyan@hse.ru
Leohin Y.L., Ph.D., Professor of the National Research University «Higher School of Economics», yleokhin@hse.ru
Abstract: The paper proposes a methodic to increase the efficiency of software development, in which, in particular, there is no need to use the refactoring. This is achieved by use a new principle of data structure and computational process organization based on the dataflow paradigm of the computing process.
Keywords: efactoring, dataflow, software development, object-attribute principle of organization of the computational process and data structures.
Facebook
Мой мир
Вконтакте
Одноклассники
Google+
|