Например, в одной и той же области действия можно объявить две функции:

function потребности (подразделение:взвод) return расчет;

function потребности (подразделение:рота) return расчет;

Для каждой функции нужно написать свое тело. Если теперь объ­явить объекты

А:взвод;

В:рота;

то вызов потребности(А) будет означать выполнение тела первой функции, а вызов потребности(В) - второй. Для пользователя же "видна" единственная операционная абстракция "потребности", применимая к объектам и типа "взвод", и типа "рота", т. е. поли­морфная функция.

Упражнение. Укажите дополнительный контекст знака функции в приведенном примере.

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

Вопрос. Как это сделать?

4.2.7. Янус-проблема

Вспомним, как возникла янус-проблема. Мы пришли к концеп­ции уникальности, желая упростить контроль. И потеряли возмож­ность иметь объекты, играющие одновременно разные роли.

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

НЕ нашли? Не то? Что вы ищете?

К характеристикам типа (общим для всех животных этого типа) добавляются специфические характеристики класса (общие только для выбранного класса, а не для всего типа), затем добавляются характеристики отряда и т. д., вплоть до характе­ристик вида.

Еще сложней ситуация, когда классификация не иерархическая. Человек - одновременно сотрудник лаборатории, отдела, института и т. п.; жилец в квартире, доме, микрорайоне и т. п.; подписчик газеты, муж, брат, сват, любитель бега и т. п. Если нужно написать на Аде пакет моделирование_человека, то как уложиться в концепцию уни­кальности типа?

Напомним, что проблема возникла именно потому, что мы хотим прогнозировать и контролировать различные роли объектов. Если иг­норировать проблему прогнозирования-контроля, то исчезнет и янус-проблема. Как в Алголе-60 - везде массивы целых, и представ­ляй их себе в любой роли (ведь эти роли - вне программы!).

Полного и изящного решения янус-проблемы Ада не предлагает - этого пока нет ни в одном ЯП. Ближе всего к идеалу - объектно-ориентированные ЯП.

Но можно выделить три основных свойства Ады, направленных на решение янус-проблемы. Каждое из них по-своему корректирует концепцию уникальности, а вместе они образуют практически при­емлемое решение.

Эти средства - ПРОИЗВОДНЫЕ ТИПЫ + ПРЕОБРАЗОВАНИЯ типов + понятие ОБЪЕКТА ДАННЫХ.

Производные типы. Мы уже видели, что объявление производно­го типа указывает родительский тип и определяет, что объекты про­изводного типа могут принимать лишь подмножество значений, до­пустимых для объектов родительского типа. Вместе с тем для объек­тов производного типа можно определить новые операции, неприме­нимые в общем случае к объектам родительского типа. Но ведь это связь старшей и младшей категории в иерархической классифика­ции.

Если, например, определен тип "млекопитающие", то его произ­водным может стать тип "хищные", его производным тип "кошки", его производными типы "сибирские_кошки" и "сиамские_кошки". При этом все уменьшается совокупность допустимых значений (в "хищные" не попадают "коровы", в "кошки" - "собаки", в "сибирские_кошки" - "львы") и добавляются операции и свойства (млеко­питающие - операция "кормить молоком"; хищные - "съедать живо­тное"; кошки - "влезать на дерево"; сибирские кошки - "иметь пушистый хвост"), причем всегда сохраняются операции и свойства всех родительских типов, начиная с так называемого БАЗОВОГО ТИПА, не имеющего родительского типа.

Итак, производные типы решают проблему полиморфизма "свер­ху вниз" - это одновременно частное решение янус-проблемы.

Вопросы. Почему это решение названо решением "сверху вниз"? Сравните с предложенным ранее решением "снизу вверх". Почему это лишь частное решение янус-проблемы?

