Добавил:
ivanov666
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз:
Предмет:
Файл:Сильный искусственный интеллект и объектно-ориентированное программирование синтез парадигм
.pdf
терфейсом. Также адаптируемый связан с каждым конкретным
адаптером по типу композиции, так как каждый конкретный
адаптер формируется под конкретного адаптируемого, возмож-
но не под одного.
Значимость паттерна в контексте разработки систем
сильного ИИ. Преобразование информации между различными
составляющими системы, осуществляемое с целью приведения
этой информации в тот самый вид, в котором ее будет способен
«усвоить» воспринимающий, настолько необходимо, что утверждение об этом выглядит абсолютно тривиально. Однако это преобразование может быть осуществлено различными способами.
Можно действительно по умолчанию оборудовать одну группу
компонентов для взаимодействия с другой группой, если нам заранее известно, что ни с кем более им взаимодействовать не
придется. Однако в случае с паттерном «Адаптер» сама природа
взаимодействия инкапсулируется в некую отдельную сущность
и априори подразумевается возможность одной группы ПК взаимодействовать с абсолютно любой другой группой компонентов. Нам в контексте разработки систем сильного ИИ подобный
подход представляется наиболее продуктивным, и на это есть
причины. А именно – в самоорганизующейся системе, в которой
сама возможность установления связей подразумевается не ограниченной локально, а, напротив, рассредоточенной по всей системе, динамика установления этих связей и возможные успешные соединения между компонентами выше, чем если бы связи
были ограничены локально. К тому же, объективация самой
природы, самой сути связи с последующим ее воплощением
в отдельной сущности нам представляется не просто соблюдением принципов единственной ответственности и разделения
интерфейса, а даже больше – приближением к утрированному
воплощению принципа инверсии зависимостей, к той самой ситуации, когда все зависимости сводятся к абстракциям.
231

13.2. Структурный паттерн «Прокси»
Идентификатор. Прокси (Proxy).
Классические элементы. Прокси, субъект, реальный субъект.
Назначение. Как указывается в Design patterns, «Прокси»
«является суррогатом другого объекта и контролирует доступ
к нему» [114, с. 203]. Собственно говоря, цитата из Design patterns практически исчерпывающе отражает суть данного паттерна. Однако все же формализуем. «Прокси» представляет собой
ПК, являющийся копией другого, связанного с ним ПК, и способен осуществлять предварительную обработку всех запросов
к этому компоненту.
Проблема. На самом деле проблем, которые решает «Прокси», довольно большое количество, и охватывать их все не представляется нам целесообразным. Поэтому мы выберем один вариант (с некоторыми минимальными оттенками вкрапления
других) из этого множества, а именно – охранник (иногда еще
обозначается как защищающий прокси). К примеру, у нас наличествует некий ПК. У него, соответственно, имеются некоторые
свойства и качества. К этому ПК могут делать некие запросы
другие ПК, которых имеется некоторое количество. Из этого количества половина компонентов может только лишь получать
ответы от нашего компонента. Другая половина тоже может получать ответы, но еще может и устанавливать нашему компоненту некоторые блоки данных в виде пар key – value («ключ –
значение»), делая эти самые блоки его частью. Еще добавим, что
наш ПК довольно-таки тяжелый и большой в плане занимаемой
памяти и постоянное его «дергание» по всем запросам оказывается не вполне оптимальным. Нам необходимо сделать так, чтобы мы могли проверять полномочия каждого ПК, который обращается к нашему компоненту, на предмет того, на что он имеет
право, и только после этого при необходимости переадресовывать запрос нашему компоненту. И это поможет осуществить
структурный паттерн «Прокси».
Концептуальное решение. Опять же ввиду многочисленности
вариантов воплощения, приведем один из вариантов реализации
232

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

и применяется кэширование, т. е. сохранение ответа на некоторый запрос в некоторой промежуточной структуре данных
в виде пар key – value («ключ – значение»). И при наличии в такой структуре данных «ключа», совпадающего с некоторым запросом конкретного пользователя, обращения к основному ПК
не происходит, а просто сразу возвращается ответ из промежуточной структуры данных. Это и есть кэширование.
Гипотетический пример реализации паттерна. В случае
с данным паттерном очень даже корректным примером является
теория о наличии двойников у некоторых высокопоставленных
лиц. Один человек, прокси, замещает собой другого, реального
человека, для того чтобы к реальному человеку пропускались
только те запросы (в общем смысле слова), которые он не против
удовлетворить (также в общем смысле). Единственное замечание: в данном примере в роли субъекта находится реальный
субъект, а в роли реального субъекта – также он сам. В остальном пример почти идентичен тому, как реализуется паттерн.
Осмысление структуры паттерна. ПК, представляющие
собой прокси и реального субъекта, связаны с компонентом
субъекта по типу наследования, а также прокси связан с реальным субъектом по типу композиции, так как прокси не сможет
существовать без реального субъекта.
Значимость паттерна в контексте разработки систем
сильного ИИ. Если несколько отвлечься от сугубо утилитарной
значимости данного паттерна и сразу выйти на несколько абстрактный уровень, то центральной абстракцией всего паттерна
«Прокси» окажется реализация кажущегося замещения или,
проще говоря, притворства. Может показаться, что это не особенно значимо в контексте разработки систем сильного ИИ. Однако есть, как водится, некоторый нюанс. К примеру, человек
одинаково себя ведет, если в темной комнате заметит змею или
если в этой же комнате заметит веревку, которая покажется ему
змеей. Т. е., по всей видимости, реальное положение дел не всегда адекватно отражается внутренним миром. Можно попробовать продолжить эту идею тем, что также для человека далеко
не всегда важно то, кто он есть, а важно то, кем он себя считает
234

