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

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


Рис, 5-9 Хороший интерфейс класса похож на верхушку айсберга: большую часть класса он оставляет скрытой

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

Пример сокрытия информации

Допустим, вы пишете программу, каждый объект которой должен иметь уникальный идентификатор, хранящийся в переменной-члене id. Один подход к проектированию может заключаться в применении целочисленных идентификаторов и хранении максимального на данный момент идентификатора в глобальной переменной gmaxid. При создании новых объектов вы можете - скажем, в конструкторе каждого объекта - просто выполнять команду id = +-g maxld, что гарантирует уникальность идентификаторов, и требует абсолютно минимального кода при создании каждого объекта. Разве это может привести к каким-нибудь неприятностям?

Может. Что, если вы захотите зарезервировать диапазоны идентификаторов для определенных целей? Что, если для повышения защищенности программы вы захотите назначать идентификаторы в другом порядке? А если вы захотите повторно задействовать идентификаторы уничтоженных объектов? Или включить в программу диагностический тест, проверяющий, не превысило ли число идентификаторов допустимый предел? Если, назначая идентификаторы, вы распространите команды id = ++g maxld по всей программе, вам придется изменить каждую из них. Кроме того, этот подход небезопасен в многопоточной среде.

Способ генерации новых идентификаторов является тем аспектом проектирования, который следует скрыть. Применив команду ++g maxld, вы раскроете сведения о том, что новый идентификатор создается просто путем увеличения переменной gmaxid. Если же вместо этого вы используете команды id = NewIdQ, вы



скроете информацию о способе создания новых идентификаторов. Сам метод NewIdQ может состоять из единственной строки return (++g maxld) или ее эквивалента, однако, если вы позднее решите зарезервировать определенные диапазоны идентификаторов для специфических целей или повторно использовать старые идентификаторы, вам придется изменить только метод NewIdQ, но не десятки команд id = NewIdQ- Какими бы сложными ни были изменения метода NewIdQ, они не повлияют ни на какую другую часть программы.

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

Итак, тип идентификатора - это тоже секрет, который следует скрыть. Показывая, что идентификаторы - целые числа, вы поощряете программистов выполнять над ними такие операции, как >, < и =. Программируя на С++, вы могли бы не объявлять идентификаторы как int, г назначить им при помощи директивы typedef попъъо-вательский тип IdType соответствующий тому же int. Или же вы могли бы создать простой класс IdType. Повторю еще раз: сокрытие аспектов проектирования позволяет значительно уменьшить объем кода, затрагиваемого изменениями.

Сокрытие информации полезно на всех уровнях проектирования: от применения именованных констант вместо литералов до создания типов данных и проектирования классов, методов и подсистем.

Две категории секретов

Связанные с сокрытием информации секреты относятся к двум общим категориям:

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

секреты, которые скрывают источники изменений с целью локализации результатов возможных изменений.

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

Барьеры, препятствующие сокрытию информации

В некоторых случаях скрыть информацию невозможно,

однако большинство барьеров, препятствующих сокрытию йьйые фрагменты шго раз-информации, является умственными и обусловлены привы- дц jy 1кз оутьи «De$(gnmg канием к другим методикам. Software for Ease of extension

Избыточное распространение информации Зачастую <гшт (*amas, 1079) сокрытию информации препятствует избыточное распространение информации по системе. Так, жесткое кодирование литерала 100 во многих местах программы децентрализует ссылки на него. Лучше скрыть эту информацию в одном месте - скажем, при помощи константы MAXEMPLOYEES, для изменения значения которой придется изменить только одну строку кода.



Еще один пример избыточного распространения информации - распределение по системе кода взаимодействия с пользователями. При этом в случае изменения способа взаимодействия - например, при замене графического интерфейса на интерфейс командной строки - придется изменить почти весь код. Лучше сконцентрировать взаимодействие с пользователями в одном классе (пакете, подсистеме), который можно было бы изменить, не влияя на всю систему.

В качестве другого примера приведу повсеместное исполь-

сешха О доступе к глобальным данным при зование глобального элемента данных, такого как массив

помощи интерфейсов классов данных о сотрудниках, поддерживающий до 1000 элемен-см. подраздел «Используйте тов. Если программа будет обращаться к глобальным дан-методы доступа вместо глобаль- ным напрямую, информация о реализации элемента данных ных данных» раздела 13.3. скажем, то, что это массив, способный включать до 1000

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

Круговая зависимость Более тонким барьером, мешающим сокрытию информации, является круговая зависимость, когда, например, метод класса А вызывает метод класса В, а метод класса В - метод класса А.

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

Ошибочное представление о данных класса как о глобальных данных

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

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

Конечно, это предполагает, что система включает фамотно спроектированные небольшие классы. Если профамма использует огромные классы, включающие десятки методов, различие между данными класса и глобальными данными стирается, и данные класса приобретают многие недостатки, характерные для глобальных данных.

Шря9%т tmmt О новы Кажущееся снижение производительности Наконец, ттт производительности на С)тказ от сокрытия информации может объясняться стрем-уровнв кода см, главы 25 и Е6. лением избежать снижения производительности и на уровне

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



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.0021