[x]. "Не создавайте разработчику лишних проблем".

[x]. "Тщательно взвешивайте все "за и против" любой новинки, обращая особое внимание на безопасность и качество".

[x]. "Убедитесь, что общие операции могут быть выражены в простой и ясной форме". Применение этого принципа требует особой тщательности, поскольку проектировщик языка может ошибаться в своих оценках того, что же является наиболее общим случаем. Но в данной ситуации все кажется проще. Для сущностей развернутого типа (таких как INTEGER) присваивание и сравнение значений представляются наиболее употребительными операциями. Для ссылок, в то же время, ссылочное сравнение и присваивание используется чаще, чем клонирование, копирование и сравнение объектов. Поэтому в обоих случаях предпочтительнее использовать := и = для фундаментальных операций.

[x]. "Для сохранения компактности и простоты языка вводите новые обозначения, только если это абсолютно необходимо". Это справедливо в частности для приведенного примера - существующая нотация работает и не существует опасности путаницы.

[x]. "Если вы знаете, что существует риск возникновения недоразумений между двумя возможностями, то соответствующие нотации должны различаться очевидным образом". Так что необходимо избегать использования символов, близких по написанию (:- и :=), но с различной семантикой.

Еще одна причина играет роль в данном случае, хотя она включает механизм, пока еще не изученный. В последующих лекциях мы познакомимся с родовыми или универсальными классами, такими как LIST [G], где G, известно как формальный родовой параметр, представляющий произвольный тип. Такой класс может манипулировать сущностями типа G и использовать их в присваиваниях и проверках на равенство. Клиенты, нуждающиеся в использовании такого класса, должны позаботиться о создании типа, служащего в качестве фактического родового параметра. Например, они могут использовать LIST [INTEGER] или LIST [POINT]. Как показывают эти примеры, фактический родовой параметр может быть развернутого типа, как в первом случае, так и ссылочного типа - во втором случае. В подпрограммах такого родового класса, если a и b имеют тип G, то часто полезно использовать присваивания в форме a := b или тесты в форме a = b с намерением получить семантику значений, когда фактический параметр принадлежит развернутому типу, такому как INTEGER, и ссылочную семантику - для ссылочного типа, такого как POINT.

Примером подпрограммы, нуждающейся в таком дуальном поведении, является процедура вставки элемента x в список. Процедура создает новый элемент списка. Если x целое, элемент должен содержать копию значения x. Если x является ссылкой, то элемент списка должен содержать ссылку на объект, присоединенный к x .

В таких случаях, правила, определенные выше, гарантируют желаемое дуальное поведение, что было бы недостижимо, если бы требовался различный синтаксис для двух видов семантики. С другой стороны, если во всех случаях требуется единая семантика, то и это достижимо: такое поведение может быть только семантикой значений (так как семантика ссылок не имеет смысла для развернутых типов); поэтому в соответствующих подпрограммах следует использовать clone (или copy) и equal, а не (:= и =).

<p>Форма операций клонирования и эквивалентности</p>

Форма вызова подпрограмм clone и equal является стилевой особенностью, которая может вызвать удивление. На первый взгляд нотация:

clone (x)

equal (x, y)

выглядит не слишком объектно-ориентированной. Догматичное следование принципу "ОО-стиля вычислений" из предыдущей лекции предполагает другую форму (См. "Объектно-ориентированный стиль вычислений", лекция 7):

x.twin -- twin это двойник - клон.

x.is_equal (y)

В первой версии нотации так и делалось, однако возникла проблема пустых ссылок. Вызов компонента вида x.f (...) не может быть корректно выполнен в случае пустого x во время выполнения. В этом случае вызов инициирует исключение, которое повлечет аварийное завершение всей системы, если в соответствующем классе не предусмотрена обработка исключений. Поскольку во многих случаях x может быть действительно пустой ссылкой, то это означало бы, что каждый вызов twin должен предусматривать охрану и выглядеть так:

if x = Void then

z := Void

else

z := x.twin

end

Соответственно, реализация вызова is_equal должна выглядеть (and then является вариантом and. См. "Нестрогие булевы операции", лекция 13):

if

((x = Void) and (y = Void)) or

Перейти на страницу:

Похожие книги