Добавил:
ivanov666
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз:
Предмет:
Файл:Сильный искусственный интеллект и объектно-ориентированное программирование синтез парадигм
.pdf
а именно – незыблемая значимость точных последовательных действий и конкретных, идеально подобранных пропорций. В последнем определении алгоритма частично раскрывается суть
того, что в функциональном программировании (да и в целом
в программировании) называется «чистой функцией», т. е. функцией, не изменяющей значения своих аргументов и всегда возвращающей один и тот же результат при одних и тех же входных
данных. Это яркий пример функционирования алгоритма. И именно этот же «шаблонный» пример практически недостижим при
применении любого классического паттерна, так как паттерн
в программировании вовсе не подразумевает высокого уровня
конкретики и точности своих составляющих. Паттерн – это скорее
логически связанный, практически апробированный и задокументированный набор рекомендаций, которые сложились воедино
в результате моделирования (примерно так же, как в нейролингвистическом программировании) успешных действий в каком-либо контексте и направлены на решение некоторой задачи.
10.2. Демаркация между паттерном, шаблоном
и алгоритмом и контекст реализации паттерна
После общего ознакомления с понятиями шаблона, паттерна
и алгоритма приведем пример, иллюстрирующий фундаментальную разницу между ними как феноменами.
Шаблон «Материал». Тактико-технические характеристики: тяжесть – БСТ, класс прочности – В30, морозостойкость –
F300, подвижность – П5, водонепроницаемость – W10, плотность –
D2500.
Алгоритм «Бетон М400». 1 весовая доля цемента, 1,2 весовые доли песка, 2,7 весовые доли щебня. Перемешивать 2 минуты при 15 оборотах в минуту.
Паттерн «Смесь». Смешивание песка некоторой степени
очистки с цементом некоторого качества, а также щебня определенного размера способно приводить к появлению новых материалов с определенными тактико-техническими характеристиками.
131

Можно заметить, что шаблону совершенно «все равно», каким именно образом он будет достигнут, паттерн только лишь
направляет и определяет общие черты, но ничего не конкретизирует и, более того, не гарантирует, а алгоритм совершенно «не
в курсе», зачем именно он реализуется – он просто приносит
один и тот же результат при одних и тех же входных данных
и одинаковых действиях.
Примерно таким же образом все происходит и в рамках разработки программного обеспечения. В этой связи у нас наличествует некоторая коллатеральная по отношению к основной ветви исследования ремарка. А именно: в области построения ПК
практически всегда то, что называют шаблоном, на самом деле
является паттерном. Просто, как мы и говорили ранее, проблема
не только и не столько в переводе с английского на русский терминов pattern и template. Аналогичная проблема – путаница –
существует также и в среде англоязычных программистов: понятие шаблона зачастую заменяется понятием паттерна и, соответ ственно, наоборот. А к примеру, русское выражение «действовать
по шаблону» как раз и означает реализовывать непосредственно
паттерн. Относительно же алгоритма следует заметить, что один
и тот же алгоритм вполне может применяться и в рамках реализации различных паттернов, и для достижения совершенно разных шаблонов – всего лишь изменяются дозировки и контекст.
Мы, в рамках нашего исследования, акцентируем на терминологических, методологических и сущностных различиях между шаблоном, паттерном и алгоритмом столь высокий уровень
внимания по причине важности феномена паттерна непосредственно для построения систем сильного ИИ. У нас, на данном
этапе развития парадигмы ИИ, нет и не может быть никакого
шаблона и даже намека на него – мы и близко не представляем,
чем именно должна и чем может быть система сильного ИИ.
Еще раз напомним, что мы говорим именно о системе сильного
ИИ в том смысле, в котором его понимал Джон Сёрл, т. е. искусственный разум, который «будет разумом в том смысле, в котором человеческий разум – это разум» [61, с. 7]. Мы имеем некоторое представление о том, что есть разум человеческий, но не
132

