Главная страница
Навигация по странице:

  • Правило бойскаута

  • Предыстория и принципы

  • Содержательные имена

  • Избегайте дезинформации

  • Используйте осмысленные различия

  • Создание, анализ ирефакторинг


    Скачать 3.16 Mb.
    НазваниеСоздание, анализ ирефакторинг
    Дата29.09.2022
    Размер3.16 Mb.
    Формат файлаpdf
    Имя файлаChistyj_kod_-_Sozdanie_analiz_i_refaktoring_(2013).pdf
    ТипКнига
    #706087
    страница6 из 49
    1   2   3   4   5   6   7   8   9   ...   49
    35
    школы мысли
    А как насчет меня (Дядюшка Боб)? Что я думаю по поводу чистого кода? Эта книга расскажет вам во всех подробностях, что я и мои соратники думаем о чи- стом коде . Вы узнаете, как, по нашему мнению, должно выглядит чистое имя переменной, чистая функция, чистый класс и т . д . Мы излагаем свои мнения в виде беспрекословных истин и не из- виняемся за свою категоричность . Для нас, на данном моменте наших карьер, они являются бес- прекословными истинами . Они составляют нашу
    школу мысли в области чистого кода .
    Мастера боевых искусств не достигли единого мнения по поводу того, какой из видов едино- борств является лучшим, а какие приемы — самы- ми эффективными . Часто ведущие мастера созда- ют собственную школу и набирают учеников . Так появилась школа дзю-дзюцу Грейси, основанная семьей Грейси в Бразилии . Так появилась школа дзю-дзюцу Хаккорю, основанная
    Окуямой Рюхо в Токио . Так появилась школа Джит Кун-до, основанная Брюсом
    Ли в Соединенных Штатах .
    Ученики этих разных школ погружаются в учение основателя школы . Они по- свящают себя изучению того, чему учит конкретный мастер, часто отказываясь от учений других мастеров . Позднее, когда уровень их мастерства возрастет, они могут стать учениками другого мастера, чтобы расширить свои познания и прове- рить их на практике . Некоторые переходят к совершенствованию своих навыков, открывают новые приемы и открывают собственные школы .
    Ни одна из этих разных школ не обладает абсолютной истиной . Тем не менее в рамках конкретной школы мы действуем так, будто ее учение и арсенал приемов верны . Именно так и следует тренироваться в школе Хаккорю или Джит Кун-до .
    Но правильность принципов в пределах одной школы не делает ошибочными учения других школ .
    Считайте, что эта книга является описанием Школы учителей Чистого кода .
    В ней представлены те методы и приемы, которыми мы сами пользуемся в сво- ем искусстве . Мы утверждаем, что если вы последуете нашему учению, то это принесет вам такую же пользу, как и нам, и вы научитесь писать чистый и про- фессиональный код . Но не стоит думать, что наше учение «истинно» в каком-то абсолютном смысле . Существуют другие школы и мастера, которые имеют ничуть не меньше оснований претендовать на профессионализм . Не упускайте возмож- ности учиться у них .
    В самом деле, многие рекомендации в этой книге противоречивы . Вероятно, вы согласитесь не со всеми из них . Возможно, против некоторых вы будете яростно протестовать . Это нормально . Мы не претендуем на абсолютную истину . С дру-
    35

    36
    Глава 1 . Чистый код гой стороны, приведенные в книге рекомендации являются плодами долгих, непростых размышлений . Мы пришли к ним после десятилетий практической работы, непрестанных проб и ошибок . Независимо от того, согласитесь вы с нами или нет, нашу точку зрения стоит по крайней мере узнать и уважать .
    Мы — авторы
    Поле
    @author комментария javadoc говорит о том, кто мы такие . Мы — авторы .
    А как известно, у каждого автора имеются свои читатели . Автор несет ответ- ственность за то, чтобы хорошо изложить свои мысли читателям . Когда вы в следующий раз напишете строку кода, вспомните, что вы — автор, и пишете для читателей, которые будут оценивать плоды вашей работы .
    Кто-то спросит: так ли уж часто читается наш код? Разве большая часть времени не уходит на его написание?
    Вам когда-нибудь доводилось воспроизводить запись сеанса редактирования?
    В 80-х и 90-х годах существовали редакторы, записывавшие все нажатия клавиш
    (например, Emacs) . Вы могли проработать целый час, а потом воспроизвести весь сеанс, словно ускоренное кино . Когда я это делал, результаты оказывались про- сто потрясающими . Большинство операций относилось к прокрутке и переходу к другим модулям!
    Боб открывает модуль.
    Он находит функцию, которую необходимо изменить.
    Задумывается о последствиях.
    Ой, теперь он переходит в начало модуля, чтобы проверить инициализацию пере-
    менной.
    Снова возвращается вниз и начинает вводить код.
    Стирает то, что только что ввел.
    Вводит заново.
    Еще раз стирает!
    Вводит половину чего-то другого, но стирает и это!
    Прокручивает модуль к другой функции, которая вызывает изменяемую функцию,
    чтобы посмотреть, как она вызывается.
    Возвращается обратно и восстанавливает только что стертый код.
    Задумывается.
    Снова стирает!
    Открывает другое окно и просматривает код субкласса. Переопределяется ли
    в нем эта функция?
    . . .
    В общем, вы поняли . На самом деле соотношение времени чтения и написания кода превышает 10:1 . Мы постоянно читаем свой старый код, поскольку это не- обходимо для написания нового кода .
    36

    Предыстория и принципы
    37
    Из-за столь высокого соотношения наш код должен легко читаться, даже если это затрудняет его написание . Конечно, написать код, не прочитав его, невозмож- но, так что упрощение чтения в действительности упрощает и написание кода .
    Уйти от этой логики невозможно . Невозможно написать код без предваритель- ного чтения окружающего кода . Код, который вы собираетесь написать сегодня, будет легко или тяжело читаться в зависимости от того, насколько легко или тяжело читается окружающий код . Если вы хотите быстро справиться со своей задачей, если вы хотите, чтобы ваш код было легко писать — позаботьтесь о том, чтобы он легко читался .
    Правило бойскаута
    Хорошо написать код недостаточно . Необходимо поддерживать чистоту кода с течением времени . Все мы видели, как код загнивает и деградирует с течением времени . Значит, мы должны активно поработать над тем, чтобы этого не про- изошло .
    У бойскаутов существует простое правило, которое применимо и к нашей про- фессии:
    Оставь место стоянки чище, чем оно было до твоего прихода
    1
    .
    Если мы все будем оставлять свой код чище, чем он был до нашего прихода, то код попросту не будет загнивать . Чистка не обязана быть глобальной . Присвойте более понятное имя переменной, разбейте слишком большую функцию, устрани- те одно незначительное повторение, почистите сложную цепочку if
    Представляете себе работу над проектом, код которого улучшается с течением времени? Но может ли профессионал позволить себе нечто иное? Разве посто- янное совершенствование не является неотъемлемой частью профессионализма?
    Предыстория и принципы
    Эта книга во многих отношениях является «предысторией» для книги, напи- санной мной в 2002 году: «Agile Software Development: Principles, Patterns, and
    Practices» (сокращенно PPP) . Книга PPP посвящена принципам объектно- ориентированного проектирования и практическим приемам, используемым про- фессиональными разработчиками . Если вы еще не читали PPP, скажу, что там раз- вивается тема, начатая в этой книге . Прочитавшие убедятся, что многие идеи пе- рекликаются с идеями, изложенными в этой книге на уровне кода .
    1
    Из прощального послания Роберта Стивенсона Смита Баден-Пауэлла скаутам: «Поста- райтесь оставить этот мир чуть лучшим, чем он был до вашего прихода…»
    37

    38
    Глава 1 . Чистый код
    В этой книге периодически встречаются ссылки на различные принципы проек- тирования . В частности, упоминается принцип единой ответственности (SRP), принцип открытости/закрытости (OCP) и принцип обращения зависимостей
    (DIP) . Все эти принципы подробно описаны в PPP .
    Заключение
    Книги по искусству не обещают сделать из вас художника . Все, что они могут — познакомить вас с приемами, инструментами и направлением мысли других художников . Эта книга тоже не обещает сделать из вас хорошего программиста .
    Она не обещает сформировать у вас «чувство кода» . Я могу лишь показать, в каком направлении мыслят хорошие программисты и какие приемы, трюки и инструменты они применяют в своей работе .
    Подобно книгам по искусству, эта книга насыщена подробностями . В ней много кода — как хорошего, так и плохого . Вы увидите, как плохой код преобразуется в хороший . Вы найдете списки эвристических правил, дисциплин и методов . Вы увидите множество примеров . А дальше дело только за вами .
    Помните старый анекдот о скрипаче, который заблудился по пути на концерт?
    Он остановил старика на углу и спросил, как попасть в Карнеги-холл . Старик посмотрел на скрипача, на зажатую у него под мышкой скрипку и сказал: «Ста- райся, сынок . Старайся!»
    литература
    [Beck07]: Implementation Patterns, Kent Beck, Addison-Wesley, 2007 .
    [Knuth92]: Literate Programming, Donald E . Knuth, Center for the Study of Lan- guage and Information, Leland Stanford Junior University, 1992 .
    38

    Содержательные имена
    Тим Оттингер
    Имена встречаются в программировании повсеместно . Мы присваиваем имена своим переменным, функциям, аргументам, классам и пакетам . Мы присваиваем имена исходным файлам и каталогам, в которых они хранятся . Мы присваиваем имена файлам jar, war и ear . Имена, имена, имена… Но то, что делается так часто, должно делаться хорошо . Далее приводятся некоторые простые правила создания хороших имен .
    2
    39

    40
    Глава 2 . Содержательные имена
    Имена должны передавать
    намерения программиста
    Легко сказать: имена должны передавать намерения программиста . И все же к вы- бору имен следует относиться серьезно . Чтобы выбрать хорошее имя, понадобит- ся время, но экономия окупит затраты . Итак, следите за именами в своих про- граммах и изменяйте их, если найдете более удачные варианты . Этим вы упро- стите жизнь каждому, кто читает ваш код (в том числе и себе самому) .
    Имя переменной, функции или класса должно отвечать на все главные вопросы .
    Оно должно сообщить, почему эта переменная (и т . д .) существует, что она делает и как используется . Если имя требует дополнительных комментариев, значит, оно не передает намерений программиста .
    int d; // Прошедшее время
    Имя d
    не передает ровным счетом ничего . Оно не ассоциируется ни с временными интервалами, ни с днями . Его следует заменить другим именем, которое указы- вает, что именно измеряется и в каких единицах:
    int elapsedTimeInDays;
    int daysSinceCreation;
    int daysSinceModification;
    int fileAgeInDays;
    Содержательные имена существенно упрощают понимание и модификацию кода .
    Например, что делает следующий фрагмент?
    public List getThem() {
    List list1 = new ArrayList();
    for (int[] x : theList)
    if (x[0] == 4) list1.add(x);
    return list1;
    }
    Почему мы не можем сразу сказать, что делает этот код? В нем нет сложных выражений . Пробелы и отступы расставлены грамотно . В коде задействованы только три переменные и две константы . В нем нет никаких хитроумных классов или полиморфных методов, только список массивов (по крайней мере на первый взгляд) .
    Проблема кроется не в сложности кода, а в его неочевидности, то есть степени, в которой контекст не следует явно из самого кода . Код подразумевает, что мы знаем ответы на вопросы:
    1 . Какие данные хранятся в theList
    ?
    2 . Чем так важен элемент theList с нулевым индексом?
    3 . Какой особый смысл имеет значение 4?
    4 . Как будет использоваться возвращаемый список?
    40

    Избегайте дезинформации
    41
    Ответы на все эти вопросы не следуют из примера, хотя и могли бы . Допустим, мы работаем над игрой «Сапер» . Игровое поле представлено в виде списка ячеек с именем theList
    . Переименуем его в gameBoard
    Каждая ячейка игрового поля представлена простым массивом . Далее выясняет- ся, что в элементе с нулевым индексом хранится код состояния, а код 4 означает
    «флажок установлен» . Даже простое присваивание имен всем этим концепциям существенно улучшает код:
    public List getFlaggedCells() {
    List flaggedCells = new ArrayList();
    for (int[] cell : gameBoard)
    if (cell[STATUS_VALUE] == FLAGGED)
    flaggedCells.add(cell);
    return flaggedCells;
    }
    Обратите внимание: простота кода несколько не изменилась . Новая версия содер- жит точно такое же количество операторов и констант, с абсолютно таким же ко- личеством уровней вложенности . Однако код стал существенно более понятным .
    Можно пойти еще дальше и написать простой класс для представления ячеек вместо использования массива int
    . В класс включается функция, передающая на- мерения программиста (назовем ее isFlagged
    ); она скрывает «волшебные» числа .
    В результате мы получаем новую версию функции:
    public List getFlaggedCells() {
    List flaggedCells = new ArrayList();
    for (Cell cell : gameBoard)
    if (cell.isFlagged())
    flaggedCells.add(cell);
    return flaggedCells;
    }
    Не изменилось ничего, кроме имен — но теперь можно легко понять, что здесь происходит . Такова сила выбора хороших имен .
    Избегайте дезинформации
    Программисты должны избегать ложных ассоциаций, затемняющих смысл кода .
    Не используйте слова со скрытыми значениями, отличными от предполагаемого .
    Например, переменным не стоит присваивать имена hp
    , aix
    , and sco
    , потому что они ассоциируются с платформами и разновидностями Unix . Даже если в пере- менной хранится длина гипотенузы и имя hp кажется хорошим сокращением, оно может ввести в заблуждение читателя кода .
    Не обозначайте группу учетных записей именем accountList
    , если только она дей- ствительно не хранится в списке (
    List
    ) . Слово «список» имеет для программиста вполне конкретный смысл . Если записи хранятся не в
    List
    , а в другом контейнере,
    41

    42
    Глава 2 . Содержательные имена это может привести к ложным выводам
    1
    . В этом примере лучше подойдет имя accountGroup
    , bunchOfAccounts и даже просто accounts
    Остерегайтесь малозаметных различий в именах . Сколько времени понадобится, чтобы заметить незначительное различие в
    XYZControllerForEfficientHandlingOf-
    Strings в одном модуле и
    XYZControllerForEfficientStorageOfStrings где-то в другом месте? Эти имена выглядят устрашающе похожими .
    Сходное представление сходных концепций — информация . Непоследователь- ное представление — дезинформация . Современные среды Java поддерживают удобный механизм автоматического завершения кода . Вы вводите несколько символов имени, нажимаете некую комбинацию клавиш (а иногда обходится и без этого) и получаете список возможных вариантов завершения имени . Очень удобно, если имена похожих объектов сортируются по алфавиту, и если различия предельно очевидны — ведь разработчик, скорее всего, выберет ваш объект по имени, не увидев ни ваших обширных комментариев, ни хотя бы списка методов класса .
    По-настоящему устрашающие примеры дезинформирующих имен встречаются при использовании строчной «L» и прописной «O» в именах переменных, особен- но в комбинациях . Естественно, проблемы возникают из-за того, что эти буквы почти не отличаются от констант «1» и «0» соответственно .
    int a = l;
    if ( O == l )
    a = O1;
    else l = 01;
    Возможно, некоторым читателям этот совет покажется надуманным, однако мы неоднократно видели код, в котором подобных ухищрений было предостаточно .
    В одном случае автор кода даже предложил использовать другой шрифт, чтобы различия стали более очевидными — в дальнейшем это решение должно было передаваться всем будущим разработчикам на словах или в письменном доку- менте . Простое переименование решает проблему окончательно и без создания новых документов .
    Используйте осмысленные различия
    Когда программист пишет код исключи- тельно для того, чтобы удовлетворить за- просы компилятора или интерпретатора, он сам себе создает проблемы . Например, поскольку одно имя в одной области име- ни не может обозначать две разные вещи,
    1
    Как будет показано ниже, даже если контейнер действительно представляет собой
    List, лучше обойтись без кодирования типа контейнера в имени .
    42

    Используйте осмысленные различия
    43
    возникает соблазн произвольно изменить одно из имен . Иногда для этого имя записывается заведомо неправильно и возникает удивительная ситуация: после исправления грамматической ошибки программа перестает компилироваться
    1
    Недостаточно добавить в имя серию цифр или неинформативные слова, даже если компилятору этого будет достаточно . Если имена различаются, то они должны обозначать разные понятия .
    «Числовые ряды» вида
    (a1, a2, .. aN)
    являются противоположностью созна- тельного присваивания имен . Такие имена не дезинформируют — они просто не несут информации и не дают представления о намерениях автора . Пример:
    public static void copyChars(char a1[], char a2[]) {
    for (int i = 0; i < a1.length; i++) {
    a2[i] = a1[i];
    }
    }
    Такая функция будет читаться намного лучше, если присвоить аргументам имена source и destination
    Неинформативные слова также применяются для создания бессодержательных различий . Допустим, у вас имеется класс
    Product
    . Создав другой класс с именем
    ProductInfo или
    ProductData
    , вы создаете разные имена, которые по сути обозна- чают одно и то же .
    Info и
    Data не несут полезной информации, как и артикли a,
    an и the .
    Следует учесть, что использование префиксов a и the вовсе не является ошибкой, но только при условии, что они создают осмысленные различия . Например, пре- фикс a может присваиваться всем локальным переменным, а префикс the — всем аргументам функций
    2
    . Проблема возникает тогда, когда вы называете перемен- ную theZork
    , потому что в программе уже есть другая переменная с именем zork
    Неинформативные слова избыточны . Слово variable никогда не должно встре- чаться в именах переменных . Слово table никогда не должно встречаться в име- нах таблиц . Чем имя
    NameString лучше
    Name
    ? Разве имя может быть, скажем, вещественным числом? Если может, то это нарушает предыдущее правило о дез- информации . Представьте, что в программе присутствуют два класса с именами
    Customer и
    CustomerObject
    . Что вы можете сказать о различиях между ними? Какой класс предоставляет лучший путь к истории платежей клиента?
    Эта проблема встретилась нам в одном реально существующем приложении .
    Мы изменили имена, чтобы защитить виновных, но точная форма ошибки вы- глядит так:
    getActiveAccount();
    getActiveAccounts();
    getActiveAccountInfo();
    1
    Для примера можно привести совершенно отвратительную привычку создавать перемен- ную klass только из-за того, что имя class было использовано для других целей .
    2
    Дядюшка Боб действовал так при программировании на C++, но потом бросил эту при- вычку, потому что благодаря современным IDE она стала излишней .
    43

    1   2   3   4   5   6   7   8   9   ...   49


    написать администратору сайта