Причина упрощения интерфейса - в относительной автономности знаний: мы указываем на интересующий объект информационной доски и даем источнику команду применить его правила, учитывая глобальное состояние доски. При выполнении правил каждый из источников знаний может осуществлять следующие действия:
• Высказать предположение о подстановке.
• Найти противоречие в ранее предложенных подстановках и откатить их.
• Высказать утверждение о подстановке.
• Сообщить контроллеру о своем желании записать на доску что-то интересное.
Все эти действия являются общими для всех источников знаний. Перечисленные операции образуют механизм вывода заключений. Определим механизм вывода (InferenceEngine) как объект, который выполняет известные правила для того, чтобы либо найти новые правила (прямая последовательность рассуждений), либо доказать некоторую гипотезу (обратная последовательность рассуждений). На основании сказанного введем следующий класс:
class InferenceEngine {public:
InferenceEngine(
...};
Конструктор класса создает экземпляр объекта и населяет его правилами. Лишь одна операция сделана в этом классе видимой для источников знании:
• evaluate - Выполнить правило механизма вывода.
Теперь о том, как сотрудничают источники знаний: каждый специализированный источник определяет свои собственные правила и возлагает ответственность за их выполнение на класс InferenceEngine. Точнее, операция KnowledgeSource::evaluate вызывает метод InferenceEngine::evaluate, что приводит к выполнению одной из четырех упомянутых выше операций. На рис. 11-4 показан сценарий такого взаимодействия:
Что такое правило? Для иллюстрации приведем (в формате Lisp) правило, касающееся знаний об общеупотребительных суффиксах:
((* I ? ?)(* I N G)(* I E S)(* I E D))
Это правило означает, что заданному шаблону *I?? (условие - antecedent) могут соответствовать суффиксы ING, IES и IED (заключение - consequent). В C++ можно определить следующий класс для представления правил:
class Rule {public:...
int bind(String
protected:
String
};
Смысл приведенных операций полностью понятен из их наименований. Мы здесь повторно использовали некоторые классы из главы 9.
С точки зрения строения данного класса можно утверждать, что источники знаний являются разновидностью механизма вывода. Кроме того, они ассоциированы с объектами доски, поскольку находят там приложение своим усилиям. Наконец, каждый источник знаний связан с контроллером и посылает ему свои соображения. Контроллер, в свою очередь, может активизировать источники знаний.
Выразим все сказанное следующим образом:
class KnowledgeSource : public InferenceEngine, public Dependent {public:
KnowledgeSource(Blackboard*, Controller*);void reset();void evaluate();
protected:
Blackboard* blackboard;Controller* controller;UnboundedOrderedCollection
};
В этот класс введен защищенный элемент данных pastAssumptions, позволяющий сохранять всю историю предположений в целях самообучения.
Экземпляры класса Blackboard служат для хранения объектов информационной доски. По схожим соображениям, необходим также класс KnowledgeSources, охватывающий все источники знаний, относящиеся к решаемой задаче:
class KnowledgeSources : public DynamicCollection
Одно из свойств этого класса состоит в том, что при создании его экземпляра создаются также 13 специализированных источников знаний. Для объектов этого класса определяются три операции:
• restart - Перезапустить источник знаний.
• StartKnowledgeSource - Задать начальные условия для источника знаний.
• connect - Связать источник знаний с доской или контроллером.
На рис. 11-5 показана структура созданных в процессе проектирования классов источников знаний.
Проектирование контроллера