имеем ни малейшего представления, каким именно может быть
разум нечеловеческий. Именно поэтому никакого шаблона быть
не может. В этом постулате также убежден, к примеру, известный
шведский исследователь, философ Ник Бостром. Он утверждает: «Искусственный интеллект может быть менее человечен,
чем пришелец. Нет ничего удивительного, что любого разумного пришельца могут побуждать к действию такие вещи, как голод, температура, травмы, болезни, угроза жизни или желание
завести потомство. ИИ, по сути, ничто из перечисленного интересовать не будет. Вряд ли вы сочтете парадоксальной ситуацию, если появится какой-нибудь ИИ, чьим единственным предназначением, например, будет: подсчитать песчинки на пля жах
острова Боракай; заняться числом π и представить его, наконец,
в виде обыкновенной десятичной дроби; определить максимальное количество канцелярских скрепок в световом конусе будущего» [82, с. 171–172].
С другой же стороны, если довести суть паттерна, как феномена, до апофеоза, то именно паттерн представляет собой, по
сути, уникальный абстрактный механизм: абстракцию как целое,
состоящую из абстракций как частей и несущую в себе общий
образ действия. Смысл этого определения лучше всего иллюстрируется метафорой «сначала был хаос, а потом из него начали черпать идеи». Ранее мы говорили о реализации некоего абстрактного паттерна, который должен выполнять определенные
действия (порождать подпаттерны и связывать их в единый опыт)
и приводить к соответствующему результату, который, собственно, и должен оказаться сильным искусственным интеллектом.
Саму природу непосредственно алгоритма, который будет использоваться в рамках реализации паттерна, мы на данный момент не затрагиваем. Ведь любой алгоритм, в осмысленном
и целесообразном контексте, как и в случае с нашим примером
выше, должен функционировать в соответствии с некоторым
паттерном и именно им изначально и быть обусловлен, и детерминирован. И уж тем более при полном отсутствии всякого
шаб лона. Т. е. сначала нечто общее – паттерн, затем уже частности – алгоритм; сначала общий план действий, а затем уже
133

конкретные шаги; сначала представление о том, что нечто + нечто = что-то, а затем только А**2 + В**2 = С**2; сначала образ
действия смешивания, а затем уже конкретные дозировки и манипуляции. И именно по этой причине нам видится глубочайший смысл в использовании именно паттерна для построения
систем сильного ИИ. Вопрос в том, какого именно и каким/какими он/они должен/должны быть? И именно поэтому мы считаем необходимым глубже исследовать феномен паттерна в целом, паттерна в программировании, а также разобрать основные
паттерны, непосредственно использующиеся в ООП: уже упомянутые «Абстрактную фабрику» и «Шаблонный метод», а также все остальные.
Вообще же о паттернах можно говорить в весьма обширном
спектре абстракций – от наиболее общих и абстрактных до наиболее частных и конкретных. Имеют место паттерны вселенского масштаба, а также паттерны в рамках некоторой отдельно
взятой предметной области. Дополнительно вводить какую-либо классификацию паттернов по уровню абстракции или по
дисциплинарной принадлежности нам целесообразным не видится, так как в рамках исследования нас все же интересуют
в первую очередь именно ПП ПК в рамках ООП и именно в контексте их значимости для построения систем сильного ИИ.
О чем-то пока только подобном, а именно о ПП, упомянул
Кристофер Александер в 1977 г., причем не только не в контексте ООП, но даже и не в рамках программирования вообще. Он
говорил о проектировании городской архитектуры, и его характеристика, данная паттернам, выглядит так: «…каждый шаблон
(паттерн) дает описание той или иной задачи, регулярно возникающей в окружающем нас пространстве, вслед за которым представлена суть решения данной проблемы, сформулированная
таким образом, чтобы вы могли многократно использовать это
решение, но никогда не копировать его» [118, с. 20]. Стоит заметить, что характеристика «отца-основателя», данная им паттернам, полностью соответствует нашим «наброскам» на данном
этапе исследования. Т. е. просто решение некоторой болееменее схожей задачи в более-менее схожем контексте в некото-
134