и кем притворяется. Также в произведении «Государство» у Платона указано притворство в качестве одного из основных средств
воспитания [120]. У Йохана Хёйзинги в трактате «Человек играющий» было указано, что все сложные формы человеческой деятельности изначально осваивались в игровой форме [94]. Причем
человеческое притворство – это довольно сложный с психической точки зрения процесс, подразумевающий наличие самосознания и высокую лабильность Я-концепции. Т. е. интеллектуальная система без наличия техноропного сознания не будет
способна на реализацию столь сложных феноменов. Однако
можно пойти как раз от обратного и попытаться приблизить вероятность возникновения самого технотропного сознания при
помощи реализации феноменов его уровня. И на наш взгляд
именно абстракция, лежащая в основе данного паттерна, выглядит наиболее перспективно для реализации этой концепции.
Собственно говоря, это именно то, что и пытаются сделать
в рамках разработки систем слабого ИИ: пытаются сформировать некий суррогат какого-либо человеческого аспекта для
того, чтобы «остальной человек» сформировался сам. Т. е. принцип, лежащий в основе, подобран верно, однако его реализация – нет. Для того чтобы феномен такого рода смог привести
к качественным преобразованиям в системе, опыт реализации
данного феномена должен быть затем органично интегрирован
в общий опыт системы и «осознан» ею. А подобное возможно
только в контексте систем, способных к самоорганизации, которые мы и предлагаем формировать для дальнейшего продвижения на пути к сильному ИИ.
13.3. Структурный паттерн «Мост»
Идентификатор. Мост (Bridge).
Классические элементы. Абстракция, конкретная абстрак-
ция, реализатор, конкретный реализатор.
Назначение. В Design patterns указано, что «Мост» «связывает абстракцию с ее, возможно, многочисленными реализациями. Данный паттерн предоставляет клиентам стабильный ин-
235

терфейс, позволяя в то же время изменять классы, которые его
реализуют. Мост также подстраивается под новые реализации,
появляющиеся в процессе развития системы» [114, с. 214]. Данный паттерн предназначен для того, чтобы в рамках какой-либо
системы все включенные в нее ПК были иерархически разделены на две большие (не обязательно равные) группы, причем таким образом, чтобы компоненты одной группы могли трансформироваться вне зависимости от состояния компонентов другой
группы (как и наоборот), но в то же время чтобы компоненты
обеих групп могли эффективно взаимодействовать для решения
общих задач. Также можно определить, что паттерн «Мост»
предназначен для разделения сложного, динамически трансформирующегося функционала в рамках системы на два различных
функционала: высокоуровневый и низкоуровневый. Данные функционалы способны как трансформировать свое состояние вне
зависимости от состояния другого, так и успешно решать совместные задачи.
Проблема. Представим себе некую систему, в рамках которой наличествует определенное количество ПК. Компоненты
подразделяются на некоторые группы, объединенные общими
ответственностями. И если, в данном конкретном случае, посмотреть на такую систему, то мы можем обнаружить, что она,
по большому счету, выполняет обязанности, подразделяемые на
две большие группы. К примеру, одна из этих групп обязанностей сводится к получению извне некой информации, а другая
же группа ее преобразует и хранит. Дополним также, что в рамках данной системы специфика самой информации зависит от
способа ее получения, а также от способа обработки и хранения.
С другой стороны, программным компонентам первой группы
нет дела до того, каким именно образом хранится и обрабатывается информация, а компонентам второй группы нет дела до
того, как она получена. Однако специфика информации заставляет подстраиваться всю систему. К примеру, если попробовать
добавить некую обязанность к первой группе, скажем добавить
новый способ получения информации, то вынуждена будет трансформироваться вся система в целом, так как ПК второй группы
236

