Компонентно-ориентированное программирование: различия между версиями

Материал из Викиучебника — открытых книг для открытого мира
Содержимое удалено Содержимое добавлено
м замена категории на шаблон для работы полки, removed: Категория:Программирование с помощью AWB
м →‎Ссылки: установка шаблона "по алфавиту" по консенсусу на форуме с помощью AWB
Строка 74: Строка 74:
* Материалы по Компонентному Паскалю [[Компонентный Паскаль]]
* Материалы по Компонентному Паскалю [[Компонентный Паскаль]]


{{Темы|Программирование}}


{{По алфавиту}}

{{Темы|Программирование}}

Версия от 22:02, 24 марта 2016


Эта работа распространяется на условиях лицензии Creative Commons Attribution 3.0 (CC-BY).

Вы можете без ограничений распространять данную работу, изменять и использовать её в любых (в том числе коммерческих) целях при условии указания оригинального авторства.

Компонентно-ориентированное программирование

Эта парадигма программирования направлена прежде всего на повышение надёжности открытых динамических систем. Суть компонентно-ориентированного программирования (далее КОП) сводится к возможности контролировать взаимодействие проектируемых и выполняемых модулей на предмет согласованности информационных структур. Идея является относительно новой. Частично идеи КОП воплощены в такие языки как Java, Ada, C#, прямым применением идей КОП являются языки программирования Modula-2, Oberon, Oberon-2, и наиболее известный из семейства КОП — Компонентный Паскаль.

Отличительные черты КОП

Не смотря на свою относительную молодость — КОП имеет свои особенности, которые регулируют не только особенности языка, но и всей экосистемы КОП. К таким отличительным чертам следует отнести:

  • Чётко выраженную ориентированность на модули. Модуль, является основной структурной единицей.
  • Раздельная компиляция модулей. Это приводит к сбережению вычислительных и временных ресурсов.
  • Строгая типизация, как внутри модуля, так и между модулями. Обеспечивает надёжную работу компонентов в целом.
  • Неизбежность динамической сборки мусора. Для компилируемых языков это важный и необычный механизм.
  • Строгое разделение частей модулей, предназначенных для взаимодействия с другими модулями, и скрытые части только для работы внутри модуля.

Отличия от функционального программирования

Как и в функциональном программировании, в компонент-ориентированном не только возможно, но и приветствуется использование функций и процедур. В обоих видах декомпозиции возможно использование функций внутри других функций, статических и динамических переменных, глобальных и локальных структур. В виду явной необходимости в функциональном программировании введены такие абстракции, как например: анонимные лямбда-функции, хвостовая рекурсия, побочные эффекты и ряд др. специфических особенностей. Ряд средств в функциональном программировании на самом деле избыточен, но другая часть действительно нужна, но это только усугубляет проблему: из-за различных эффектов характерных для функционального программирования, программисту легко потерять логику рассуждений и совершить какую-нибудь досадную ошибку, на выявление которой может уйти огромная часть времени. Такой язык, как Си — вполне выражает такую парадигму. Мощный, как трёхлинейная винтовка, и такой же опасный — можно шутя выстрелить себе в ногу с более чем фатальными последствиями. В целом, про функциональное программирование можно сказать, что оно отлично применимо к несложным проектам, которые должны работать с высочайшей скоростью на относительно простых аппаратных платформах. Компонент-ориентирование программирование стремится всеми доступными средствами к надёжности и гибкости на сколько это возможно. В целом, программа представляет из себя на КОП несколько хорошо изолированных частей. Это приводит к ясной структуре и простым эффективным правилам передачи и обработки информации, что безусловно положительно влияет на надёжность в целом. Сокрытие информации отсеивает лишнюю информацию для программиста, что позволяет ему сконцентрироваться на существенной части задачи. Упрощение языка и отказ от многих сомнительных приёмов программирования вынуждают программиста писать простой для понимания программный код, в котором трудно совершить досадную ошибку. Компилятор языка программирования в духе КОП не позволит создать программу, в которой программистом допускает нарушение отношений типов данных. Можно сказать, что требование к надёжности программ, создаваемых с помощью КОП является главным. В связи с этим, синтаксис языков специально спроектированных для компонент-ориентированного программирования достаточно прост и выразителен. Едва ли возможна, например, в таком языке как Компонентный Паскаль нечитаемая конструкция, как в уже вышеупомянутом Си. В попытках понять что имел в виду программист на Си можно провести огромную часть рабочего времени. В целом про КОП можно сказать, что он предназначен для крупных программных систем, к которым предъявляются особые требования к надёжности.

