According to Date

Encapsulation Is a Red Herring

C.J. Date

DataBase Programming & Design OnLine, September 1998
Оригинал статьи можно найти по адресу http://www.dbpd.com/9809date.html

И, может быть, лучше было бы отказаться от этого термина

Инкапсуляция широко воспринимается как ключевое свойство или премущество объектной технологии. Однако, по мнению автора, понятие инкапсуляции всегда несколько переоценивалось; более важным является проведение четкого различия между типами и представлениями. Как показывает название этого материала, автор считает инкаплуляцию саму по себе в чем-то похожей на "ржавую середку" [если читателей заинтересует происхождение такой странной метафоры, рекомендую заглянуть на Web-страницу журнала Red Herring - www.herring.com/about/lore.html (прим. С.Кузнецова)] и постарается далее это объяснить.

Что означает инкапсуляция?

Говорят, что тип данных инкапсулирован, если экземпляры этого типа не имеют видимых пользователям компонентов. (Термин "экземпляр" используется как удобное, хотя и неуклюжее сокращение для "значение или переменная".) Например, в своей книге Smalltalk-80: The Language and It's Implementation (Addison-Wesley, 1983) Adele Goldberg и David Robson говорят: "Объект состоит из некоторой частной памяти и набора операций ... Публичные свойства объекта являются [спецификациями операций], составляющими его интерфейс... Частные свойства объекта - это набор [компонентов], составляющих его частную память".

Замечание: Автор предпочитает не использовать слишком часто термин "объект", потому что он нечеткий и часто приводит к неправильному пониманию сути. Ниже этот термин используется только в цитатах.

Как явствует из приведенного высказывания, пользователи Smalltalk оперируют экземплярами ("объектами") инкапсулированного типа посредством операций, явно определенных в связи с этим типом. Например, можно иметь тип CIRCLE и быть в состоянии вызывать операции, которые возвращают площадь - или длину окружности, или радиус (и т.д.) - любого заданного круга. Однако было бы незаконно утверждать, что круг имеет компонент площади, компонент длины окружности, компонент радиуса и т.д. Важным следствием этого является то, что мы не знаем и не должны знать, как круги представлены внутри системы; это представление доступно только через код, реализующий операции. Другими словами, для пользователей представляет интерес тип - это часть модели, - в то время как представление интересно только для реализации. В своем введении в объектные базы данных Stanley Zdonik и David Maier говорят: "Инкапсуляция [означает, что у каждого типа имеется] набор [операций и] представление ... которое выделяется для каждого его экземпляра. Это представление используется для сохранения состояния объекта. Только методы, реализующие операции объектов, имеют доступ к представлению, что дает возможность изменять представление не затрагивая оставшуюся часть системы. Требуется только переписать методы".(1)

Заметим, что к инкапсуляции можно относиться как к другому обличию известного понятия независимости данных. Если должным образом различать тип и представление, и если добиться скрытости представления, то можно изменять представление без потребности изменять прикладные программы. Нужно всего лишь изменить код, реализующий операции.

Возможно, теперь можно начать понимать, почему автор не считает понятие инкапсуляции слишком существенным. Основной смысл этого понятия состоит в том, что мы не должны беспокоиться о том, о чем нам не следует беспокоиться, а именно, о физическом представлении (называемом также действительным или внутренним представлением). Другими словами, как уже утверждалось, инкапсуляция, на самом деле, является логическим следствием различия между типом и представлением.

