Главная - Литература

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 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294

Не делайте метод открытым лишь потому, что он использует только открытые методы То, что метод использует только открытые методы, не играет особой роли. Лучше спросите себя, согласуется ли предоставление доступа к данному методу с абстракцией, формируемой интерфейсом.

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

Очень, очень настороженно относитесь к семанти-

Если для понимания того, что

происходит, нужно жь ре- «" нарушениям инкапсуляции Когда-то мне каза-алйзамию, это не абстракция. лось, что, научившись избегать синтаксических ошибок, я Ф. Дж. Плоджер обрету покой. Но вскоре я обнаружил, что это просто от-(R РШидег) крыло передо мной дверь в мир совершенно новых ошибок, большинство которых диагностировать и исправлять сложнее, чем синтаксические.

Аналогичные отношения имеют место между синтаксической и семантической инкапсуляцией. С точки зрения синтаксиса, не совать нос во внутренние дела другого класса относительно легко: достаточно просто объявить его внутренние методы и данные закрытыми. Достичь семантической инкапсуляции гораздо сложнее. Вот несколько примеров того, как вы можете нарушить инкапсуляцию семантически. Вы можете:

решить не вызывать метод InitializeOperationsQ Класса А, потому что метод РефгтРтЮрегаНопО Класса А вызывает его автоматически;

не вызвать метод database.ConnectQ перед вызовом метода employee Retrieve ( database ), потому что знаете, что при отсутствии соединения с БД метод employeeRetrieveQ его установит;

не вызвать метод Terminate() Класса А, так как знаете, что метод PerformFinal-OperationQ Класса А уже вызвал его;

использовать указатель или ссылку на Объект В, созданный Объектом А, даже после выхода Объекта А из области видимости, потому что знаете, что Объект А хранит Объект В в статическом хранилище, вследствие чего Объект В все еще будет корректным;

использовать константу MAXIMUM ELEMENTS Класса В вместо константы MAXI-MUMELEMENTS Класса А, потому что знаете, что они имеют одинаковые значения.

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



сквозь интерфейс разрушает инкапсуляцию, а вскоре к ней присоединяется и абстракция.

Если исключительно по документации интерфейса разобраться с использованием класса не удается, изучение реализации класса по исходному коду не будет грамотным решением. Это хорошая инициатива, но плохое решение. Вы поступите правильно, если свяжетесь с автором класса и скажете ему: «Я не могу понять, как использовать этот класс». Автор класса поступит правильно, если не ответит вам, а изучит файл интерфейса, изменит соответствующую документацию, зарегистрирует файл в общих исходных кодах проекта и скажет: «Посмотрите, поймете ли вы работу класса сейчас». Желательно, чтобы этот диалог происходил в самом коде интерфейса: так он будет сохранен для будущих программистов. Если диалог будет происходить исключительно в вашем уме, это внесет тонкие семантические зависимости в код клиентов класса. Если же он будет межличностным, выгоду сможете извлечь только вы, и больше никто - это некрасиво.

Остерегайтесь слитком жесткого сопряжения «Сопряжение» (coupling) характеризует силу связи между двумя классами. Как правило, чем сопряжение слабее, тем лучше. Из этого можно вывести несколько общих правил:

минимизируйте доступность классов и их членов;

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

делайте данные базового класса закрытыми, а не защищенными: это ослабляет сопряжение производных классов с базовым;

не включайте данные-члены в открытый интерфейс класса;

остерегайтесь семантических нарушений инкапсуляции;

соблюдайте «Правило Деметры» (см. раздел 6.3).

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

6.3. Вопросы проектирования и реализации

Для создания высококачественной программы недостаточно определить удачные интерфейсы классов - не менее важно грамотно спроектировать и реализовать внутреннее устройство классов. В этом разделе мы обсудим вопросы, связанные с включением, наследованием, методами/данными-членами, сопряжением классов, конструкторами, а также объектами-значениями и объектами-ссылками.

Включение (отношение «содержит»)

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



Реализуйте с помощью включения отношение <содержит Включение можно рассматривать как отношение «содержит». Например, объект «сотрудник» может «содержать» фамилию, номер телефона, идентификационный номер налогоплательщика и т. д. Это отношение можно реализовать, сделав фамилию, номер телефона и номер налогоплательщика данными-членами класса Employee.

В самом крайнем случае реализуйте отношение <содержит при помощи закрытого наследования Иногда включение не получается реализовать, делая один объект членом другого. Некоторые эксперты советуют при этом выполнять закрытое наследование класса-контейнера от класса, который должен в нем содержаться (Meyers, 1998; Sutter, 2000). Главным мотивом такого решения является предоставление классу-контейнеру доступа к защищенным методам/данным-членам содержащегося в нем класса. На практике этот подход устанавливает слишком близкие отношения между дочерним и родительским классом, нарушая инкапсуляцию. Обычно это указывает на ошибки проектирования, которые следует решить иначе, не прибегая к закрытому наследованию.

Настороженно относитесь к классам, содержащим более семи элементов данных-членов При выполнении других заданий человек может удерживать в памяти 7±2 дискретных элементов (Miller, 1956). Если класс содержит более семи элементов данных-членов, подумайте, не разделить ли его на несколько менее крупных классов (Riel, 1996). Можете ориентироваться на верхнюю границу диапазона «7±2», если данные-члены являются примитивными типами, такими как целые числа и строки, и на нижнюю, если они являются сложными объектами.

Наследование (отношение «является»)

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

Планируя использовать наследование, вы должны принять несколько решений.

Будет ли конкретный метод-член доступен производным классам? Будет ли он иметь реализацию по умолчанию? Можно ли будет переопределить его реализацию по умолчанию?

Будут ли конкретные данные-члены (в том числе переменные, именованные константы, перечисления и т. д.) доступны производным классам?

Ниже аспекты этих решений обсуждаются подробнее.

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

Самое еажйое пртт обьакт- „

но-ориеитировашого програм- отношение яаляется Если программист решает создать

мйроваийя на С такоао: от- новый класс путем наследования его от существующего

крытое наследование оаначает класса, он по сути говорит, что новый класс «является» бо-

«является». Запомните m лее специализированной версией существующего класса.

Стп Шй0рс

(Scott Meyers)



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 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294



0.0142