будут вынуждены определять для себя новую функциональность, которая заключается в преобразовании и хранении информации, полученной неизвестным ранее способом. Точно так
же, если добавится новый способ хранения или преобразования
информации, то компонентам первой группы придется определять для себя способ ее получения вне зависимости от того, будут они ее получать или нет. В общем случае получается, что
система чересчур связана между собой, нарушая тем самым
фундаментальный принцип слабого зацепления, также в такую
систему крайне тяжело вносить изменения, так как они слишком быстро накапливаются, делая систему неудобной в плане
поддержки. Для интенсификации наглядности можно просто
представить, что все ПК в такого рода системе наследуются от
одного и того же базового компонента и все изменения, необходимые какому-либо из подклассов, вносятся сразу в базовый
класс («на всякий случай»).
Концептуальное решение. Мы могли бы разделить систему
вышеприведенного типа на две структурно обособленные функционально различающиеся части. Часть первую – ответственную
за получение информации откуда-то извне, а соответственно,
и за UI (User Interface – пользовательский интерфейс) – мы обозначим как абстракцию, а часть вторую – как реализатор. ПК,
относящиеся к первой части, мы обозначим как конкретные аб-
стракции, а относящиеся ко второй части – конкретные реализаторы. Абстракция содержит в себе ссылку на самого реализатора. Этим их связи пока и ограничиваются (мост, как суть
паттерна). Теперь при получении какой-либо информации абстракция просто вызывает метод реализатора, который затем
передает запрос конкретным реализаторам, подходящим под
актуальную ситуацию. Конкретные реализаторы на месте осуществляют преобразование и хранение информации. Если мы
теперь захотим добавить какой-либо новый способ получения
информации, то мы изменим только либо какие-то конкретные
абстракции, либо саму абстракцию, и больше изменений в систему вносить не нужно. Если же мы решим добавить некий новый способ преобразования и хранения информации, то нам
237

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

зан к контексту разработки программного обеспечения, то его,
соответственно, необходимо существенно абстрагировать. Сама
идея разделения абстракции и конкретики лежит в основе как
нашего подхода к разработке систем сильного ИИ, так и актуального исследования. Так что будет явным преуменьшением
постулировать, что данная идея нам близка. Более того, нам
представляется, что принцип взаимно независимой трансформации абстрактного и конкретного не отражает всей полноты картины. Нам представляется непротиворечивой возможность, при
которой, если приводить в пример вышеописанную ситуацию
с надвое разделенной системой с множеством ПК, то, согласно
нашему предположению, к феномену независимой трансформации должны быть способны вообще все ПК системы. И в данном случае не представляется существенным, ответственность
какого рода лежит на этих компонентах – абстрактная или конкретная. Другое дело, сколько данных изменений будут затем
органично встроено в новый контекст системы, а скольким будет уготовано закончиться без принятия в контекст.
Это несколько напоминает первый этап методологии автоматизации развертывания программных проектов – CI/CD (continuous integration / continuous delivery / continuous deployment, т. е.
непрерывная интеграция / непрерывная доставка / непрерывное
развертывание). В рамках данной методологии происходит приблизительно следующее. Разработчики относительно независимо друг от друга вносят в целостную систему некоторые изменения. Разумеется, что каждый из них старается, чтобы с теми
изменениями, которые внес в систему именно он, никаких проблем не возникло, и они были органично интегрированы. Т. е.
никто специально не будет делать так, чтобы быть отвергнутым
контекстом. Далее все изменения, внесенные в систему, накапливаются в центральном узле – репозитории. После этого начинается тестирование внесенных изменений. И если где-либо
обнаруживается какая-то проблема, то затем ее, при помощи
регрессионных тестов, будут искать, эмулировать и затем устранять. Примерно тот же процесс видится нам наиболее естественным и целесообразным при самоорганизации сложной интел-
239

лектуальной системы. Т. е., в соответствии с нашим подходом
к формированию систем сильного ИИ, к независимой и автономной трансформации должны быть способны не только абстракция и реализация, а все ПК системы без исключения.
13.4. Структурный паттерн «Компоновщик»
Идентификатор. Компоновщик (Composite).
Классические элементы. Компонент, лист, узел.
Назначение. В Design patterns указано, что предназначение
«Компоновщика» сводится к тому, что он «должен так структурировать классы, чтобы различные взаимосвязанные объекты
удавалось трактовать единообразно, а несколько объектов рассматривать как один» [114, с. 214]. Таким образом, данный паттерн применяется в качестве конструктора для того, чтобы сформировать структуру данных, внешне напоминающую дерево,
для тех ПК, которые реализовывают схожее поведение как будучи в одиночестве, так и находясь в составе группы других компонентов. Т. е. паттерн облегчает работу с некой системой за
счет объединения и унификации всех компонентов данной системы.
Проблема. Можно представить себе программную систему,
состоящую из некоторого количества частей, т. е. компонентов.
Компоненты, принадлежащие данной системе, реализуют схожее поведение, но являются в некотором роде разрозненными
и разобщенными, можно сказать «разбросанными». Причем
этих самых компонентов очень много и они могут различаться
по размеру. Далее нам необходимо каким-то образом взаимодействовать с этими компонентами, узнавать о них некоторую информацию, считать их количество, перемещать их из одного
места в другое и т. д. Т. е., допустим, нам в рамках системы необходимо переместить некоторое количество компонентов из
точки А в точку В. И мы направляемся в точку А, берем один
компонент и перемещаем его в точку В. Затем идем обратно, берем следующий компонент и его тоже перемещаем в точку В.
И так далее, возможно очень много раз. Само собой разумеется,
240
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]