Последнее замечание по поводу термина. При подготовке этого материала автору пришлось просмотреть около 20 книг по объектной технологии и связанным темам. Удивительно, что ни в одной из них не удалось найти точное определение понятия инкапсуляции. (Лучшее разъяснение содержится в приведенной выше цитате из книги про Smalltalk; следует заметить, помимо прочего, что в этой книге вообще не используется термин "инкапсуляция", его нет даже в индексе.) Автор обнаружил, что некоторые авторы понимают под этим понятием физическое связывание определений представления данных и определений операций. Например, "Инкапсуляция - это понятие соединения обработки или поведения с экземплярами объектов, определенных в классах. Инкапуляция позволяет упаковывать вместе код и данные".(2) Но, по мнению автора, такая интерпретация термина приводит с перемешиванию модельных и реализационных вопросов. Пользователь не должен беспокоиться, у него не должно возникать поводов для беспокойства по поводу того, "упакованы ли вместе" код и данные. Убеждение автора состоит в том, что с точки зрения пользователя, т.е. с модельной точки зрения, инкапсуляция означает, что данные не содержат видимых пользователям компонентов и могут быть доступны только через посредство уместных операций.

Но что насчет непредвиденных запросов?

Инкапсуляция вступает в некоторый конфликт с потребностью выполнять непредвиденные запросы. Инкапсуляция означает, что доступ к данным может быть произведен только через заранее определенные операции, а смысл непредвиденных запросов, почти по определению, состоит в том, что требуется доступ, способ которого невозможно предопределить. Например, пусть имеется тип данных POINT; предположим, что также имеется (предопределенная) операция для "взятия" (чтения или выборки) X-координаты заданной точки, но нет подобной операции для соответствующей Y-координаты. Тогда невозможно выполнить даже следующие простые запросы и множество подобных:

В Third Manifesto (3) Крис Дейт и Хью Дарвин для разрешения этого конфликта предлагают потребовать, чтобы для каждого заданного типа были определены операции, раскрывающие некоторое возможное представление экземпляров этого типа (авторы называют такие операции "THE_operators"). Для типа POINT, например, можно было бы определить операции THE_X и THE_Y, что позволило бы производить следующие действия:

	Q := THE_Y (P) ;
	/* получить Y-координату точки P и присвоить ее Q */
	Z := SQRT ( THE_X (P) ** 2 + THE_Y (P) ** 2 ) ;
	/* получить расстояние до точки P от точки (0,0) и присвоить его Z */

Таким образом, THE_X и THE_Y действительно раскрывают возможное представление - а именно, декартовы координаты X и Y - и обеспечивают возможность выполнять непредвиденные запросы с точками. Однако это не означает того, что внутри системы точки действительно представлены декартовыми координатами; это значит лишь то, что декартовы координаты являются возможным представлением. В реальном представлении могут использоваться декартовы координаты, полярные координаты R и U или что-нибудь совсем другое. Другими словами, THE_операции не нарушают инкапсуляцию и не подрывают независимость данных. Заметим, кстати, что типы данных DATE и TIME языка SQL представляют пример встроенных типов с раскрытием некоторых возможных представлений. Например, для дат раскрывается возможное представление с компонентами YEAR, MONTH и DAY. Хотя, вероятно, следует добавить, что эти "возможные" представления в SQL, к сожалению, близки к реальным представлениям; в SQL различие между типами и представлениями часто не является четким.

Нам не всегда желательна инкапсуляция

Еще одна причина, которая заставляет автора считать инкапсуляцию не настолько важным понятием, как это полагается в литературе, состоит в следующем. Некоторые типы являются совсем не инкапсулированными, и это никому не мешает! В частности, это относится к "генерируемым" типам, которые определяются с использованием генераторов типов, такие как ARRAY, LIST, TUPLE и RELATION. Рассмотрим для определенности RELATION. Предположим, что теперь мы имеем дело с relvar (relation variable) POINT:

	VAR POINT ... RELATION { X ..., Y ... } ... ;
	/* для простоты типы атрибутов X и Y опущены */

Замечание: Определение сформулировано на языке Tutorial D, который определен в Third Manifesto для иллюстративных целей. Многоточия в определении поставлено вместо конструкций, не являющихся существенными для этого обсуждения.