Отличия от объектно-ориентированного программирования

С точки зрения программиста, придерживающегося компонент-ориентированного программирования далеко не всё является объектом. Строго говоря, это действительно так. Понятие объект импортировано из логики, где кроме объектов есть ещё и субъекты. Разница между ними заключена, как известно, в том, что субъекты воздействуют на объекты. Но где можно найти формулировку субъекта в объектно-ориентированном программировании(ООП)? Т. е. попросту говоря, в ООП изначально неверно определены категории сущностей, что неизбежно заставляет ввести в ООП такие абстракции как «активный объект» (что на самом деле и является субъектом, хотя субъект не только не производное от объекта, а вообще независимая самостоятельная абстракция в логике).

Приверженцы ООП в качестве важного отличительного свойства этой парадигмы указывают на возможность наследования — отношения двух сущностей, когда одна из сущностей либо полностью, либо частично на основе наследования повторяет поведение и состояние другой сущности. Действительно, такое поведение часто встречается в реальном мире, и не лишено основания. Но, тем не менее, существующие попытки свести всю иерархию сущностей в одно дерево развития на самом деле мало чем оправдано и приводит к тому, что изменив базовую сущность автоматически происходит изменение и всех зависимых сущностей. И это не всегда удобно. Точнее даже, частенько это может грозить катастрофой программному проекту. Кроме того, существует огромное число объектов, для которых привязка к базовому типу совершенно необоснованна. Так например, такой ООП язык программирования, как Java принуждает программиста строить все объекты от встроенных. Из-за подобного жёсткого ограничения пришлось вводить возможность поломать это правило. Разумеется, это приводит к потенциальным нарушениям. В целом, ООП заметно облегчает декомпозицию программы, но в то же время привносит и свои сложности, в ключе понимания логики исполнения программы в целом. Компонентно-ориентированное программирование не принуждает программиста использовать какие-либо сущности. Язык и так достаточно строг, для того, чтобы программист не смог поломать свой код. Понятие сущности существенно совмещено с понятием модуля, и как правило, сущность содержится в одном модуле, что позволяет более полно контролировать логику программы. Впрочем, при явной необходимости сущность может быть распределена между многими модулями (например, при случае объединении в одной сущности многих других меньших сущностей). При этом КОП, в отличии от ООП элегантно и естественно решают проблему хрупкого базового класса — множественное наследование попросту не нужно.

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

сахар = вода + углекислый_газ + ультрафиолет

Но стало ли понятней, что происходит в таком коде? Давайте взглянем на код написанный в функциональном стиле

сахар = СинтезироватьСахар(вода, углекислый_газ, ультрафиолет)

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

вода = сахар - (углекислый_газ + ультрафиолет)

в корне не верна и даже эмоционально вызывает чувство протеста. Кроме того, до сих пор идут жаркие споры, а на сколько полиморфизм вообще является частью ООП? Существует достаточное число языков программирования в которых полиморфизм есть, а объектов -- нет!) Обратное утверждение также верно: существуют языки с парадигмой ООП, но в них нет перегрузки операторов. Очень спорно назвать такие языки не полностью отвечающими парадигме ООП.

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

Контроль во время компиляции и исполнения

