Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Скачиваний:
46
Добавлен:
16.04.2013
Размер:
111.1 Кб
Скачать

Эти цифры наводят на размышления о сложности языка. Согласно приведен-

ным данным, Modula-2 немного сложнее, чем ее предок, Pascal; оба и стоят на одном уровне с языком С. C++ задумывался как расширение С и сопровождался руковод­ством в стиле С. C++ v3.0 добавил девятнадцать ключевых слов к имевшимся в тра­диционном С, что увеличило его на две трети. Справочное руководство по C++ v3.0 содержало 155 страниц, в четыре раза больше, чем руководство по С. Эти две оценки наводят на мысль, что C++ v3.0 значительно сложнее, чем С. Проект стандарта ANSI C++ 1995 составлял примерно 650 страниц. Это опять в четыре раза больше по срав­нению с 1990 годом. И хотя этот документ включает большое количество дополни­тельного материала по улучшенной стандартной библиотеке, его объем все же отра­жает большую сложность языка. Более того, многие конструкции C++ независимы, так что их взаимодействие значительно влияет на сложность.1

Подтверждающий пример можно найти на стр. 306 книги Эллис (Ellis) и Страус-трупа (Stroustrup) “С+ + Annotated Reference Manual”1, где для показа различных ва­риантов и свойств разных типов функций используется таблица размером 13 на 5. Пятью характеристиками функции являются: наследуемость, виртуальность, воз­вращаемый тип, дружественная ли это функция или функция-член и генерируемость по умолчанию. Например, конструкторы, деструкторы и функции преобразования не могут объявлять свой возвращаемый тип, a new() и delete () должны объяв­лять, причем void* и void соответственно. С, в действительности, имеет лишь одну форму семантики функции. Такое увеличение в шестьдесят пять раз внушает благо­говейный трепет. И хотя в этой таблице присутствует стройность и многие характе­ристики могут быть получены из концептуального понимания модели языка, авторы, тем не менее, считают целесообразным перечислить различия и свойства разных ти­пов функций.

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

Успех ООП на C++

Объектно-ориентированное программирование на C++ завоевало ослепительный успех, несмотря на известные недостатки. Причина в том, что C++ привнес в про­мышленность приемлемую технологию ООП. Он основан на существующем, удач­ном и широко используемом языке. Он позволяет писать переносимый, компактный и эффективный код. Сохранена безопасность типов, а расширяемость типов является всеобъемлющей. C++ сосуществует с другими распространенными языками и не предъявляет особых требований к системе.

С изначально создавался как язык для разработки операционных систем, и как таковой позволяет писать код, который легко транслируется для эффективного ис­пользования машинных ресурсов. Благодаря этой эффективности программные про­дукты приобретают существенные преимущества. Поэтому, несмотря на жалобы на то, что традиционный С не является безопасным и надежным языком, С развивался в своей сфере применения. Сообщество программистов на С использовало структур­ное программирование и АТД-расширения на основе специальных соглашений и правил. ООП вторглось вэто профессиональное сообщество лишь когда оно (ООП) «поженилось» с языком С в рамках концепции, которая поддерживала и традицион­ную точку зрения С, и преимущества ООП. Ключом к популярности C++ было пони­мание того, что наследование и полиморфизм дают важные дополнительные преиму­щества по сравнению с традиционной практикой программирования. Полиморфизм в C++ позволяет клиенту использовать АТД как черный ящик. Успех ООП характе­ризуется той степенью, в которой пользовательский тип может быть сделан неотли­чимым от собственного типа языка. Полиморфизм позволяет задать приведения ти­пов, которые интегрируют АТД с собственными типами. Он разрешает объектам из иерархии подтипов динамически реагировать на вызов функции; в этом заключается принцип сообщений в ООП. Полиморфизм также упрощает клиентский протокол, а «размножение» имен управляется перегрузкой функций и операторов. Наличие всех четырех форм полиморфизма вдохновляет программиста на проектирование с уче­том инкапсуляции и сокрытия данных.

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

ООП = расширяемость типов + полиморфизм.

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

Платонизм: проектирование «tabula rasa»

C++ дает программисту инструмент для реализации объектно-ориентированного проекта. Но как вы разрабатываете такой проект? Не бывает простой методики tabula rasa1, то есть проекта «на ровном месте». Любой проект всегда должен быть тесно привязан к предметной области и отражать ее абстракции. Раскрыть эти абст­ракции позволяет философия проектирования, которую мы называем платонизмом (platonism).

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