В этом определении для указании (генерируемого) типа relvar используется генератор типов RELATION; это конкретный тип отношения, а именно:

	RELATION { X ..., Y ... }
	/* снова для простоты типы атрибутов X и Y опущены */

И этот тип определенно не является инкапсулированным: у него имеются видимые пользователям компоненты - атрибуты X и Y. И именно наличие этих видимых пользователям компонентов позволяет выполнять над relvar POINT непредвиденные запросы; например, можно выполнить проецирование на атрибут Y, ограничение по условию "значение Y меньше пяти".

Заметим мимоходом, что похожие наблюдение содержатся в книге Mike Stonebraker про объектно-реляционные СУБД: "Базовые типы полностью инкапсулированы. Единственный способ манипулировать [экземпляром] базового типа состоит в том, чтобы выбрать его и выполнить функцию, принимающую [экземпляр этого типа] в качестве аргумента. В противоположность этому, составные типы полностью прозрачны. Можно видеть все поля, и они легко доступны в языке запросов. Конечно, промежуточная позиция заключается в том, чтобы допустить в составном объекте наличие публичных (видимых) полей и приватных (инкапсулированных). Этот подход используется в Си++". Однако следует добавить, что остается неясным, проводит ли Стоунбрейкер четкое различие между реальными и возможными представлениями, как это делается в The Third Manifesto.

Вернемся к основному аргументу. Тот факт, что типы отношений не искапсулированы, не означает потерю независимости данных! Например, в случае relvar POINT нет абсолютно никаких причин, по которым нельзя было бы физически представить эту relvar полярными координатами R и U, а не декартовыми координатами X и Y. (Вероятно, это невозможно сделать в сегодняшних продуктах SQL, но это недостаток продуктов. Сегодняшние продукты SQL вообще обеспечивают меньшую независимость данных, чем теоретически в состоянии обеспечить реляционная технология.) Другими словами, даже при использовании не инкапсулированных типов требуется четко различать типы и представления, и "отказ от инкапсуляции" не обязательно ведет к разрушению независимости данных.

Скалярные и не скалярные типы

В The Third Manifesto авторы требуют поддержки генераторов типа TUPLE и RELATION; в результате пользователи могут определять свои собственные типы кортежей и отношений. Кроме того, требуется, чтобы пользователи могли определять "простые" типы, такие как POINT, LENGTH, AREA, LINE и т.д., возможно, даже типы, подобные INTEGER, если система не обеспечивает их как встроенные типы. И термин "скалярный тип" относится к таким "простым" типам (после чего можно говорить о скалярных значениях, скалярных переменных и скалярных операциях).

Причины выбора термина "скаляр" заключались в следующем:

Заметим теперь, что термин "скалярный" означает в точности то же самое, что и "инкапсулированный", т.е. тип данных инкапсулирован тогда и только тогда, когда он скалярный. Поэтому, по мнению автора, в индустрии следовало бы использовать привычный термин "скалярный" без потребности употребления "инкапсулированный". Это помогло бы избежать некоторых двусмысленностей.

Замечание: Можно говорить, что скалярные (или, если это больше нравится, инкапсулированные типы) являются атомарными, поскольку не содержат видимых пользователям компонентов.

Заключение

В этой заметке автор пытался показать, что термин "инкапсулированный" вызывает больше беспокойств, чем того стоит:

Литература

  1. Zdonik, S.B. and D. Maier. "Fundamentals of Object-Oriented Databases". Readings in Object-Oriented Database Systems. Morgan Kaufmann, 1990.
  2. Barry, D.K. The Object Database Handbook: How to Select, Implement, and Use Object-Oriented Databases. John Wiley & Sons, 1996.
  3. Date, C.J. and H. Darven. Foundations for Object/Relational Databases: The Third Manifesto. Addison-Wesley, 1998.
  4. Stonebraker, M. (with D. Moore). Object-Relational DBMSs: The Next Great Wave. Morgan Kaufmann, 1996.