2 Категории сложности, универсальности и гибкости
Помимо изменения понятия компьютерной программы всё более и более наполнялись содержанием категории, относящиеся к качественным свойствам программы. Три качественных свойства программы мы рассмотрим во второй части нашей статьи.
2.1 Категория сложности
Программирование, как и многие другие виды деятельности, направленные на создание качественно новых вещей, имеет ограничения, накладываемые рабочей средой. На заре программирования ограничения были связаны в основном с физическими возможностями машин, и понятие сложность программирования более относилось к эксплуатации машин, нежели к внутреннему смыслу программ. Но через очень короткое время, когда развитие техники существенно отодвинуло сугубо физические ограничения, и программисты смогли разрабатывать программы большого размера, а в настоящее время - крупные программные системы, разразился «кризис программирования», перманентно длящийся до сих пор, и являющийся притчей во языцех уже для нескольких поколений программистов.
Под кризисом программирования обычно подразумевают такое состояние, при котором программист или нормально (по представлениям своего времени) организованный коллектив программистов, не имея существенных физических ограничений машины, не может создать за разумное время не содержащую ошибок программу, решающую большую и сложную задачу. Причин кризиса программирования несколько, и одна из них - сложность создаваемой программы.
Сложность решаемой задачи - это субъективное ощущение человека; обычно это ощущение возникает в тогда, когда человек не знает способа решения задачи. Чем менее очевиден способ решения, тем сложнее кажется задача. Категорию сложности в программировании можно рассматривать с двух позиций: теоретической и инженерной. Сложность с точки зрения теории обычно связана с поиском эффективных алгоритмов решения вычислительных задач и с построением адекватных информационных моделей. Теоретическая сложность не относится непосредственно к процессу программирования. Сложность с точки зрения инженерной деятельности обычно связана с количеством элементов программы и способностью программиста удержать эти элементы и взаимосвязи между ними в своём уме. Мы будем рассматривать только сложность с инженерной точки зрения.
Под элементами программы мы понимаем части программы, атомарные на определённом уровне абстракции.
В первых языка программирования среднего уровня целые программы представляли собой линейно организованные последовательности операторов. Предельный размер таких программ с точки зрения сложности составляет порядка 1000 строк. С программами большего размера, реализованными с помощью "линейного" подхода, эффективно работать (модифицировать и расширять их) практически невозможно, потому что программист не в состоянии запомнить все используемые переменные, а логические части программы можно искать лишь по комментариям к коду или по номерам строк.
Когда отрасль столкнулась с задачами, требующими разработки более крупных программ, был применён давно известный метод управления сложностью "разделяй и властвуй": прежде линейно записываемые операторы начали организовывать в иерархию вызовов процедур и функций, появилось понятие областей видимости переменных, переменные разделились на глобальные и локальные, переменные были организованы в структуры данных - структурный подход к программированию. Теперь программист мог мыслить решение задачи как иерархию сравнительно коротких алгоритмов, состоящих из вызовов процедур и функций, содержание которых кратко выражалось в их названиях. Благодаря такому инженерному решению предел сложности программ отодвинулся на два порядка и оценивается в 100 тысяч строк кода на языке высокого уровня.
Несмотря на то, что вызовы процедур и функций организованы иерархически, сами процедуры и функции во многих языках структурного программирования представляют собой одноранговые множества, и на более высоком уровне абстракции могут мыслиться как элементы программы. Когда появилась потребность создавать программы, требующие написания тысяч процедур и функций, которые человеческий ум не был в состоянии охватить в совокупности, кризис программирования перешёл на следующий этап - опять потребовалось каким-либо образом организовать программные элементы. Развитием структурного подхода стал модульный подход, в котором процедуры и функции больших программ группируются в модули, пакеты или библиотеки, часто разрабатываемые разными программистами. Предел сложности программ увеличился, и верхняя его оценка сейчас неизвестна, потому что по ряду причин единых программных решений размером свыше нескольких миллионов строк кода не создавалось - вместо единых решений создают программные комплексы.
Принципы декомпозиции кода программной системы на модули могут быть самыми разными, одним из принципов является функциональная декомпозиция - каждый модуль программы решает какую-то отдельную задачу или содержит набор процедур, функций и данных, в совокупности предоставляющих какую-нибудь услугу. Благодаря функциональной декомпозиции программной системы на модули отдельный модуль можно считать программным элементом на более высоком уровне абстракции. Интересный эффект наблюдается в программных системах, работающих как множество параллельно исполняемых процессов. В каждой процессе модуль может иметь независимые значения глобальных переменных, может независимо инициализироваться. Фактически, в программной системе может существовать несколько автономных друг от друга экземпляров одно и того же программного модуля. Если довести принцип модульности до логического конца, каждый минимально возможный функциональный элемент программы реализуя в виде отдельного модуля, а также позволить модулям в рамках одного процесса иметь несколько автономных друг от друга экземпляров, мы приходим к тождеству между понятием модуля и понятием класса в объектно-ориентированном программировании. Экземпляры класса - объекты являются атомарными программными элементами системы, созданной при помощи объектно-ориентированного подхода. Каждый класс содержит внутри себя структуру данных, а также процедуры и функции, логически связанные функциональным назначением класса.
Модули обычно имеют две части: интерфейс и реализацию. Процедуры, функции и структуры данных, описанные в интерфейсной части, доступны другим модулям программы, а в части реализации могут находиться процедуры, функции и данные, доступные лишь внутри модуля. Так в модульном программировании реализуется принцип инкапсуляции, перешедший впоследствии в объектно-ориентированный подход. Благодаря принципу инкапсуляции на каждый модуль или объект программы существует две точки зрения: снаружи и изнутри.
Снаружи объект выглядит как "чёрный ящик" с известными входами и выходами. Кроме того программист знает функциональное назначение объекта, которое обычно кратко выражено в названии класса объектов. Этих сведений вполне достаточно, чтобы программист смог воспользоваться модулем или объектом программы. Взаимодействие объекта и окружающего его мира происходит через интерфейс объекта. Одни объекты, являясь клиентами, "поручают" выполнение каких-либо услуг или решение какой-либо задачи другим объектам, являющимся серверами.
Внутри объект имеет какую-либо реализацию своего интерфейса. Когда программист разрабатывает новый объект, он рассматривает объектный интерфейс как множество возложенных на объект обязательств выполнить те услуги или решить те задачи, которые требуются внешнему для объекта миру. Как именно эти обязательства будут реализованы, для внешнего мира не столь важно. (Хотя с точки зрения всей системы это важный вопрос.) Каждый объект или модуль в своей реализации может обращаться к другим объектам или модулям, но благодаря принципу инкапсуляции эта информация скрыта от клиентов объекта.
Описанная организация программного кода в виде иерархии модулей или систем объектов на данный момент является достаточно удобным решением для построения программных систем любого размера. Каждая задача внутри программной системы решается через организацию ответственной за решение задачи подсистемы, состоящей из небольшого числа программных объектов. Благодаря свойству инкапсуляции программист может безопасно абстрагироваться от внутреннего устройства объектов, концентрируя своё внимание лишь на сути решаемой им задачи.
В настоящее время для крупных программных систем проблема сложности переходит из сферы программирования в сферу проектирования: именно на стадии проектирования необходимо снять противоречия между требованиями к программной системе.
Окончание следует
Information
- Posted on 31.01.2010 22:03
- Просмотры: 4021