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

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

• Абстракция, формируемая этими методами, относится к уровню «етр1оуее» (сотрудник).

void AddEmployee( Employee employee ); void RemoveEmployee( Employee employee );

Абстракция, формируемая этими методами, относится к уровню «list» (список).

Employee NextItemInList(); Employee Firstltem(); Employee LastltemO;

private:

Этот класс представляет два АТД: Employee и ListContainer (список-контейнер). Подобные смешанные абстракции часто возникают, когда программист реализует класс при помощи класса-контейнера или других библиотечных классов и не скрывает этот факт. Спросите себя, должна ли информация об использовании класса-контейнера быть частью абстракции. Обычно это является деталью реализации, которую следует скрыть от остальных частей программы, например так:

Пример интерфейса, формирующего согласованную абстракцию (С++)

class EmployeeCensus { public:

открытые методы

- Абстракция, формируемая всеми этими методами, теперь относится к уровню «enfipioyee».

void AddEmployee( Employee employee ); void RemoveEmployee( Employee employee ); Employee NextEmployeeO; Employee FirstEmployeeO; Employee LastEmployeeO;

private:

Tot факт, что класс использует библиотеку ListContainer, теперь скрыт.

> ListContainer m EmployeeList;

Программисты могут утверждать, что наследование от ListContainer уц,о6но, потому что оно поддерживает полиморфизм, позволяя создать внешний метод поиска или сортировки, принимающий объект ListContainer. Но этот аргумент не проходит главный тест на уместность наследования: «Используется ли наследование только для моделирования отношения „является"?» Наследование кягссг EmployeeCensus (каталог личных дел сотрудников) от класса ListContainer означало бы, что Employee-Census «является» ListContainer, что, очевидно, неверно. Если абстракция объекта EmployeeCensus заключается в том, что он поддерживает поиск или сортировку.



эти возможности должны быть явными согласованными частями интерфейса класса.

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

Убедитесь, что вы понимаете, реализацией какой абстракции является класс Некоторые классы очень похожи, поэтому при разработке класса нужно понимать, какую абстракцию должен представлять его интерфейс. Однажды я работал над программой, которая должна была поддерживать редактирование информации в табличном формате. Сначала мы хотели использовать простой элемент управления «grid» (сетка), но доступные элементы управления этого типа не позволяли закрашивать ячейки ввода данных в другой цвет, поэтому мы выбрали элемент управления «spreadsheet» (электронная таблица), который такую возможность поддерживал.

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

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

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

Предоставляйте методы вместе с противоположными им методами

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



Убирайте постороннюю информацию в другие классы Иногда вы будете обнаруживать, что одни методы класса работают с одной половиной данных, а другие - с другой. Это значит, что вы имеете дело с двумя классами, скрывающимися под маской одного. Разделите их!

По мере возможности делайте интерфейсы программными, а не семантическими Каждый интерфейс состоит из программной и семантической частей. Первая включает типы данных и другие атрибуты интерфейса, которые могуг быть проверены компилятором. Вторая складывается из предположений об использовании интерфейса, которые компилятор проверить не может. Семантический интерфейс может включать такие соображения, как «Метод А должен быть вызван перед Методом В» или «Метод А вызовет ошибку, если переданный в него Элемент Данных 1 не будет перед этим инициализирован». Семантический интерфейс следует документировать в комментариях, но вообще интерфейсы должны как можно меньше зависеть от документации. Любой аспект интерфейса, который не может быть проверен компилятором, является потенциальным источником ошибок. Старайтесь преобразовывать семантические элементы интерфейса в программные, используя утверждения (assertions) или иными способами.

Опасайтесь нарушения целостности интерфейса при

жанки качшеа кода при аго изменении класса При модификации и расширении клас-йзмвненйй см. главу 24. са часто обнаруживается дополнительная нужная функци-

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

Пример интерфейса, изуродованного при сопровождении программы (С++)

class Employee { public:

открытые методы FullName GetNameO const; Address GetAddressO const; PhoneNumber GetWorkPhoneO const;

bool IsJobClassificationValid( JobClassification jobClass ); bool IsZipCodeValid( Address address ); bool IsPhoneNumberValid( PhoneNumber phoneNumber );

SqlQuery GetQueryToCreateNewEmployee() const; SqlQuery GetQueryToModifyEmployee() const; SqlQuery GetQueryToRetrieveEmployee() const;

private:

To, что начиналось как ясная абстракция, превратилось в смесь почти несогласованных методов. Между сотрудниками и методами, проверяющими корректность



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