Подсказка. Для полиморфизма: не полиморфная операция "надстраивается" над независимыми типами, а типы заранее строятся "сверху вниз" так, что операция над объектами "старшего" типа оказывается применимой и к объектам "младшего". Для янус-проблемы: существенно, что при объявлении типа "млекопитающие" нужно знать об атрибутах потенциальных производных типов, иначе негде спрятать "пуши­стый хвост” - подробнее об этом в разделе о наследовании с критикой Ады; вспомните также о пакете модель_человека - там не спасет иерархия типов.

Преобразования типов. Считается, что каждое объявление произ­водного типа неявно вводит и операции ПРЕОБРАЗОВАНИЯ ОБЪ­ЕКТОВ из родительского типа в производный и обратно. При этом перейти от родительского типа к производному можно только при выполнении объявленных ограничений на значения объекта, а об­ратно - всегда. Можно написать процедуру, например "кормить_молоком" для типа "млекопитающие", и применять ее и к "телятам", и к "котятам", и к "львятам". При связывании аргумента с пара­метром выполняется подразумеваемое преобразование типа и проце­дура применяется к аргументу как к "млекопитающему". Но нельзя применять процедуру "влезть_на_дерево" к "корове" - только к "кошке".

Возможны и явно определяемые программистом преобразования типа. В них нет ничего удивительного - это просто функции, аргу­менты которых одного типа, а результаты - другого. Их естественно считать преобразованием типа, если они сохраняют значение объек­та в некотором содержательном смысле. Так, можно написать преобразование из типа "очередь" в тип "стек", сохраняющее содержимое объекта. К результату такого преобразования можно применять опе­рации "втолкнуть", "вытолкнуть", определенные для стеков, а к ар­гументу нельзя. Подчеркнем, что написать преобразование можно далеко не в каждом контексте - нужно иметь возможность "достать" содержимое аргумента и "создать" содержимое результата.

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

Но это значит, что к объектам данных следует относить только сущности, с которыми связан тип (в описании ЯП или в программе). Таковыми в Аде служат, во-первых, изображения предопределенных значений (изображения чисел, символов и логических значений); во-вторых, переменные, постоянные, динамические параметры, вы­ражения. В Аде не относятся к объектам данных процедуры, функ­ции, типы, сегменты, подтипы. Имена у них есть, а типов нет. Их нельзя присваивать и передавать в качестве аргументов процедур и функций - это их основное отличие от объектов данных.

4.2.8. Критерий содержательной полноты ЯП. Неформальные тео­ремы

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

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

С авторской позиции исключительно важен критерий качества, который можно назвать критерием полноты ЯП: автор ЯП должен уметь доказывать существование решения всех известных критич­ных проблем. Другими словами, уметь доказывать неформальную теорему содержательной полноты ЯП по отношению к выбранной ПО. Вполне возможно, что позже будут найдены другие, более удач­ные решения критичных проблем, однако это уже не столь принци­пиально с точки зрения жизнеспособности ЯП.

4.3. Регламентированный доступ и типы данных

Начав с общей потребности прогнозировать и контролировать, мы пришли к общей идее приемлемого решения - к идее уникальности типа, обозначенного определенным именем. Теперь нужно разо­браться со средствами, позволяющими связать с именем типа техно­логически значимые характеристики данных (характер доступа, строение, изменчивость, представление и т. п.).

В соответствии с принципом технологичности эти средства обслу­живают определенные потребности жизненного цикла комплексного программного продукта. Чтобы лучше почувствовать, что это за по­требности, продолжим нашу серию примеров. Ближайшая цель - подробнее рассмотреть средства управления режимом доступа к данным.

4.3.1. Задача моделирования многих сетей

Постановка задачи. Допустим, что пользователям понравился пакет управление_сетью и они заказывают более развитые услуги. Одной сети мало. Нужно сделать так, чтобы пользователи могли со­здать столько сетей, сколько им потребуется, и по отношению к каждой могли воспользоваться любой из уже привычных операций (вставить, связать и т. п.). Вместе с тем больше всего пользователи оценили именно надежность наших программных услуг, гарантию целостности сети. Это важнейшее свойство необходимо сохранить.

Из за большого объема этот материал размещен на нескольких страницах:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24