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

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

Эволюция заключает в себе и опасность, и возможность бушт кода, наегошо гро-

приближения к совершенству. При необходимости измене- мозкого, тощтш или

ния кода старайтесь улучшить его, чтобы облегчить внесе- шшнт.нШытштШт

ние изменений в будущем. В процессе написания програм- ШШ тротщтт.

мы вы всегда узнаете о ней что-то новое. Получив возмож- Джералм Вайнберг

ность изменения программы, используйте то, что вы узна- {ШгвШ ШеШгд) ли, для ее улучшения. Пишите первоначальный код и его изменяйте, держа в уме дальнейшие изменения.

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

24.2. Введение в рефакторинг

Важнейшей стратегией достижения цели Главного Правила Эволюции ПО является рефакторинг, который Мартин Фаулер определяет как «изменение внутренней структуры ПО без изменения его наблюдаемого поведения, призванное облегчить его понимание и удешевить модификацию» (Fowler, 1999). Слово «рефакторинг» возникло из слова «факторинг», которое изначально использовал в контексте структурного программирования Ларри Константайн, назвавший так максимально возможную декомпозицию программы на составляющие части (Yourdon and Constantine, 1979).

Разумные причины выполнения рефакторинга

Иногда код деградирует при сопровождении, а иногда он изначально имеет невысокое качество. В обоих случаях на это - и на необходимость рефакторинга - указывают некоторые предупреждающие знаки, иногда называемые «запахами» (smells) (Fowler, 1999). Они описаны ниже.

Код повторяется Повторение кода почти всегда говорит о неполной факторизации системы на этапе проектирования. Повторение кода заставляет параллельно изменять сразу несколько фрагментов программы и нарушает правило, которое Эндрю Хант и Дэйв Томас назвали «принципом DRY»: Dont Repeat Yourself (не повторяйтесь) (Hunt and Thomas, 2000). Думаю, лучше всех это правило сформулировал Дэвид Парнас: «Копирование и вставка кода - следствие ошибки проектирования» (McConnell, 1998b).

Метод слишком велик В объектно-ориентированном программировании методы, не умещающиеся на экране монитора, требуются редко и обычно свидетельствуют о попытке насильно втиснуть ногу структурного программирования в объектно-ориентированный ботинок.

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

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



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

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

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

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

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

Отдельные части класса изменяются независимо от других частей

Иногда класс имеет две (или более) разных области ответственности. Если это так, вы заметите, что вы изменяете или одну часть класса, или другую, и лишь немногие изменения затрагивают обе части класса. Это признак того, что класс следует разделить на несколько классов в соответствии с отдельными областями ответственности.

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

Вам приходится параллельно изменять несколько иерархий наследования

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

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

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



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

Элементарный тип данных перегружен Элементарные типы данных могут представлять бесконечное число сущностей реального мира. Если вы собираетесь представить распространенную сущность - скажем, денежную сумму - целочисленным или другим элементарным типом данных, подумайте: не создать ли вместо этого простой класс Money, чтобы компилятор мог выполнять контроль типов объектов Money, дабы можно было проверять значения, присваиваемые этим объектам, и т. д. Если и Money, и Temperature будут представлены целыми числами, компилятор не сможет предупредить вас об ошибочных операциях вида bank-Balance = recordLowTemperature.

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

По цепи методов передаются бродячие данные Данные, передаваемые в метод лишь затем, чтобы он передал их другому методу, называются «бродячими» (tramp data) (Page-Jones, 1988). Это не всегда плохо, но в любом случае спросите себя, согласуется ли передача конкретных данных с абстракцией, формируемой интерфейсом каждого из методов. Если с абстракциями интерфейсов порядок, с передачей данных тоже все в норме. Если нет, найдите способ, позволяющий улучшить согласованность интерфейса каждого метода.

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

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

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

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

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



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