ром роде похожим образом. Разительное отличие природы паттерна от природы шаблона и тем более от сущности алгоритма
налицо. Разумеется, что сказанное Александером о паттернах
в контексте городского домостроительства вполне успешно и абсолютно непринужденно теоретически экстраполируется также
и в контекст программирования, как, собственно, и вообще на
любой иной контекст.
Так, само собой, и случилось не только в теоретическом смысле, но также и в плане практическом. В 1994 г. Эрих Гамма, Ричард Хелм, Ральф Джонсон и Джон Влиссидес, следуя принципам выявления, абстрагирования и построения паттернов,
написали книгу под названием Design patterns, что в дословном
переводе означает «Паттерны дизайна» (была переведена как
«Паттерны проектирования»). В книге были теоретически обозначены, подробно описаны и тщательно проиллюстрированы
(на языках программирования C++ и Smalltalk) 23 ПП программного обеспечения. С тех пор книга много раз переиздавалась, также появилось значительное количество дополнительной литературы на тему ПП. Постепенно ввиду чуть большего,
чем стандартное, количества авторов книги к ней «приросло»
устойчиво прозвище Gang of four – «Банда четырех», которое
в свою очередь эволюционировало до аббревиатуры GoF. И эти
23 ПП также стали называться по имени основателей GoF-patterns – «Паттерны банды четырех». И именно данные паттерны
считаются классическими и основополагающими в контексте
ООП. Теперь же, перед непосредственным проникновением
в конкретику ПП в рамках парадигмы ООП, необходимо совершить одно небольшое, но необходимое отступление.
Очень даже возможно, что некоторые подобия ПП, применяемых в рамках ООП, также возможны и в исключительно функциональном или декларативном программировании с существен ной подстройкой под контекст и особенности конкретной
методологии программирования. Попробуем привести пример
экстраполяции паттерна из области ООП в контекст функционального программирования. Например, мы можем определить
функцию, которая должна выводить, в зависимости от передан-
135

ных ей аргументов, некоторый диапазон простых чисел от a до n
включительно. Мы также определяем функцию, которая осуществляет проверку числа на простоту. Т. е. первая функция получает a и n и должна вернуть некоторое множество простых
чисел, вторая определяет, простое ли число, переданное ей в аргументе, и возвращает некоторое булево значение – True или
False. Собственно говоря, первая функция абсолютно не обязательно сама должна высчитывать весь соответствующий диапазон – функционал счетчика передается третьей функции, которая
в качестве аргументов получает верхнюю и нижнюю границы
необходимого диапазона значений в виде переменных a и n,
затем инкрементирует некоторую внутреннюю переменную x,
сверяет ее текущее значение на предмет соответствия числа
простому числу и вносит значение переменной в выходной список, возвращая его в первую функцию. Выходит, что вся работа
осуществляется верно, все функции «чистые» и, что важно, первая и вторая функции никак между собой не контактируют и вовсе не подозревают о существовании друг друга – между ними
отсутствует сильное зацепление, они внутренне сильно связаны
и все их взаимодействие опосредовано третьей функцией. Опять
же, во избежание возможной критики в излишнем усложнении,
это все не выглядит совершенно необходимым и, конечно, весь
указанный функционал мы вполне можем реализовать в рамках
одной-единственной функции за счет использования вложенных
циклов, таким образом нарушив первый из принципов SOLID –
принцип единственной ответственности, сотворив некую Godfunction (аллюзия на метафору God-object из ООП), а также лишив себя возможности повторного использования каждого из
участков логики указанного функционального взаимодействия.
Поэтому приведенный пример можно считать реализацией на
практике, в контексте функционального программирования, GoFпаттерна «Посредник». Разумеется, пример несколько притянут
за уши и с его претензией на соответствие паттерну можно поспорить, так как он далеко не в полной мере отражает суть и ответственности «Посредника» из ООП, но все же в случае экстраполяции смысла из ООП в функциональное программирование
он достаточно корректен.
136

Данный пример показателен в том плане, что позволяет оценить всю мощь абстракции, заключенной в феномене паттерна.
У «Посредника» много вариантов реализации: столкновение двух
астероидов через третий, смех сквозь слезы и так, собственно,
далее – от уровня почти полного отсутствия всякой конкретики
до почти алгоритмического.
Однако GoF-паттерны – это непосредственно ООП, и сочетание абстракции-конкретики на данном уровне именно такое
и никакое не иное, а посему взаимоотношения между абстрактными компонентами в них описываются именно в терминологии ООП – той терминологии, которая была нами озвучена ранее. Итак, все отношения между компонентами в рамках любой
конкретной реализации любого условного паттерна описываются
в контексте взаимодействия между классами и их экземплярами –
объектами (а не как в вышеприведенном примере – функциями),
каждому из которых назначена определенная ответственность.
Выделяют следующие взаимодействия между классами в ООП:
ассоциация, подразделяющаяся на агрегацию и композицию,
а также отдельно – обобщение. Еще иногда отдельно выделяют
зависимость и агрегирование (в качестве синонима делегирования), но это уже далеко не всегда считается каноничным в плане
введения этих типов связей в основные из наличествующих
в ООП. Заранее скажем, что существует достаточно много различных вариантов классификации взаимодействия между ПК
в рамках ООП (к примеру, иногда ассоциацию выделяют как
еще один отдельный тип связи) и мы отобрали наиболее всеохватывающую – в смысловом плане, а не по количеству вариантов. Кратко охарактеризуем классификацию.
Агрегация – способ взаимодействия между ПК (большим
и меньшим, целым и частью, общим и частным и так далее), при
котором одна часть может существовать без другой части. Если
вспомнить наш недавний пример: это про отношение между песком и бетоном.
Композиция – способ взаимодействия между ПК (также общее-частное, часть-целое и так далее), при котором одна часть
не может существовать отдельно от другой части. Вновь про
137

