Главная страница


su.dbms

 
 - SU.DBMS ----------------------------------------------------------------------
 From : Victor V. Metelitsa                  2:5077/13      29 Jan 2001  15:13:04
 To : All
 Subject : Заметки по поводу Дейта и GemStone
 -------------------------------------------------------------------------------- 
 
 Крис Дейт. Введение в системы баз данных.
 Шестое издание. Диалектика. Киев, Москва 1998.
 "Часть VI. Объектно-ориентированные системы."
 
 Очень известная и популярная книжка. К сожалению, в шестой части Дейт допустил
 ряд крупных ошибок, которые привели его к неверным выводам. Проблема, наверное, 
 в том, что Дейт недостаточно хорошо знал Smalltalk и не разобрался в GemStone.
 
 !> Hесколько вступительных слов:
 
 Hачну с того, что Smalltalk можно рассматривать не только как язык
 программирования, но и как язык запросов к базе данных. В самом деле, сравним
 
 [1]
 SELECT * FROM Employee WHERE lastName = 'Smith'
 
 и
 [2]
 Employee select: [:eachEmployee | eachEmployee lastName = 'Smith']
 
 Первое выражение (на SQL), уверен, понятно всем читателям этой конференции:
 имеется таблица Employee, из нее выбраны все записи, удовлетворяющие условию
 lastName = 'Smith'.
 
 Второе (на Smalltalk) означает примерно то же самое:
 имеется глобальная переменная по имени Employee, ссылающаяся на коллекцию
 объектов. Из нее выбираются такие объекты, для которых верно соответствующее
 условие.
 
 Пояснение:
 [:eachEmployee | eachEmployee lastName = 'Smith']
  :eachEmployee - это параметр.
 
 Перед вертикальной чертой перечисляются параметры. После вертикальной черты
 записывается выражение. Все вместе, заключенное в квадратные скобки, называется 
 "блок кода". В данном примере блоку кода передается параметр, производится
 вычисление и в качестве результата появляется true или false.
 
 То, что реально внутри метода #select: запрятан цикл с перебором, никого
 волновать не должно. Ведь внутри SQL-ного SELECT тоже запрятан цикл с перебором.
 То, что при исполнении SQL-ного SELECT может использоватся индекс, не имеет
 отношения к делу. Smalltalk'овый #select: также может анализировать выражение и 
 использовать индекс (что на практике подтверждает TOPLink). Это все детали
 реализации. К делу отношение имеет лишь форма записи. В обоих случаях речь идет 
 о записи операции на множестве (коллекции) для получения выборки.
 
 Можно привести аналоги более сложным выражениям, и не только выборки, но и
 INSERT, UPDATE и DELETE. Благодаря тому, что Smalltalk можно рассматривать как
 язык запросов, среды программирования для него можно рассматривать как интерфейс
 к объектно-ориентированной базе данных. Прочие языки программирования не могут
 это полностью воспроизвести.
 
 GemStone добавляет к обычному Smalltalk'у такие вещи, как журнал транзакций и
 crash recovery, многопользовательность и средства разграничения доступа, индексы
 и прочие вещи, положенные для СУБД. (Hо тем не менее это чистый Smalltalk, а не 
 "нечто, на что оказал влияние Smalltalk", и называется он GemStone Smalltalk, а 
 слово OPAL сейчас практически не упоминается).
 
 !>Далее примечания к тексту...
 
 >Стр. 605. "Обратите внимание, что если для манипулирования данными заданы
 >только *заранее* определенные методы, то *незапланированные* запросы
 >невозможны, если только для объектов не задана некая специализированная
 >структура."
 
 Как же, как же. См. выражение [2]. И передать на сервер строчку с произвольным
 выражением (и получить результат) вполне можно.
 
 >Стр. 605. "Методы вызываются с помощью сообщений, которые, по сути,
 >являются вызовами функций...".
 
 Для Smalltalk'а это не совсем так. Объекту посылается сообщение, которое
 характеризуется селектором и аргументами. Если у объекта есть метод с тем же
 селектором, то он и вызывается. В противном случае вызывается метод с селектором
 #doesNotUnderstand:. Это не обязательно ошибочная ситуация: на этом основана
 куча вещей, включая proxy и анализ скомпилированных блоков. Почему Дейт называет
 метод, определяемый им на странице 623, анонимным (про синтаксические ошибки я
 вообще стараюсь молчать), не могу понять. Очевидно, селектор метода - это и есть
 его имя, в его примере селектор должен быть #ADD_EMP#:ADD_ENAME:ADD_JOB: (но
 компилятор такое не проглотит). Дейт пишет про "сигнатуру", в которую входят
 имена параметров. Однако на самом деле имена параметров в селектор _не входят_.
 
 >Стр. 624. "Обратите внимание, что встроенный метод NEW никогда не должен
 >использоваться для класса EMPLOYEE [...]".
 
 Потому что пример неправильный. Правильно было создать в классе Employee метод
 #initialize, который добавляет self в ESet (Set of Employee), затем, возможно
 (нужное могло быть уже сделано в суперклассе), переопределить метод #new так:
 
 Employee class>>#new
 
 ^super new initialize
 
 Можно подумать также, что Дейт не знал о возможности перекрытия методов (или
 знал, но не осознавал).
 
 >Стр. 626. "Обратите внимание, что (в соответствии с используемым
 >представлением иерархии контейнеров) здесь не было создано 'множество
 >*всех* дисциплин'".
 
 Во всяком случае, никто не заставлял использовать "иерархию контейнеров" и никто
 не мешал сделать "множество всех дисциплин".
 
 [Примечание:
 
 В отличие от реляционных СУБД, где каждая запись физически присутствует в одной 
 и только одной таблице, GemStone всегда имеет дело со ссылками (исключением
 могут быть лишь экземпляры SmallInteger, Characher и nil), и ссылки на один и
 тот же объект могут содержать разные коллекции (и не только коллекции).
 
 Правда, это несколько затрудняет удаление, поскольку формально никакого
 удаления-то и нет. Просто, если на объект никто не ссылается, его в конце концов
 съест сборщик мусора. Итак, чтобы удалить объект, надо удалить его из всех
 коллекций (необходимое, но недостаточное условие). Удобно сделать так, чтобы
 объект знал, кто именно на него ссылается, и сам убрал все ссылки на себя "перед
 смертью". Звучит, может быть, и сложно, но делается просто (см. также ниже).
 
 Казалось бы, Дейт об этом в курсе, но почему же тогда он не сделал "множество
 всех дисциплин", а потом жалуется на неудобства?
 ]
 >Стр. 627. "Прежде чем приступать к подробному описанию операций
 >извлечения, следует отметить, что (хотя это и вполне очевидно) язык OPAL,
 >как и другие объектно-ориентированные языки в целом, функционирует по
 >принципу последовательной обработки записей, а не множеств.
 >Следовательно, для решения большинства проблем программист должен создать
 >некоторую процедуру.".
 
 Утверждения неверны. См. самое начало. Метод можно и создать, почему бы и нет,
 но здесь его следует рассматривать скорее как аналог VIEW.
 
 > Стр. 628. "Квадратные скобки ... можно заменить круглыми".
 
 а самом деле фигурными.
 
 >Стр. 629. "В языке OPAL циклы поддерживаются с помощью специального
 >метода DO".
 
 Как обычно, поправки:
 * Язык - Smalltalk, а не OPAL;
 * Метод - #do:, не DO, и никакой не "специальный" (нет такого понятия -
 "специальный метод", а есть просто методы);
 * Поддерживаются не циклы, а операции над множествами (точнее, коллекциями).
 
 Пример:
 
 коллекция do: [:параметр | выражение ].
 
 Сравним
 
 [3]
 UPDATE Employee SET lastName = 'Smith'
 
 [4]
 Employee do: [:eachEmployee | eachEmployee lastName: 'Smith']
 Это означает примерно то же самое. Hам нет никакого дела до реализации,
 а записано одно и то же.
 
 С условием фильтрации Smalltalk начинает выглядеть громоздким:
 [5]
 UPDATE Employee SET lastName = 'Smith'
 WHERE lastName = 'Simpson'
 
 [6]
 (Employee select: [:eachEmployee | eachEmployee lastName = 'Simpson'])
   do: [:eachEmployee | eachEmployee lastName: 'Smith']
 
 однако есть и возможность разного рода оптимизаций.
 
 > Стр. 630.
 
 Как выполнить операцию DELETE CASCADE?
 
 ОТВЕТ:
 Каждый объект должен знать ссылающихся на него и уметь удалить ссылки на себя.
 Hазовем соответствующий метод #removeMe.
 
 Далее, пустить мы удаляем некий объект x, который ссылается на объекты класса Y 
 в отношении 1:N, т.е. имеет переменную экземпляра по имени ys с коллекцией
 объектов класса Y. Тогда метод removeMe объекта x должен содержать
 
 (ys copy) do: [:each | each removeMe].
 
 Вот и все! А если приложить немного мозгов, то можно будет устроить так: метод
 #removeMe будет един, и в каждом классе, где требуется каскадное удаление,
 потребуется не переписывать #removeMe, а просто указать коллекцию имен
 переменных в некотором методе (назовем его #selectorsForCascadeDelete), и
 выражение будет выглядеть как
 
 self selectorsForCascadeDelete do:
   [:eachSelector | ((self perform: eachSelector) copy) do:
     [:each | each removeMe]].
 
 [Примечание: Для чего нужна посылка сообщения #copy? Обычно не рекомендуется
 добавлять/удалять элементы в той коллекции, где производится #do:. Так #copy
 создаст временную коллекцию, к которой и будет применен #do:, затем она
 исчезнет].
 
 > Стр. 630. "Обычно для этого нужно... а также объектный класс
 > 'семейства', например ESET, на основе которого можно собирать отдельные
 > экземпляры...'.
 
 Вовсе нет. Класс ESET не нужен, можно обойтись Set'ом. Тут, кстати, можно еще
 вспомнить определения класса EMPLOYEE, которое я перепишу так, чтобы оно было
 больше похоже на настоящее:
 
 Object
   subclass:     'Employee'
   instvarNames: #('empNo' 'eName' 'job')
   constraints:  #[ #['empNo', String],
                    #['eName', String],
                    #['job', String]
                  ].
 
 Дейт не указал, что ограничения не обязательны. Определение могло выглядеть
 и так:
 
 Object
   subclass:     'Employee'
   instvarNames: #('empNo' 'eName' 'job')
   constraints:  #[].
 
 что было бы эквивалентом
 
 Object
   subclass:     'Employee'
   instvarNames: #('empNo' 'eName' 'job')
   constraints:  #[ #['empNo', Object],
                    #['eName', Object],
                    #['job', Object]
                  ].
 
 и в таком случае в переменные можно присваивать значения (или ссылки на них)
 какого угодно класса. То же самое относится и к коллекциям (ESET и т.п.).
 
 Далее. Пусть в ограничениях указано #['varName', SomeClass], тогда в varName
 может быть не только (ссылка/значение) класса SomeClass, но и любого полкласса
 SomeClass.
 
 В подклассах можно усиливать ограничения, например
 
 Employee
   subclass:     'SubEmployee'
   instvarNames: #()
   constraints:  #[ #['empNo', SubclassOfString]
                  ].
 
 > Стр. 631. "Дополнительные замечания".
 
 (Как знает и Дейт) обычно объект в ОО-системе хранит в себе ссылки на то, к чему
 он имеет отношение. Если объекты класса A находятся в отношении 1:N к объектам
 класса B, то обычно объект класса A содержит коллекцию объектам класса B и
 одновременно объект класса B - ссылку на объект класса A, причем это взаимо
 согласованно (Пример: учитель 'Александров' содержит коллекцию ссылок на
 учеников {'Иванов', 'Петров', 'Сидоров'} и одновременно 'Иванов', 'Петров',
 'Сидоров' имеют по ссылке на учителя по фамилии 'Александров'). Одновременно
 можно иметь также коллекцию всех учителей, всех учеников и т.д.
 
 Если данные организованы именно так, то проблем с запросами не будет. Считаю,
 что заявление Дейта "В *объектно-ориентированных* системах, наоборот,
 симметричная эксплуатация не предусмотрена" - неверно.
 
 А если возникают вопросы типа "методом какого класса это должно быть - X или
 Y?", обычный ответ - "реализуй и там, и там". Сперва там, где это будет наиболее
 эффективно, затем из другого вызывается этот.
 
 > Стр. 631. Оператор соединения (JOIN).
 
 Вопрос решается по разному в разных ситуациях. Здесь подробности приводить не
 буду.
 
 >Стр. 635. "В объектно-ориентированных системах обычно используется два
 >языка: один для программирования приложений, а другой для создания
 >запросов".
 
 Hо не в GemStone. Smalltalk'а хватает для всего.
 
 >Стр. 642. "В объектно-ориентированной системе, наоборот, это ограничение
 >почти определенно будет приведено в действие с помощью некоторой
 >*процедуры*".
 
 Точнее, программистом будет исправлен код соответствующего set-метода (методов).
 
 Впрочем, если быть предусмотрительным, то код set-метода можно не исправлять.
 Решение: Пусть объект должен знать коллекцию имен неких check-методов. Перед
 присваиванием set-метод должен вызвать каждый метод из той коллекции и, в случае
 непрохождения проверки, выбросить исключение (или check-метод сам выбросит
 исключение). Коллекция может быть общей для класса или индивидуальной для
 каждого объекта.
 
 Таким образом, добавление новой проверки сводится к
 
 1) Добавлению метода
 2) Добавлению его имени в соответствующую коллекцию.
 
 и (после некоторого дополнительного размышления) отпадают вопросы со стр. 642.
 
 >Стр. 643. "Выше уже упоминалось, что в объектно-ориентированных системах
 >существуют определенные трудности с обработкой *незапланированных*
 >запросов (также называемых запросами типа *ad hic)".
 
 Сколько угодно упоминайте неправду, правдой от этого она не станет. GemStone не 
 имеет таких трудностей. Как на SQL-сервер можно отправить строчку с
 SQL-выражением, так и на GemStone можно отправить строчку со
 Smalltalk-выражением и получить какой-то ответ (например, результирующую
 коллекцию).
 
 > Стр.645. "Каталог. Где и в каком виде находится каталог в
 > объектно-ориентированной системе? Существуют ли какие-либо стандарты в
 > отношении каталогов?"
 
 В клиентском Smalltalk'е каталог один (по имени Smalltalk), а GemStone их может 
 иметь неограниченное количество (один, понятно, самый главный, другие могут быть
 совместно используемыми или же частными, что позволяет иметь в системе разные
 одноименные классы и таким образом разработчики не навредят друг другу). Для
 пользователей это прозрачно.
 
 Поэтому то, что Дейт пишет ниже про отличие реляционной системы от ОО-системы,
 неверно. GemStone как минимум такая же "готовая для употребления" система, как и
 реляционка.
 
 О стандарте говорить не приходится, поскольку GemStone единственный в своем
 роде.
 
 > 645. "Является ли значение nil членом каждого класса?"
 
 nil isKindOf: Object
 Ответ: true
 nil isMemberOf: Object
 Ответ: false
 25-ю главу даже можно не читать.
 
 > Стр.652.
 
 Для Smalltalk'а не надо переименовывать "посылку сообщения" в "вызов метода",
 потому что это разные вещи. Дело в том, что объект может не иметь
 соответствующего сообщению метода, причем не в результате ошибки, а умышленно
 (далее осуществляется нечто вроде маршрутизации сообщения).
 
 С объединением и проекцией - не вижу никаких проблем.
 
 --- GoldED/W32 3.0.1
  * Origin: Завод по изготовлению и переработке вакуума: (2:5077/13)
 
 

Вернуться к списку тем, сортированных по: возрастание даты  уменьшение даты  тема  автор 

 Тема:    Автор:    Дата:  
 Заметки по поводу Дейта и GemStone   Victor V. Metelitsa   29 Jan 2001 15:13:04 
Архивное /su.dbms/18363a757a82.html, оценка 3 из 5, голосов 10
Яндекс.Метрика
Valid HTML 4.01 Transitional