Например, в одной и той же области действия можно объявить две функции:
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 |