наш пример: бетон – материал композитный, и без своих частей
он существовать не сможет.
Обобщение – обычно отношение наследования (описанного
нами ранее как один из ключевых фундаментальных аспектов
ООП), т. е. такое взаимодействие между ПК, при котором один
обладает всеми характеристиками другого и, возможно, некоторыми собственными, дополненными. Причем один компонент
является «родительским» по отношению к другому, другой, соответственно, «дочерним».
Зависимость – способ взаимодействия между ПК, при котором некоторое изменение в независимом компоненте неизбежно
скажется на зависимом, т. е. с зависимым компонентом произойдут какие-либо (заранее оговоренные) изменения. Порой зависимость есть реализация связи по принципу «один ко многим». И тут можно привести очень хорошую метафору: «один
с сошкой, а семеро с ложкой». Как только что-то меняется у компонента, от которого зависят остальные (от одного до очень
многих), то тут же у всех наступают изменения, которые могут
носить, в случае множественных связей, весьма непредсказуемый характер.
Агрегирование как синоним делегирования – некоторый
антоним понятия Dependency injection, т. е. предоставление
внешних зависимостей ПК в том смысле, что ПК перепоручает
некоторые ответственности иному, отличному от самого себя,
но в то же время именно своему собственному, внутреннему ПК.
Знание и понимание способов взаимодействия между ПК
важно по той причине, что именно на взаимодействии между
различными абстрактными сущностями реализуется концептуальная суть и теоретический смысл паттерна, а затем уже, на
практике, эта суть резюмируется в реальных конкретных программных компонентах и определенных связях между ними.
Здесь стоит еще раз отметить, что мы, в рамках нашего исследования, будем выделять максимально абстрактные составляющие данных связей, пусть даже в некоторый ущерб конкретным реализациям. Мы, исключая некоторые отдельно взятые
случаи, будем подходить с как можно более общих позиций по
той причине, что конкретные реализации всегда есть следствие
138

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

работки лишь в качестве объяснений и иллюстраций, а также
порой опускаем некоторые детали, дабы сосредоточиться непосредственно на цели нашего исследования. Более того, используем философско-методологическое интерпретирование характерных особенностей и ключевых аспектов ООП. И именно
с этих позиций мы намерены подойти к исследованию и описанию GoF-паттернов.
После этого небольшого, но необходимого отступления целесообразно перейти непосредственно к самим GoF-паттернам.
Они по своей сути подразделяются на три группы разного размера и функционала: порождающие (creational), поведенческие
(bihevioral) и структурные (structural).
В каждом из данных паттернов, независимо от их групповой
принадлежности, выделяют четыре неизменных атрибута: имя,
задача, решение, результаты.
Имя. Идентификатор конкретного паттерна, призванный отражать его суть, обозначать область решаемой им проблематики, недвусмысленно указывать на специфику решения и делать
некоторый прогноз возможных результатов, а также, желательно, быть ассоциативно связанным с некоторыми общеупотребительными понятиями.
Задача. Определение области решаемой проблематики: теоретическое обозначение ситуаций применения паттерна и контекста его применения, обозначение уровня абстракции для реализации паттерна.
Решение. Обозначение до некоторого уровня конкретных
ПК, используемых в контексте решения задачи с определенным
уровнем конкретики, идентификаторы этих компонентов, определение связей между компонентами и типов этих связей, определение динамики происходящего в контексте работы с задачей.
Результаты. Вероятные ожидаемые и, насколько это возможно, прогнозируемые итоги применения решения к проблеме.
Как нами уже не раз обозначалось, результаты применения решения, оформленного в виде паттерна, и результаты применения решения, оформленного в виде алгоритма, – совершенно
разные результаты. В случае с алгоритмом они всегда гарантированно одинаковы при одинаковых входных данных, включая
140
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]