Эти две фазы гораздо более тесно связаны, чем в случае других парадигм программирования. Так как, например C++ не гарантирует исполнения кода во всех возможных случаях, в него с неизбежностью введены лексемы, служащие для перехвата исключений. И это, в целом, плохо. Такие лексемы говорят о том, что язык не был сконструирован, а скорее сочинён. Более того, наличие инструкций перехвата управления, вовсе не гарантирует отсутствие таких эффектов как улететь в космос (неправильное исполнение программы, которое может нанести существенный ущерб целостности структур данных). В КОП нет ничего подобного. Подобные лексемы избыточны и в-общем случае -- бессмысленны. В то же время, вполне возможна ситуация, когда интерфейс модуля в КОП был изменён. В такой ситуации надстройка над средой исполнения, отвечающая за сведение различных модулей в режиме исполнения заметит несоответствие типов и запретит передачу данных между несовместимыми модулями. Это позволит своевременно остановить распространение ошибки по всей программе между модулями, которые, возможно, даже не участвовали во взаимодействии двух первых несовместимых модулей. Такое правило контроля во время исполнения приводит к важному следствию: взаимодействие между модулями происходит только с помощью базовых типов. Во время компиляции, все задействованные модули для разработки проверяются на согласованность со вновь создаваемым модулем, а если уже существующий модуль необходимо изменить -- создаётся новый (с другими интерфейсами), либо изменяется содержимое существующего модуля (с учётом возможных последствий для зависимых модулей). Так, например, решается проблема "ада динамических библиотек".

Отличительные особенности компонентно-ориентированного программирования

КОП, в чём-то расширяет, а в чём-то ограничивает другие идиомы программирования. Так, поддерживается функциональный стиль, но ограничиваются сторонние эффекты. Как такого в КОП нет понятия класса, но в то же время, структуры данных, и привязанные к ним процедуры в рамках модуля -- вполне описывает понятие класса в ООП. Достаточно простые исходные лексемы позволяют строить (и поощряют такое построение) код, который хорошо согласуем, сильно типизирован, может быть расширен (как через исходный код, так и через скомпилированный в машинном виде). КОП, прежде всего, нацелен на устойчивую динамичную среду. Так например, возможна ситуация в ОС Linux, когда с помощью специальных механизмов в ряде случая можно внести исправления прямо в работающее ядро, в Компонентном Паскале -- динамически заменить один работающий модуль на другой не представляет сложности (за исключением тех случаев, когда происходит взаимодействие с ресурсами, которые Компонентный Паскаль контролировать, по понятным причинам, -- не может). Также следует упомянуть о том, что в компилируемых языках автоматическая уборка мусора не такая простая вещь. В динамических системах потребность в сборщике мусора ещё выше. Обычно, программист сам должен контролировать низкоуровневые операции с памятью. И здесь нарушается принцип абстракции. Используя КОП можно построить систему, в которой уборка мусора будет выполняться автоматически и абсолютно точно. Это можно достигнуть несколькими способами, и самый доступный из них -- подсчёт ссылок на структуру данных. Если модуль, который обрабатывает какую-либо структуру выгружен из памяти -- очевидно, эти данные уже никто не сможет обработать).

Примеры реализации компонентно-ориентированного программирования

Существует несколько языков программирования, которые следуют принципу КОП. Так или иначе, они связаны с именем Никлауса Вирта. Наиболее известные реализации можно представить в виде следующей таблицы:

Oberon
Oberon-SA Oberon-2 Активный Оберон
Revised Oberon Компонентный Паскаль

На базе Оберонов были разработаны такие языки, как Go, Modula-3, C#. В основе Java также лежит компонентно-ориентированный подход (который, впрочем, не был реализован в полной мере).

Компонентный Паскаль

Одна из самых известных реализаций КОП. Существует ряд весьма сложных проектов, выполненных на нём. Чего стоит система управления транспортом в Швейцарии, или бортовая система для беспилотного вертолёта. Компонентный Паскаль можно встретить под ОС Windows, Linux, ColibriOS. На родственных языках была написана ОС Oberon, Blubottle, ряд других экспериментальных проектов. Сущеcтвует реализация Компонентного Паскаля для среды .Net.

Ссылки