Вселенной. Такой стул может быть подкатегорией другого идеала — мебели. Стул может иметь подкатегории, та­кие как вращающийся стул, шезлонг, стул с подлокотниками, кресло-качалка и тому подобное. Для толкового описания стула, возможно, потребуется устроить эксперти­зу по стульям, с привлечением производителей и пользователей стульев с целью прийти к соглашению о сути и природе «стульности». Платонический стул должен легко модифицироваться для описания наиболее часто встречающихся стульев. Пла­тонический стул следует описывать в терминах, согласовывающихся с существую­щей «стульной» терминологией. На C++ оказал влияние Simula 67 — язык, специально разработанный для моделиро­вания. Платоническая парадигма является моделированием или симуляцией конк­ретного мира. Она несет в себе дополнительные возможности по формированию объектно-ориентированного проекта программного продукта. Объектно-ориентиро­ванное проектирование обычно предоставляет открытый интерфейс, который явля­ется удобным, обобщенным и эффективным. Эти важные моменты могут вступать в противоречия. Еще раз отметим, что нет простых правил, позволяющих разрешить подобные конфликты.

Дополнительные возможности могут оказаться очень выгодными и существенно перекроют увеличение стоимости начального проекта. Первое и главное: они налага­ют дополнительный уровень дисциплины на процесс программирования. Усиление такой дисциплины всегда приносит дивиденды. Второе: идеологически связанные Друг с другом, наполненные смыслом куски кода инкапсулируются в классы. Инкапсу­ляция и декомпозиция (разбиение на составляющие) тоже всегда приносили свои пло­ды. Третье: совершенствуется повторное использование кода с помощью наследования и АТД. Повторное использование кода всегда выгодно. Четвертое: улучшается прото-типирование посредством откладывания выбора реализации и предоставления досту­па к большим и удобным общим библиотекам. Дешевые прототипы всегда окупаются.

Платоническая парадигма применения техники ООП проводит бархатную рево­люцию во всем процессе программирования. Она не вытесняет более раннюю техни­ку, например приемы структурного программирования, используя их для програм­мирования «в малом», чтобы потом эффективнее управлять композицией большие программных проектов.

Принципы проектирования

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

Сообщество разработчиков обладает обширным опытом по использованию стан­дартных контейнерных классов. Приемлемые соглашения существуют в отношении поведения стека, ассоциативного массива, двоичного дерева, очереди. Кроме того, у сообщества программистов есть много примеров специализированных языков про­граммирования, ориентированных на конкретную проблемную область. Например, язык SNOBOL и выросший из него язык ICON имеют очень богатые возможности для обработки строк. Подобные возможности могут быть собраны в виде АТД в C++.

Полезным принципом проектирования служит принцип «лезвия Оккамы1» (Occam's razor). Этот принцип утверждает, что не следует вводить новые сущности сверх необходимости — или сверх законченности, обратимости, ортогональности, со­гласованности, простоты, эффективности и выразительности. Подобные идеалы мо­гут конфликтовать, что часто приводит к неизбежной «торговле» между ними, когда дело доходит до реализации проекта.

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

Законченность (completeness) лучше всего рассматривать на примере булевой алгеб­ры, где единственной операции «и-не» достаточно для конструирования всех возмож­ных булевских выражений. Но при изучении булевой алгебры в качестве основных опе­раций обычно рассматриваются отрицание, логическое умножение (конъюнкция) и логическое сложение (дизъюнкция). Самой по себе законченности недостаточно для того, чтобы судить о проекте. Больший набор операторов часто и более выразителен.

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

Иерархичность (hierarchy) достигается с помощью наследования. Проекты долж­ны быть иерархичны, что является отражением двух принципов — декомпозиции и локализации. Оба они служат для сокрытия деталей — ключевой идеи в борьбе со сложностью. Однако при подобном проектировании существует серьезная проблема. Какой детализации достаточно для того, чтобы сделать сущность пригодной в каче­стве класса? Важно избежать разрастания излишне детализированных сущностей. Избыточные детали слишком усложняют управление проектом классов.

Схемы, диаграммы и инструменты

Процесс проектирования можно облегчить с помощью диаграмм. Существует не­сколько систем обозначений (нотаций) для объектно-ориентированного проектиро­вания. Некоторые из них используются автоматизированными системами разработ­ки программ (computer assisted software engineering — CASE-средства). Опишем две схемы, которые мы находим полезными. Первая из них — это CRC-карточки (Budd, 1991), а вторая — диаграммы Вассермана-Пирчера, (Wasserman et. al., 1990; в каче­стве альтернативы — Booch, 1995).

CRC означает класс, ответственность, сотрудничество (class, responsibility, collaboration). Ответственность — это обязательства, которые должен выполнять класс. Например, объекты комплексных чисел должны предоставлять реализацию комплексной арифметики. Сотрудник (collaborator) — это другой объект, который взаимодействует с данным для обеспечения некоего общего набора поведений. На­пример, целые и действительные числа сотрудничают с комплексными для предо­ставления исчерпывающего набора математических поведений.

CRC-карточки используются для проектирования заданного класса. Сначала описываются ответственность класса и его сотрудники. Оборотная сторона карточки применяется для описания деталей реализации. Лицевая сторона карточки соответ­ствует открытому поведению.

CRC-карточка

Соседние файлы в папке Тельминов (мб)