    Глава 23 

    Пример объектно-ориентированной системы 

    23.1. Введение 

    В предыдущей главе были представлены базовые концепции 
объектно-ориентированного подхода, В этой главе на простом примере 
последовательно демонстрируется взаимодействие этих идей, а именно: как 
определяется объектно-ориентированная база данных, как она пополняется 
данными, как в ней выполняются операции извлечения и обновления данных. 
Рассматриваемый пример основан на хорошо известных объектно-ориентированных 
системах— системе GemStone (предлагаемой корпорацией Servio Corporation 
[23.2]) и языке представления данных OPAL. Замечание. На язык OPAL, в свою 
очередь, в значительной мере оказал влияние язык Smalltalk-80 (см. [22.6, 23.1]). 
    В этой главе также представлены операции определения данных, операции 
управления данными, а в конце главы приведено резюме и несколько 
заключительных замечаний. 

    Пример 

    Этот пример основан на упрощенной версии образовательной базы данных из 
упражнений главы 5. 
    •  Образовательная база данных содержит информацию о схеме подготовки и 
обучения специалистов внутри некоторой компании. Для каждого курса обучения 
(COURSE) содержится подробное описание предлагаемых для обучения дисциплин 
(OFFERING). Для всех дисциплин хранятся данные обо всех учениках (ENROLLMENT) 
и преподавателях (TEACHER). Кроме того, в этой базе данных содержатся сведения 
о сотрудниках компании. 
    Реляционную версию такой базы данных можно представить следующим образом: 

    EMPLOYEE ( ЕМР#, ENAME, JOE ) PRIMARY KEY ( EMP* ) 
    COURSE ( COURSE*, TITLE ) 
    PRIMARY KEY ( COURSE* ) 
    OFFERING ( COURSE*, OFF*, ODATE, LOCATION ) PRIMARY KEY ( COURSE*, OFF* ) 
FOREIGN KEY ( COURSE* ) REFERENCES COURSE 
    ENROLLMENT ( COURSE*, OFF*, EMP#, GRADE ) PRIMARY KEY ( COURSE*, OFF*, 
EMP* ) FOREIGN KEY ( COURSE*, OFF* ) REFERENCES COURSE FOREIGN KEY ( EMP* ) 
REFERENCES EMPLOYEE 
    TEACHER ( COURSE*, OFF*, EMP* ) 
    PRIMARY KEY ( COURSE*, OFF#, EMP* ) 
    FOREIGN KEY ( COURSE*, OFF* ) REFERENCES OFFERING 
    FOREIGN KEY ( EMP* ) REFERENCES EMPLOYEE 

    На рис. 23.1 показана диаграмма связей для этой базы данных. Более 
подробно она описана в главе 5, в упражнениях и ответах к упражнениям. 

    Глава 23, Пример объектно-ориентированной системы	619 


    Рис. 23.1, Диаграмма связей для рассматриваемого примера образовательной 
базы данных 


    23.2. Определение данных 

    Теперь следует перейти к описанию определения данных на языке OPAL для 
образовательной базы данных. Ниже приводится первое определение объектного 
класса сотрудников EMPLOYEE (для удобства строки этого определения пронумерованы). 

    1	OBJECT SUBCLASS : 'EMPLOYEE1 
    2	INSTVARNAMES : # [ '?№#', 'ENAME', 'JOB' ] 
    3	CONSTRAINTS : # [ #  f # EMP#, STRING ] , 
    4	[ # ENAME, STRING ], 
    5	[   #  JOB,   STRING  ]   ]    . 

    Пояснения: 
    •  В строке 1 определен объектный класс EMPLOYEE, производный от 
встроенного класса OBJECT. (Согласно терминологии языка OPAL в строке 1 
посылается сообщение объекту OBJECT с запросом выполнить метод SUBCLASS, 
причем с помощью директив INSTVARNAMES и CONSTRAINTS задаются параметры этого 
метода. Для определения нового класса (как и всего прочего) необходимо послать 
сообщение объекту,) 
    •  В строке 2 указывается, что объекты класса EMPLOYEE имеют три 
общедоступных переменных — ЕМР#, ENAME и JOB. 
    • В строке 3 на переменную ЕМР# накладывается ограничение: она может 
содержать объекты класса STRING (в строках 4 и 5 указано аналогичное ограничение). 
    Замечание. В этой главе опускаются чисто синтаксические подробности, не 
существенные для преследуемых в этой главе целей (например, вездесущие знаки 
"#", как в приведенном выше примере). 
    Определение класса курса COURSE: 

    OBJECT SUBCLASS : 'COURSE' 
    INSTVARNAMES : # [ 'COURSE*', 'TITLE', 'OFFERINGS' CONSTRAINTS : # ( #  [ 
# COURSE#, STRING ] , [ # TITLE, STRING ], [ # OFFERINGS, OSET] ] , 

    Пояснения: 
    В строке 5 определена переменная OFFERINGS, которая содержит объект класса 
OSET или, точнее, идентификатор объекта класса OSET.1 Неформально выражаясь, 
переменная OFFERINGS обозначает множество всех дисциплин данного 

    1 Гипотетический синтаксис, введенный в главе 22, позволяет выразить это 
более явно с помощью определения переменной OFFERINGS как REF(SET(REF(OFFERING))), 

    620 Часть VI. Объектно-ориентированные системы 

курса. Иначе говоря, связь курс-дисциплины моделируется с помощью иерархии 
контейнеров, в которой дисциплины концептуально содержатся внутри 
соответствующего курса. Определение класса OSET будет дано ниже. 
    Определение класса дисциплин OFFERING: 
    1      OBJECT SUBCLASS : 'OFFERING' 
    2	INSTVARNAMES : # [ 'OFF#', 'ODATE', 'LOCATION1, 
    3	'ENROLLMENTS', 'TEACHERS' ] 
    4	CONSTRAINTS : # [ #  [ # OFF#, STRING ] , 
    5	[ # ODATE, DATETIME ], 
    6	[ # LOCATION, STRING ], 
    7	[ # ENROLLMENTS, NSET ], 
    8	[ # TEACHERS, TSET ] ] . 
    Пояснения: 
    •  В строке 7 задана переменная ENROLLMENTS, которая содержит объект 
класса NSET (точнее, идентификатор этого объекта). Неформально выражаясь, 
класс NSET обозначает множество всех дисциплин данного курса. Аналогично, 
класс TSET обозначает множество всех преподавателей для данной дисциплины. 
Следовательно, здесь вновь принимается представление "иерархии контейнеров". 
Определения классов NSET и TSET будут даны ниже. 
    •  Обратите внимание, что внутри объектов класса OFFERING не содержатся 
переменные "внешнего ключа", которые ссылаются на объекты класса COURSE. Это 
решение согласуется с намерением использовать иерархию контейнеров. Однако это 
означает, что не существует прямого способа получить из данного экземпляра 
объекта OFFERING соответствующий экземпляр объекта COURSE. Подробнее этот 
вопрос рассматривается в следующей главе. 
    Определение класса учеников (студентов) ENROLLMENT: 
    1	OBJECT SUBCLASS : 'ENROLLMENT' 
    2	INSTVARNAMES : # [ 'EMP1, 'GRADE' ] 
    3	CONSTRAINTS  :#[#[# EMP, EMPLOYEE ] , 
    4	[ # GRADE, STRING ] ] . 
    Пояснения'. 
    •  Вновь объект класса ENROLLMENT не содержит переменных "внешнего ключа", 
которые ссылаются на объекты класса OFFERING. Однако следует обратить 
внимание, что объекты класса ENROLLMENT содержат некое подобие внешнего ключа. 
Действительно, переменная ЕМР (строка 3) содержит идентификатор объекта класса 
EMPLOYEE, представляющий отдельного сотрудника, который является учеником. 

    Замечание. Данная интерпретация "иерархии контейнеров" заключается в том, 
что объект EMPLOYEE действительно содержится внутри объекта ENROLLMENT. Однако 
здесь можно заметить асимметрию: зачисление сотрудников на обучение разным 
дисциплинам можно рассматривать как отношение типа многие-ко-многим. 

    Теперь рассмотрим объекты преподавателей. В интересах данного примера 
следует слегка отвлечься от исходной реляционной версии базы данных и 
рассмотреть преподавателей (TEACHER) как подкласс сотрудников (EMPLOYEE). 
    1	EMPLOYEE SUBCLASS : 'TEACHER' 
    2	INSTVARNAMES : # [ 'COURSES' ] 
    3	CONSTRAINTS  :#[#[# COURSES, CSET ] ]. 
    Пояснения'. 
    •  В строке 1 определен класс TEACHER, который является подклассом ранее 
определенного класса EMPLOYEE (иначе говоря, TEACHER "ISA" EMPLOYEE). Таким 
образом, каждый отдельный объект TEACHER имеет переменные ЕМР#, ENAME и JOB 
(которые унаследованы от класса EMPLOYEE), а также переменную COURSES, которая 
содержит идентификатор объекта класса CSET. Этот объект CSET обозначает 
множество всех курсов, которые ведет данный преподаватель. 

    Глава 23. Пример объектно-ориентированной системы	621 

    Как было показано в главе 22, в объектно-ориентированных системах четко 
различаются понятия классов и семейств (коллекций). Действительно, в 
предыдущих определениях классов предполагалось существование нескольких 
семейств объектов (например, семейств дисциплин, учеников, преподавателей и 
т.д.), но они не были определены. Теперь следует дать точное определение 
семейства каждого класса, а именно: ESET, CSET, OSET, NSET и TSET. Например, 
объект класса CSET будет состоять из множества идентификаторов отдельных 
объектов класса COURSE. Ниже даются определения всех этих классов, начиная с 
класса ESET. 
    1      SET SUBCLASS      : 'ESET' 
    2	CONSTRAINTS : EMPLOYEE . 
    Пояснения: 
    •  В строке 1 дается определение класса ESET, который является подклассом 
встроенного класса множеств SET. 
    •  В строке 2 на объекты класса ESET накладывается ограничение: они должны 
быть множествами объектов (идентификаторами объектов) класса EMPLOYEE. В 
общем, может быть произвольное число объектов класса ESET, но в данном случае 
будет создан только один, который будет содержать множество идентификаторов 
всех объектов класса EMPLOYEE (его можно неформально назвать "множеством всех 
сотрудников"). Выражаясь неформально, объект ESET будет 
объектно-ориентированным аналогом базового отношения EMPLOYEE в реляционной 
версии базы данных. 
    •  Обратите внимание, что объекты класса ESET не имеют общедоступных переменных. 
    Определения классов CSET, OSET, NSET и TSET аналогичны (они приводятся 
ниже). Однако для каждого из них придется создать не один, а несколько 
объектов соответствующего класса. Например, в таком случае будет столько же 
объектов OSET, сколько существует отдельных объектов COURSE. Более подробно 
создание этих "множеств" объектов описано ниже. 

    SET SUBCLASS 
    CONSTRAINTS 
    SET SUBCLASS 
    CONSTRAINTS 
    SET SUBCLASS 
    CONSTRAINTS 
    SET SUBCLASS 
    CONSTRAINTS 
    'CSET' COURSE 
    'OSET' OFFERING 
    'NSET' ENROLLMENT 
    TEACHER 

    В заключение, приведем рис. 23.2, на котором для удобства собраны все 
описанные выше определения. Этот рисунок служит основой для описания всех 
примеров данной главы. 

    23.3. Содержание базы данных 

    Теперь рассмотрим данные, которые содержит рассматриваемая база данных. 
При этом остановимся на пяти основных типах объектов (сотрудники, курсы, 
дисциплины, ученики, преподаватели). 

    Сотрудники 

    Следует напомнить о намерении собрать вместе идентификаторы всех объектов 
EMPLOYEE в объекте ESET. Таким образом, прежде всего необходимо создать объект ESET. 

    DID OF SET OF ALL EMPS := ESET NEW . 

    622	Часть VI. Объектно-ориентированные системы 

    SET SUBCLASS 
    CONSTRAINTS 
    SET SUBCLASS 
    CONSTRAINTS 
    SET SUBCLASS 
    CONSTRAINTS 
    SET SUBCLASS 
    CONSTRAINTS 
    SET SUBCLASS 
    CONSTRAINTS 
    OBJECT SUBCLASS 
    INSTVARNAMES CONSTRAINTS 
    OBJECT SUBCLASS 
    INSTVARNAMES CONSTRAINTS 
    OBJECT SUBCLASS 
    INSTVARNAMES 
    CONSTRAINTS 
    OBJECT SUBCLASS 
    INSTVARNAMES CONSTRAINTS 
    EMPLOYEE SUBCLASS 
    INSTVARNAMES CONSTRAINTS 
    'ESET' EMPLOYEE. 
    1CSET' COURSE. 
    'OSET' OFFERING. 
    'NSET' ENROLLMENT. 
    'TSET1 TEACHER. 
    'EMPLOYEE' 
    # [ 'EMP#', 'ENAME', 'JOB1 ] 
    # [ # [ # EMP#, STRING ] , [ # ENAME, STRING ], [ t JOB, STRING] ] . 
    : 'COURSE' 
    : ft [ ' COURSE*', ' TITLE', ' OFFERINGS' :#[#[# COURSE*, STRING ] , [ # 
TITLE, STRING ], [ ft OFFERINGS, OSET ] ] . 
    : 'OFFERING' 
    : # [ 'OFF*', 'ODATE', 'LOCATION', 
    'ENROLLMENTS', 'TEACHERS' ] 
    :#[#[# OFF#, STRING ] , 
    [ # ODATE, DATETIME ], [ # LOCATION, STRING ], t # ENROLLMENTS, NSET ], t 
# TEACHERS, TSET ] ] . 
    'ENROLLMENT' 
    # [ 'EMP', 'GRADE' ] 
    # [ # t t EMP, EMPLOYEE ] , [ # GRADE, STRING ] ] . 
    'TEACHER' 
    # [ 'COURSES' ] 
    # [ t [ # COURSES, CSET ] ]. 

    Рис, 23.2, Определение данных для образовательной базы данных 
(объектно-ориентированная версия) 

    Пояснение: 
    •   Правая часть этого выражения возвращает объектный идентификатор (OID) 
нового пустого экземпляра класса ESET (т.е. пустое множество идентификаторов 
объектов класса EMPLOYEE), а затем идентификатор этого нового экземпляра 
присваивается переменной OID_OF_SET_OF_ALL_EMPS. Неформально говоря, эта 
переменная используется для идентификации "множества всех сотрудников". 
    Теперь всякий раз при создании нового объекта класса EMPLOYEE 
идентификатор этого объекта следует вставить в объект ESET, идентификатор 
которого хранится в переменной OID OF_SET_OF_ALL_EMPS. Для создания такого 
объекта класса EMPLOYEE и вставки его идентификатора в объект класса ESET 
необходимо определить следующий метод: 

    /* анонимный! */ /* параметры */ 
    1 METHOD : ESET 
    2	ADD__EMP# : ЕМР# _PARM 
    3	ADD_ENAME : ENAME_PARM 
    4	ADD_JOB : JOB_PARM 
    5	| EMPJDID | 
    6	EMPJDID := EMPLOYEE NEW . 
    7	EMPJDID SET_EMP# : EMP#_PARM , 
    8	SET ENAME : ENAME FARM 
    /* локальная переменная /* новый сотрудник. */ /* инициализация */ 
    

    Глава 23. Пример объектно-ориентированной системы 623 

    9	SET_JOB   :   JOB_PARM  . 
    10	SELF ADD :  EMPJDID  .                            /* вставка */ 
    11 % 

    Пояснения'. 
    •  В строке 1 начинается код данного метода (который завершается знаком 
"%" в строке 11) для объектов класса ESET. (На самом деле всего один объект 
этого класса будет существовать в системе во время исполнения программы.) 
    •  В строках 2-4 определены три параметра с внешними именами ADD_EMP#, 
ADD_ENAME и ADD_JOB. Эти имена будут использованы в сообщениях, которые 
вызывают данный метод. Соответствующие внутренние имена EMP#_PARM, ENAME_PARM 
и JOB_PARM будут использоваться только внутри этого метода. 
    •  В строке 5 определена локальная переменная EMP_OID, а в строке 6 ей 
присваивается идентификатор нового неинициализированного экземпляра объекта 
класса EMPLOYEE. 
    •  В строках 7-9 посылается сообщение новому объекту класса EMPLOYEE с 
указанием трех методов (SET_EMP#, SET_ENAME и SET_JOB) и передачей одного 
параметра каждому из них (EMP#_PARM для SET_EMP#, ENAME_PARM для SET_ENAME и 
JOB_PARM для SET_JOB). Замечание. Здесь предполагается, что SET_EMP# является 
методом, который используется для объекта класса EMPLOYEE и предназначен для 
присвоения некоторого значения переменной ЕМР#. Аналогично определяются методы 
SET_ENAME и SET_JOB. Несколько подробнее эти методы рассматриваются ниже. 
    •  В строке 10 посылается сообщение объекту SELF; он является особой 
переменной, представляющей объект, для которого определен этот метод в текущий 
момент (т.е. сообщение посылается текущему объекту-получателю или 
объекту-цели). Сообщение вызывает встроенный метод ADD (этот метод содержится 
в каждом определяющем "семейство" классе). В результате идентификатор объекта, 
который содержится в локальной переменной EMP_O1D, будет вставлен в объект, 
идентифицируемый значением переменной SELF. Замечание. Переменная SELF 
необходима потому, что параметр, соответствующий объекту-получателю остается 
не названным. 
    •  Обратите внимание, что, как отмечено в комментарии к строке 1, 
определенный здесь метод остается анонимным. В общем случае в языке OPAL 
методы не имеют имен, а вместо этого для них используется сигнатура (т.е. 
комбинация имени класса, в котором он применяется, и внешних имен их 
параметров). Однако в таком случае возникает опасность создания неуклюжих и 
огромных выражений. Можно отметить и еще один недостаток: если два метода 
одного и того же класса используют одни и те же параметры, то для этих методов 
должны использоваться разные внешние параметры. 
    Теперь новый метод, созданный для вставки в базу данных новых объектов 
класса EMPLOYEE, можно применить следующим образом: 

    OID_OF_SET_OF_ALL_EMPS ADD_EMP#	:	'E0091 
    ADD_E№ME	:	'Watt1 
    ADD_JOB	:	'Janitor'   . 

    При использовании этого выражения будет создан объект класса EMPLOYEE для 
сотрудника Е009, а идентификатор этого объекта будет добавлен к множеству всех 
идентификаторов объектов класса EMPLOYEE. 
    Обратите внимание, что встроенный метод NEW никогда не должен 
использоваться для класса EMPLOYEE, за исключением случаев, когда он является 
частью только что определенного метода. В противном случае могут быть созданы 
некоторые лишние объекты класса EMPLOYEE, которые не будут представлены в 
"множестве всех сотрудников". Замечание. Следует принести извинения за излишне 

    624	Часть VI. Объектно-ориентированные системы 

частое повторение таких выражений, как "множества всех сотрудников и 
только что определенный метод", однако при работе с анонимными объектами иначе 
поступить нельзя. 
    В заключение следует добавить несколько слов относительно методов 
SET_EMP#, SET_ENAME и SET_JOB. Как объяснялось в главе 22, в истинно 
объектно-ориентированной системе экземпляры переменных скрыты от пользователя 
и управление объектами осуществляется только с помощью методов (инкапсуляция), 
В соответствии с этим принципом в данной главе принято соглашение о том, что 
переменные являются скрытыми. Если в определении класса присутствует 
"общедоступная" переменная v, то это определение следует рассматривать как 
сокращенную форму определения двух методов, GET_v и SET v, где GET_v 
возвращает вызывающему объекту значение v, a SET_v присваивает v некоторое 
значение, заданное вызывающим объектом. 

    Курсы 

    Объекты для сотрудников представляют собой наиболее простой случай, 
поскольку они соответствуют "регулярным объектам" (согласно терминологии 
модели типа объект/отношение) и не содержат никаких других объектов. Теперь 
следует рассмотреть более сложный случай объектов для курсов, которые 
(несмотря на то что они все еще остаются "регулярными объектами") 
концептуально содержат некоторые другие объекты. В целом создание объектов для 
курсов требует выполнения следующих действий: 
    1.  Применение метода NEW для класса CSET для создания исходно пустого 
"множества всех курсов" (на самом деле множества идентификаторов объектов 
класса COURSE). 
    2.  Определение метода для создания нового объекта класса COURSE и вставки 
его идентификатора в "множество всех курсов". При создании этого объекта 
потребуется задать некоторые значения для параметров COURSE# и TITLE. Кроме 
того, с помощью метода NEW класса OSET потребуется также создать исходно 
пустое множество дисциплин (на самом деле идентификаторов объектов класса 
OFFERING), a затем разместить идентификатор этого пустого множества дисциплин 
в переменной OFERINGS внутри нового объекта класса COURSE. 
    3.   Активизация только что определенного метода для каждого отдельного курса. 

    Дисциплины 

    Чтобы создать объекты для дисциплин, необходимо выполнить следующие действия: 
    1.  Определить метод для создания нового объекта класса OFFERING. Этот 
метод принимает в качестве параметров значения переменных OFF#, ODATE и 
LOCATION и приводит к созданию нового объекта OFFERING с этими параметрами. 
Кроме того, потребуется также выполнить некоторые дополнительные действия. 
    • Для создания исходно пустого множества учеников (идентификаторов 
объектов ENROLLMENT) следует применить метод NEW для класса NSET, а затем 
разместить идентификатор этого пустого множества учеников в переменной 
ENROLLMENTS внутри нового объекта OFFERING. 
    • Для создания исходно пустого множества преподавателей (идентификаторов 
объектов TEACHER) следует применить метод NEW для класса NSET, а затем 
разместить идентификатор этого пустого множества учеников в переменной 
TEACHERS внутри нового объекта OFFERING. 
    2.  Этот метод принимает в качестве параметра значение переменной COURSES 
и использует его следующим образом: 
    • Сначала с помощью этого параметра требуется найти объект COURSE, 
соответствующий новому объекту OFFERING. Как это можно выполнить, описано 

    Глава 23. Пример объектно-ориентированной системы	625 

ниже. Замечание. Конечно, этот метод не позволит создать объект для новой 
дисциплины, если не будет найден соответствующий ему курс обучения. Далее в 
этой главе такие исключительные случаи рассматриваться не будут. 
    • После этого следует найти "множество всех дисциплин" для этого объекта 
класса COURSE. 
    • Затем нужно добавить идентификатор нового объекта класса OFFERING к 
соответствующему "множеству всех дисциплин". 
    Заметьте, что (как уже упоминалось в главе 22) здесь нельзя избежать 
использования таких пользовательских ключей, как COURSES. Действительно, такие 
ключи необходимы не только для отображения объектов окружающего мира, но и для 
организации подстановок справочных данных внутри базы данных. 
    3.   Наконец, следует вызвать только что определенный метод для каждой 
отдельной дисциплины. 
    Обратите внимание, что (в соответствии с используемым представлением 
иерархии контейнеров) здесь не было создано "множество всех дисциплин". В 
результате для выполнения запроса, в котором данное семейство используется в 
качестве диапазона, например для запроса "Найти все дисциплины, преподаваемые 
в Нью-Йорке", потребуется дополнительно создать некоторую процедуру. 

    Ученики 

    Различие между способами создания объектов для учеников и дисциплин 
заключается в том, что объекты ENROLLMENT содержат переменную класса EMPLOYEE, 
a именно переменную ЕМР, значение которой является идентификатором 
соответствующего объекта EMPLOYEE. Поэтому необходимая последовательность 
действий в этом случае будет такой: 
    1.   Следует определить метод для создания нового объекта класса 
ENROLLMENT. Этот метод принимает в качестве параметров значения переменных 
COURSES, OFF#, EMP# и GRADE и приводит к созданию нового объекта ENROLLMENT с 
некоторым заданным значением параметра GRADE. Кроме того, потребуются также 
некоторые дополнительные действия. 
    • Следует использовать значения COURSES и OFF# для нахождения объекта 
OFFERING, соответствующего новому объекту класса ENROLLMENT. 
    • Затем необходимо найти "множество всех учеников" для этого объекта 
класса OFFERING. 
    • Далее следует добавить идентификатор нового объекта класса ENROLLMENT к 
соответствующему "множеству всех учеников". 
    • Кроме того, следует использовать значение переменной ЕМР# для нахождения 
соответствующего объекта EMPLOYEE. 
    • Затем необходимо разместить идентификатор объекта EMPLOYEE в переменной 
ЕМР внутри нового объекта класса ENROLLMENT. 
    2.  Следует вызвать только что определенный метод для каждой отдельной дисциплины. 

    Преподаватели 

    Различие между способами создания объектов для преподавателей и дисциплин 
заключается в том, что TEACHER является подклассом класса EMPLOYEE. Ниже 
приведена последовательность действий, которые необходимо выполнить в этом случае. 

    626	Часть VI. Объектно-ориентированные системы 

    1.  Следует определить метод для создания нового объекта класса TEACHER. 
Этот метод принимает в качестве параметров значения переменных COURSES, OFF# и 
ЕМР#. Потребуются также некоторые дополнительные действия. 
    • Следует использовать значение переменной ЕМР# для нахождения 
соответствующего объекта EMPLOYEE. 
    • Затем необходимо преобразовать объект EMPLOYEE в объект TEACHER 
(поскольку этот сотрудник теперь дополнительно является также и 
преподавателем). Более подробно представление подклассов/суперклассов в 
объектно-ориентированной системе рассматривалось в главе 22. 
    • Кроме того, следует использовать значения переменных COURSES и OFF# для 
поиска объекта класса OFFERING, соответствующего новому объекту класса TEACHER. 
    • Далее необходимо найти "множество всех преподавателей" для этого объекта 
класса OFFERING. 
    • Затем добавить идентификатор нового объекта класса TEACHER к 
соответствующему "множеству всех преподавателей". 
    2.  В дополнение к этому следует определить некоторым образом множество 
всех курсов, которые может вести данный преподаватель, а также задать 
соответствующим образом значение переменной COURSES в новом объекте класса 
TEACHER. Однако здесь эти подробности будут опущены. 
    3.  Наконец, следует вызвать только что определенный метод для каждого 
отдельного преподавателя. 

    Реляционные аналоги 

    В заключение можно для сравнения показать, как реализуется представленная 
выше объектно-ориентированная структура в реляционной версии рассматриваемой 
образовательной базы данных. Вот, например, как будет выглядеть код процедуры 
вставки в базу данных сведений о сотруднике Е009 на языке SQL: 

    INSERT INTO EMPLOYEE { EMP#, ENAME, JOB ) 
    VALUES ( VE009', 'Watt', 'Janitor' ) ; 
    Для вставки сведений в другие части базы данных код имеет вид: 
    INSERT  INTO COURSE   (  COURSE#,   TITLE  ) 
    
    VALUES   (   'C001',   'Database Technology1   )   ; 
    INSERT INTO OFFERING   (  COURSE*,   OFF#,   ODATE,   LOCATION  )	_, 
VALUES   (   'C001',   '004',   DATE  '94/01/18',   'CDG'   )   ;              
                               '* 
    INSERT  INTO ENROLLMENT   (  COURSE*,   OFF#,   EMP#,   GRADE   ) VALUES   
(   'C001',    '004',    'E009',   ''   )   ; 

    Обратите внимание, что как в реляционной, так и в объектно-ориентированной 
системе для вставки, например, нового ученика от пользователя требуется 
предоставить совершенно одинаковую информацию. 

    23.4. Операции извлечения 

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

    Глава 23, Пример объектно-ориентированной системы	627 

    Рассмотрим в качестве примера запрос "Найти все дисциплины для курса С001, 
которые преподаются в Нью-Йорке". Для простоты предположим, что есть некоторая 
переменная OOSOAC, значение которой является идентификатором "множества всех 
курсов". Ниже приводится код для такого запроса. 

    1	| COURSE_C001 , C001_OFFS , C001_NY_OFFS I 
    2	COURSE_C001 
    3	:= COSOAC DETECT : [ :CX | 'C001' = CX GET_COURSE# ] . 
    4	COOljDFFS 
    5	:= COURSE_C001 GET_OFFERINGS . 
    6	C001_NY_OFFS 
    7	:= COOljDFFS SELECT : [ :OX | 'New York1 = OX GET_LOCATION ] . 
    8	Л C001_NY_OFFS . 

    Пояснения: 
    •  В строке 1 объявлено три локальных переменных: COURSE_COO 1, которая 
будет использована в качестве идентификатора курса С001; C001_OFFS, которая 
будет использована в качестве идентификатора "множества всех дисциплин" для 
курса С001; и наконец, C001_NY_OFFS, которая будет использована в качестве 
идентификатора множества идентификаторов для требуемых дисциплин. 
    •   В строках 2 и 3 посылается сообщение объекту, обозначенному переменной 
OOSOAC. Это сообщение вызывает встроенный метод DETECT данного объекта с аргументом 
    [   :х |  р(х)   ]. 
    Здесь р(х) — условное выражение, включающее переменную х, а х — переменная 
диапазона, охватывающего все члены множества, к которым применяется метод 
DETECT (т.е. множества объектов COURSE в рассматриваемом примере). Результатом 
выполнения метода DETECT является возвращение идентификатора первого объекта 
этого множества, в котором выражение р(х) является истинным (в рассматриваемом 
примере это экземпляр С001 объекта класса COURSE). Затем идентификатор этого 
объекта класса COURSE присваивается переменной COURSE_C001. 
    Замечание. В качестве аргумента метода DETECT можно также задать некоторый 
код для обработки случаев, когда выражение р(х) не является истинным. Однако 
такая детализация здесь будет опущена. 
    •   В строках 4 и 5 переменной C001_OFFS присваивается идентификатор 
"множества всех дисциплин" для курса СОО1. 
    •  Строки 6 и 7 подобны строкам 2 и 3, поскольку встроенный метод SELECT 
подобен методу DETECT, за исключением того, что он возвращает идентификатор 
семейства идентификаторов всех объектов (вместо того чтобы идентифицировать 
только первый объект), для которых выражение р(х) является истинным. В 
рассматриваемом примере в результате выполнения этого метода переменной 
C001_NY_OFFS присваивается идентификатор множества идентификаторов для 
дисциплин курса С001, которые предлагаются в Нью-Йорке. 
    •   Наконец, в строке 8 этот идентификатор возвращается (символ "Л" 
используется для обозначения операции "отобразить" или "возвратить результат"). 
    Здесь нужно обратить внимание на некоторые особенности. 
    1.  Код методов SELECT и DETECT может содержать (в самом сложном случае) 
множество простых скалярных операторов сравнения, которые соединяются с 
помощью операторов AND, т.е. он является некоторой формой условия ограничения. 
    2.  Квадратные скобки, окружающие код методов SELECT и DETECT, можно 
заменить круглыми скобками. При использовании круглых скобок в языке OPAL 
будет предпринята попытка задействовать индекс (если соответствующий индекс 
существует), а при использовании квадратных скобок индексы не будут задействованы. 

    628	Часть VI. Объектно-ориентированные системы 

    3.  Отмечая, что метод DETECT возвращает идентификатор "первого 
встречного" объекта с истинным значением выражения р(х), подразумевают, что 
первый найденный объект соответствует заданному в языке OPAL критерию поиска 
(внутри множества не задано никакого внутреннего упорядочения). В 
рассматриваемом примере это замечание не имеет смысла, поскольку "первый" 
объект, для которого выражение р(х) истинно, фактически является единственным 
таким объектом. 
    4.   Внимательный читатель непременно заметит выражения типа "метод 
DETECT", хотя, как уже отмечалось выше, методы в языке OPAL не имеют имен. 
Действительно, DETECT и SELECT не являются названиями методов (а потому 
выражения типа "метод DETECT" некорректны). Скорее, они служат именами внешних 
параметров для некоторых встроенных (и анонимных) методов. Однако для 
краткости и простоты далее в качестве имен методов по-прежнему будут 
использоваться названия DETECT и SELECT (а также другие подобные им названия 
для других подобных терминов). 
    5.   Кроме того, внимательный читатель заметит также, что довольно часто 
используется выражение "метод NEW". Однако в данном случае это выражение 
вполне корректно. В качестве исключения в языке OPAL допускается использование 
анонимных методов, которые не содержат каких-либо аргументов, кроме объекта-получателя. 
    В приведенном выше примере не было продемонстрировано, что многие операции 
извлечения требуют выполнения одного или более циклов (поскольку язык OPAL 
оперирует на уровне записей). В языке OPAL циклы поддерживаются с помощью 
специального метода DO. Сообщение, с помощью которого вызывается метод DO, 
подобно аналогичным сообщениям для вызова методов DETECT и SELECT и имеет 
следующий вид: 
    объект DO  :   [   :х [  блок_кода ]   ] 
    Здесь областью определения переменной х являются члены множества, 
обозначенного словом о&ъект, а блок_кода, в свою очередь, выполняется для 
каждого такого члена. С помощью методов GET_v и SET_v этот код может извлекать 
экземпляры переменных внутри отдельных объектов этого множества (или, конечно 
же, обновлять их, но в данном случае рассматривается операция извлечения). 
    Для составления кода с некоторыми заданными условиями также можно 
использовать методы IFTRUE и IFFALSE, описание которых опускается. 

    23.5. Операции обновления 

    Объектно-ориентированный аналог операции вставки FNSERT уже обсуждался 
выше в этой главе, а аналоги операций обновления UPDATE и удаления DELETE 
рассматриваются ниже. 

    Обновление 

    Операции обновления выполняются точно так же, как операции извлечения, за 
тем исключением, что вместо методов GET v используются методы SET v. 

    Удаление 

    Для удаления объектов используется встроенный метод REMOVE. Точнее, он 
используется для удаления идентификатора некоторого объекта из некоторого 
семейства. Если на данный объект не делается никаких ссылок, т.е. к нему вовсе 
не может быть организован доступ, то в языке OPAL для этого случая 
предусмотрена операция автоматического удаления "накопившегося мусора". Ниже 
приводится пример реализации операции "удаления сотрудника Е001 из множества 
всех сотрудников". 

    OID_OF SET_OF_ALL_EMPS 
    ""REMOVE : DETECT : [ :EX i  'Eooi' = EX GET EMP# ]. 

    Глава 23, Пример объектно-ориентированной системы	629 

    Но как в такой ситуации выполнить операцию каскадного удаления DELETE 
CASCADE: например, при удалении некоторого сотрудника удалить также все 
дисциплины для этого сотрудника? Для этого, естественно, придется создать 
соответствующую процедуру. 
    Можно представить, что применение метода удаления "накопившегося мусора" 
является всего лишь некоторой разновидностью воплощения правила 
"ограниченного" удаления DELETE RESTRICTED, поскольку объект не удаляется до 
тех пор, пока на него существует какая-нибудь ссылка. Однако в данном случае 
это требование не является обязательным. Например, объекты дисциплин 
(OFFERING) не содержат идентификаторов соответствующего объекта курса 
(COURSE), а потому дисциплины не накладывают "ограничений" на удаление курсов. 
    (В иерархиях контейнеров неявно подразумевается некоторая разновидность 
правила каскадного удаления, кроме некоторых случаев, когда пользователь включает 
    •   либо идентификатор родительского объекта в дочерний объект, 
    •  либо идентификатор дочернего объекта в родительский объект. 
    В таких случаях интерпретация "иерархии контейнеров" не имеет никакого 
смысла. Подробнее об этом речь идет далее в книге.) 
    В заключение заметим, что операция удаления REMOVE может быть использована 
для эмуляции реляционной операции DESTROY или SQL-операции DROP, например для 
удаления объекта класса ENROLLMENT. 

    23.6. Резюме 

    Представленный в этой главе пример основан на языке OPAL, который является 
диалектом языка Smalltalk-80, поддерживаемым в системе GemStone. Сначала были 
описаны операции определения данных и создание объектных классов согласно 
правилам языка OPAL. Обычно для этого необходимо иметь "базовый" объектный 
класс, например EMPLOYEE, на основе которого можно создавать отдельные 
экземпляры объектов этого класса, а также объектный класс "семейства", 
например ESET, на основе которого можно собирать отдельные экземпляры данного 
объекта в одно или несколько семейств. Оба этих класса необходимы потому, что 
базовый объектный класс является всего лишь объявлением типа, а для размещения 
экземпляров объектов (точнее, для размещения указателей на них) также 
требуется некоторый объектный класс семейства. 
    Далее подробно рассматривались методы пополнения базы данных сведениями. 
Эти методы не совсем тривиальны, поскольку для удовлетворения ограничений 
целостности (точнее, их некоторых аналогов, принятых в 
объектно-ориентированной системе) требуется выполнить значительную подгонку 
существующих объектов. (В следующей главе офаничения целостности в 
объектно-ориентированной системе рассматриваются подробнее.) Следует 
подчеркнуть, что все операции "вставки" должны выполняться с помощью 
специально определенных методов, а не с помощью метода NEW. Кроме того, в этой 
главе отмечалась необходимость использования ключей пользователя. В частности, 
как в реляционной, так и в объектно-ориентированной системе для "вставки" 
новых сведений от пользователя требуется ввести, по сути, одну и ту же информацию. 
    Затем рассматривались операции извлечения данных. Поскольку 
объектно-ориентированные системы функционируют по принципу последовательной 
обработки записей, операции извлечения (как и многие другие) в них выполняются 
с помощью процедур. Помимо этого, были приведены также примеры методов языка 
OPAL DETECT и SELECT, которые менее других методов похожи на процедуры. 
    После этого рассматривались операции обновления данных. Операция 
обновления сама по себе подобна операции извлечения, за исключением того, что 

    630	Часть VI. Объектно-ориентированные системы 

в ней вместо методов GET_v используются методы SET_v. Операция удаления выполняется 
с помощью метода REMOVE и специального процесса удаления "накопившегося 
мусора" (в отличие от языка OPAL, в некоторых объектно-ориентированных 
системах операция удаления поддерживается в более явном виде). Затем кратко 
рассматривались операции каскадного и ограниченного удаления. 
    Дополнительные замечания 
    В заключение следует привести несколько общих замечаний в отношении 
операций, выполняемых в объектно-ориентированной системе. 
    •	Рассмотрим приведенные ниже методы. 
    1.	Метод поиска курсов, посещаемых сотрудником Е, 
    2.	Метод поиска сотрудников, посещающих курс С. 
    Эти два метода взаимно обратны, но они должны быть заданы отдельно, т.е. 
для них следует создать отдельные процедуры. Однако, исходя из соображений 
комбинаторики, при увеличении числа неизвестных эта задача становится все 
более сложной. Рассмотрим пример с курсами, сотрудниками и оценками. Для 
каждого приведенного ниже запроса потребуется явным образом определить 
отдельный метод. 
    1.	Метод поиска оценок, полученных сотрудником Е при изучении курса С. 
    2.	Метод поиска сотрудников, получивших оценку "А" при изучении курса С. 
    3.	Метод поиска курсов, на которых сотрудник Е получил оценку "А". 
    4.	Метод поиска оценок, полученных сотрудником Е при изучении всех курсов. 
    Более того, неясно, для каких объектов эти методы следует создавать. 
Например, следует ли для поиска оценок, полученных сотрудником Е при изучении 
курса С, посылать сообщение от объекта Е к объекту С? (Более подробно этот 
вопрос обсуждается в главе 24.) 
    В реляционной системе перечисленные выше проблемы можно разрешить очень 
легко, поскольку в ней существует отношение ENROLLMENT, в котором вся 
необходимая информация представлена в симметричном виде. 

    ENROLLMENT { COURSE», OFF#, EMP#, GRADE ) 

    Каждый из упомянутых выше методов может быть представлен в виде 
единственного реляционного запроса для этого отношения. Поэтому нет 
необходимости определять множество различных методов с разными (в некоторых 
случаях произвольными) аргументами. Читателю в качестве упражнения 
предлагается подробно рассмотреть эту проблему самостоятельно. Таким образом, 
в реляционных системах обеспечивается симметричная эксплуатация, при которой 
пользователь может осуществить доступ к любому заданному отношению с любой 
комбинацией "известных" значений атрибутов для извлечения других "неизвестных" 
значений атрибутов. В объектно-ориентированных системах, наоборот, 
симметричная эксплуатация не предусмотрена. 
    •  Обратите внимание, что до сих пор в этой главе ничего не говорилось о 
таких реляционных операторах, как оператор соединения. Что означает в 
рассматриваемом контексте соединение двух объектов? Какой объект будет получен 
в результате такого соединения? Какой идентификатор будет иметь этот объект? 
    Фактически в объектно-ориентированных системах вместо соединений 
поддерживаются выражения пути. Например, для получения номера курса для данной 
дисциплины и данного ученика, идентификатор которого задан переменной ENROLL, 
можно записать следующее выражение: 

    ENROLL   .   OFFERING   .   COURSE   .   COURSES 

    Глава 23, Пример объектно-ориентированной системы	631 

    Такое выражение является объектно-ориентированным аналогом некоторого 
реляционного выражения (включающего два соединения и проекцию). Однако 
обратите внимание, что такие "соединения" ограничены заранее определенным 
путем и могут иметь тип только многие-ко-многим. При этом никакие новые 
объекты не могут быть созданы, т.е. в базе данных с их помощью может быть 
осуществлен только поиск уже существующих объектов. 
    Завершая обсуждение операций, выполняемых в объектно-ориентированной СУБД, 
нельзя не отметить моменты, как присущие всему объектно-ориентированному 
подходу, так и характеризующие только некоторые особенности реализации этого 
метода в языке OPAL. С точки зрения автора, в общем случае можно было бы 
избежать некоторых упомянутых здесь проблем и сложностей. Например, наличие 
анонимных параметров для объекта-получателя, а также анонимных методов не 
обязательно влечет за собой некоторые усложнения. Однако другие аспекты 
"объектно-ориентированной модели", такие как, например, приверженность к 
использованию указателей, последовательная обработка записей, отсутствие имен 
у объектов, по-видимому, неизбежно приводят к усложнениям. Некоторая сложность 
рассмотренных ранее примеров объясняется именно этой причиной. 

    Упражнения 

    23.1. Составьте макет объектно-ориентированной версии рассматриваемой 
ранее в этой книге базы данных поставщиков и деталей. Замечание. Этот макет 
будет использоваться как основа для приведенных ниже упр. 23.2-23.4. 
    23.2. Составьте подходящий набор утверждений определения данных на языке 
OPAL для объектно-ориентированной версии базы данных поставщиков и деталей. 
    23.3. Схематически обрисуйте детали методов "пополнения сведениями" 
объектно-ориентированной версии базы данных поставщиков и деталей. 
    23.4. Составьте код на языке OPAL для реализации приведенных ниже запросов 
для объектно-ориентированной версии базы данных поставщиков и деталей: 
    а) получить сведения обо всех поставщиках из Лондона; 
    б) получить сведения обо всех красных деталях. 
    23.5. Еще раз рассмотрите образовательную базу данных. Покажите, какие 
объекты будут вовлечены в процесс удаления ученика, удаления сотрудника, 
удаления курса (при условии, что в каждом случае используется присущая языку 
OPAL операция "удаления накопившегося мусора"). Укажите любые допущения, 
которые необходимо сделать для осуществления каскадного и ограниченного удаления. 
    23.6. Допустим, что база данных поставщиков, деталей и проектов 
представлена на основе простой объектно-ориентированной иерархии контейнеров. 
Сколько таких иерархий можно задать? Какая из них является наилучшей? 
    23.7. Рассмотрите вариант базы данных поставщиков, деталей и проектов, в 
которой вместо записей о том, что некоторые поставщики поставляют некоторые 
детали для некоторых проектов, содержатся записи о том, что некоторые 
поставщики поставляют некоторые детали, некоторые детали поставляются для 
некоторых проектов и некоторые проекты обеспечиваются деталями от некоторых 
поставщиков. Сколько таких объектно-ориентированных макетов можно задать (с 
учетом или без учета иерархии контейнеров)? 

    632	Часть VI. Объектно-ориентированные системы 


    Список литературы 

    23.1. Copeland G., Maier D. Making Smalltalk a Database System // Proc. 
1984 ACM SIGMOD Intern. Conf. on Management of Data.— Boston, Mass., 1984. 
(Переиздано: M. Stonebraker. Readings in Database Systems. — San Mateo, 
Calif.: Morgan Kauftnann, 1988.) 
    В работе описаны некоторые усовершенствования и изменения, внесенные в 
язык Smalltalk-80 [22.6] при создании системы GemStone и языка OPAL. 
    23.2. Servio Corporation. GemStone Reference Manual. — Beaverton, Ore., 1990. 

    Ответы к некоторым упражнениям 

    23.5. Вместо подробного ответа здесь будет сделано одно замечание 
относительно трудности этого упражнения. Прежде всего, следует обратить 
внимание на то, что термин "удалить" будет использоваться как сокращение для 
выражения "выбрать в качестве кандидата для физического удаления" (т.е. 
удалить все ссылки на этот объект). Тогда для того, чтобы удалить объект X, 
следует сначала найти все объекты Y, которые содержат ссылку на X. Затем 
следует либо удалить такие объекты Y, либо удалить в этих объектах все ссылки 
на объект X (задавая для таких ссылок специальное значение nil, т.е. 
отсутствие значения). Частично проблемы могут возникнуть потому, что 
невозможно на основании одного только определения данных указать те объекты, 
которые содержат ссылку на объект X, или хотя бы даже сказать, сколько их 
существует. Рассмотрим, например, сотрудников и объектный класс ESET. В 
принципе может быть любое количество экземпляров класса ESET, и каждое 
подмножество экземпляров класса ESET может содержать ссылку на некоторого особ 
ого сотрудника. 
    23.6. Существует шесть возможных иерархий: 
    S содержит ( Р содержит ( J )	) 
    S содержит ( J содержит ( Р )	) 
    Р содержит ( J содержит ( S )	) 
    Р содержит ( S содержит ( J )	) 
    J содержит ( S содержит ( Р )	) 
    J содержит ( Р содержит ( S )	) 
    Без какой-либо дополнительной информации невозможно дать ответ на вопрос 
"какой из вариантов является лучшим", но можно почти с полной уверенностью 
сказать, что все они одинаково плохи. 
    23.7. Существует по крайне мере двенадцать "очевидных" макетов иерархии 
контейнеров. Ниже показаны только четыре из них: 
    S содержит	( Р содержит	( J ) ) 
    S содержит	( J содержит	( Р ) ) 
    S содержит	( сначала Р,	затем J ) 
    S содержит	( сначала J,	затем Р ) 
    Существует достаточно много других возможных макетов, например объект 
"SP", который прямо указывает на то, какими поставщиками какие детали 
поставляются, а также включает два внедренных множества проектов: одно для 
поставщиков, а другое для деталей. 
    Можно также составить проект без всяких иерархий из объектов "SP", "PJ" и "JS". 

    Глава 23. Пример объектно-ориентированной системы	633 

