Анимация
JavaScript
|
Главная Библионтека Объединение позволяет методам контейнера действовать подобно фильтру, через который передаются сообщения, предназначенные для вложенного объекта. Обработчики сообщений часто будут иметь одинаковые имена в контейнере и во вложенном объекте. Например: class string строка ... public: const string &operator=( const string &r ); class numeric string строка, содержащая число string str; ... public: const string &operator=( const string &r ); const string &numeric string::operator=( const string &r ) if( r.all characters are digits() ) все символы - цифры str=r; else throw invalid assignment(); return *this; Это на самом деле довольно слабый пример объединения, потому что, если бы функция operator=() была виртуальной в базовом классе, то объект numeric string мог бы наследовать от string и заместить оператор присваивания для проверки на верное числовое значение. С другой стороны, если перегруженная операция сложения + в классе string выполняет конкатенацию, то вам может понадобиться перегрузить + в классе numeric string для выполнения арифметического сложения (т.е. преобразовывать строки в числа, которые складывать и затем присваивать результат строке). Объединение в последнем случае решило бы немного проблем. Возвращаясь к наследованию, отметим, что объекты классов, показанных в таблице 3, вероятно будут размещаться в памяти одинаково. Каждое из этих определений, как вы заметили, имеет компонент some cls , но доступ к этому компоненту требует совершенно разных процедур и механизмов. В этой книге я использую выражение "компонент базового класса" по отношению к той части объекта, которая определена на уровне базового класса, а не к вложенному объекту. То есть, когда я
98. Сначала проектируйте объекты Первым пунктом повестки дня всегда должно быть проектирование системы обмена сообщениями, обычно посредством диаграмм объектов типа описанных Бучем. Начиная с иерархии классов, вы проявляете склонность к избыточному проектированию, реализуя возможности, которые не нужны. Кроме того, не зная, как нужно связать объекты друг с другом, обычно трудно сказать заранее, какие возможности потребуются в каждом классе. Тяжело обобщать, когда у вас нет деталей. 99. Затем проектируйте иерархию снизу вверх После того, как вы спроектировали систему объектов и сообщений, вы можете приступать к иерархии. Откиньтесь на спинку кресла и взгляните на различные объекты, и вы увидите, что многие из них получают похожие сообщения. Если два сообщения, посылаемые к разным объектам, похожи, но не одинаковы, то вам может подойти слегка более общее компромиссное, которое сможет работать и в том, и в другом месте. Обработчики для всех общих сообщений должны быть сконцентрированы в единый базовый класс. Например, имея один объект, получающий сообщения A, B и C, и второй объект, получающий A, B, D и E, вы должны остановиться на маленькой иерархии классов, в которой базовый класс реализует обработчики сообщений для A и B, один производный класс реализует обработчик для C, а второй производный класс - обработчики для D и E. Вы продолжаете этот процесс соединения общих элементов в общие базовые классы до тех пор, пока нечего будет соединять. Теперь у вас есть иерархия базовых классов. Вы заметите, что, чем более общим является класс, тем выше он говорю, что объект производного класса имеет "компонент базового класса", то имею в виду, что некоторые из его полей и обработчиков сообщений определены на уровне базового класса. При рассмотрении вложенного объекта я буду называть его "полем" или "вложенным объектом". Таблица 3. Два определения класса, одинаково представляемые на уровне машинного кода расположен в иерархии. Например, класс управляющего manager вероятно имеет все свойства класса обобщенного служащего employee, а также несколько дополнительных свойств (таких, как список подчиненных служащих). Тогда для manager имеет смысл наследовать от employee, потому что это добавит возможности, отсутствующие в базовом классе employee. На этом этапе процесса проектирования вы все еще даже не подумали 0 том, что же должно быть внутри объектов. Вы по-прежнему имеете дело только с системой обмена сообщениями. Последним шагом на этом этапе проектирования - после того, как вы сделали эскиз проекта иерархии классов - остается запись определений классов. Вы добавите открытые (public) функции-члены для каждого сообщения, получаемого объектом. Эти обработчики сообщений являются единственными открытыми членами вашего определения класса. Все остальное должно быть закрытым или защищенным. Подробнее об этом далее. 99.1. Базовые классы должны иметь более одного производного объекта Это просто другая точка зрения на предыдущее правило. Если базовый класс является способом концентрации сходных свойств в одном месте, то есть смысл в том, чтобы никогда не иметь всего один производный класс. Если он у вас один, то возможности этого единственного потомка должны быть переданы родителю. 1 00. Возможности, определенные в базовом классе, должны использоваться всеми производными классами 101. Си++ - это не Smalltalk: избегайте общего класса object Процесс разработки иерархии снизу вверх обычно дает вам лес из маленьких деревьев, скорее широких, чем высоких. Построение иерархии снизу вверх поможет вам избежать общей проблемы для иерархий классов Си++: класса object, от которого наследуется все в системе, как в Smalltalk. Такой проект хорош для Smalltalk, но, как правило, не работает в Си++. Какое свойство мог бы реализовывать этот общий object? То есть, какое свойство должен иметь каждый объект каждого класса в вашей программе? Единственное, что приходит на ум, это - 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 [ 44 ] 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |