Программирование >>  Поддержка объектно-ориентированного программирования 

1 ... 99 100 101 [ 102 ] 103 104 105 ... 120


обеспечены значительной поддержкой со стороны транслятора и в общем случае поддаются статическому анализу.

12.2.1 Что представляют классы?

По сути в системе бывают классы двух видов:

[1 ] классы, которые прямо отражают понятия области приложения, т.е. понятия, которые использует конечный пользователь для описания своих задач и возможных решений; и

[2] классы, которые являются продуктом самой реализации, т.е. отражают понятия, используемые разработчиками и программистами для описания способов реализации.

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

[1 ] классы, представляющие пользовательские понятия (например, легковые машины и грузовики),

[2] классы, представляющие обобщения пользовательских понятий (движущиеся средства),

[3] классы, представляющие аппаратные ресурсы (например, класс управления памятью),

[4] классы, представляющие системные ресурсы (например, выходные потоки),

[5] классы, используемые для реализации других классов (например, списки, очереди, блокировщики) и

[6] встроенные типы данных и структуры управления.

В больших системах очень трудно сохранять логическое разделение типов различных классов и поддерживать такое разделение между различными уровнями абстракции. В приведенном выше перечислении представлены три уровня абстракции:

[1 +2] представляет пользовательское отражение системы,

[3+4] представляет машину, на которой будет работать система,

[5+6] представляет низкоуровневое (со стороны языка программирования) отражение реализации.

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

[1 ] атомов,

[2] молекул,

[3] досок и кирпичей,

[4] полов, потолков и стен;

[5] комнат.

Пока удается хранить раздельно представления этих уровней абстракции, можно поддерживать целостное представление о доме. Однако, если смешать их, возникнет бессмыслица. Например, предложение Мой дом состоит из нескольких тысяч фунтов углерода, некоторых сложных полимеров, из 5000 кирпичей, двух ванных комнат и 1 3 потолков - явно абсурдно. Из-за абстрактной природы программ подобное утверждение о какой-либо сложной программной системе далеко не всегда воспринимают как бессмыслицу.

В процессе проектирования выделение понятий из области приложения в класс вовсе не является

31 5



простой механической операцией. Обычно эта задача требует большой проницательности. Заметим, что сами понятия области приложения являются абстракциями. Например, в природе не существуют налогоплательщики , монахи или сотрудники . Эти понятия не что иное, как метки, которыми обозначают бедную личность, чтобы классифицировать ее по отношению к некоторой системе. Часто реальный или воображаемый мир (например, литература, особенно фантастика) служат источником понятий, которые кардинально преобразуются при переводе их в классы. Так, экран моего компьютера (Маккинтош) совсем не походит на поверхность моего стола, хотя компьютер создавался с целью реализовать понятие настольный , а окна на моем дисплее имеют самое отдаленное отношение к приспособлениям для презентации чертежей в моей комнате. Я бы не вынес такого беспорядка у себя на экране.

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

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

12.2.2 Иерархии классов

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

движущееся средство легковая машина аварийное средство грузовая машина полицейская машина машина скорой помощи пожарная машина машина с выдвижной лестницей

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

На С++ это можно задать так:

class Vehicle { /*...*/ }; class Emergency { /* */ }; class Car : public Vehicle { /*...*/ }; class Truck : public Vehicle { /*...*/ }; class Police car : public Car , public Emergency {



...

class Ambulance : public Car , public Emergency { ...

class Fire engine : public Truck , Emergency { ...

class Hook and ladder : public Fire engine { ...

Наследование - это отношение самого высокого порядка, которое прямо представляется в С++ и используется преимущественно на ранних этапах проектирования. Часто возникает проблема выбора: использовать наследование для представления отношения или предпочесть ему принадлежность. Рассмотрим другое определение понятия аварийного средства: движущееся средство считается аварийным, если оно несет соответствующий световой сигнал. Это позволит упростить иерархию классов, заменив класс Emergency на член класса Vehicle:

движущееся средство (Vehicle {eptr}) легковая машина (Car) грузовая машина (Truck) полицейская машина (Police car) машина скорой помощи (Ambulance) пожарная машина (Fire engine)

машина с выдвижной лестницей (Hook and ladder)

Теперь класс Emergency используется просто как член в тех классах, которые представляют аварийные движущиеся средства:

class Emergency { /*...*/ };

class Vehicle { public: Emergency* eptr; /*...*/ }; class Car : public Vehicle { /*...*/ }; class Truck : public Vehicle { /*...*/ }; class Police car : public Car { /*...*/ }; class Ambulance : public Car { /*...*/ }; class Fire engine : public Truck { /*...*/ }; class Hook and ladder : public Fire engine { /*...*/ };

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

Car::Car() конструктор Car

eptr = 0;

Police car::Police car() конструктор Police car

eptr = new Emergency;

Такие определения упрощают преобразование аварийного средства в обычное и наоборот:

void f(Vehicle* p)

delete p->eptr;

p->eptr = 0; больше нет аварийного движущегося средства ...

p->eptr = new Emergency; оно появилось снова

Так какой же вариант иерархии классов лучше? В общем случае ответ такой: Лучшей является



1 ... 99 100 101 [ 102 ] 103 104 105 ... 120

© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки.
Яндекс.Метрика