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

  • Пример совместного использования кода в блоке else с помощью вынесения общего кода в отдельный метод (C++)

  • Пример совместного использования кода в блоке else без применения goto (C++)

  • Краткий итог основных принципов использования goto

  • Перекрестная ссылка

  • 17.4. Перспективы нестандартных управляющих структур

  • Дополнительные ресурсы Следующие материалы позволят расширить ваши представ- ления о нестандартных управляющих структурах.Возвраты

  • Контрольный список: нестандартные управляющие структуры Возвраты

  • ЧАСТЬ IV

  • ГЛАВА 18

  • Пример использования сложной логики для классификации символов (Java)

  • Пример использования таблицы подстановки для классификации символов (Java)

  • Два вопроса применения табличных методов

  • 18.2. Таблицы с прямым доступом

  • Рис. 18-1.

  • Пример неуклюжего способа определения количества дней в месяце (Visual Basic)

  • Руководство по стилю программирования и конструированию по


    Скачать 7.6 Mb.
    НазваниеРуководство по стилю программирования и конструированию по
    Дата18.05.2023
    Размер7.6 Mb.
    Формат файлаpdf
    Имя файлаCode_Complete.pdf
    ТипРуководство
    #1139697
    страница50 из 104
    1   ...   46   47   48   49   50   51   52   53   ...   104
    ГЛАВА 17 Нестандартные управляющие структуры
    399
    else {
    importantVariable = GetValue();
    MID_LOOP:
    // Много кода.
    }
    Это хороший, логически извилистый пример: его практически невозможно чи- тать в том виде, в каком он есть, и тяжело правильно переписать без
    goto. Если вам кажется, что вы легко преобразуете его в вариант без
    goto, попросите кого- нибудь проверить ваш код! Несколько экспертов-программистов переписали его некорректно.
    Вы можете изменить этот код разными способами. Можно продублировать код,
    вынести общий код в отдельный метод и вызывать его из двух мест или повтор- но проверять одно и то же условие. В большинстве языков новая версия будет больше и медленнее оригинала, но совсем не намного. Поэтому, если этот код не исключительно критичен к скорости и объему, перепишите его, не задумываясь об эффективности.
    Лучший способ переписать этот код — вынести участок
    // Много кода в отдель- ный метод. После этого вы сможете вызывать его из тех мест, где раньше распо- лагались
    goto и метка его перехода. Это позволит сохранить оригинальную струк- туру условного оператора. Вот как это выглядит:
    Пример совместного использования кода в блоке else
    с помощью вынесения общего кода в отдельный метод (C++)
    if ( statusOk ) {
    if ( dataAvailable ) {
    importantVariable = x;
    DoLotsOfCode( importantVariable );
    }
    }
    else {
    importantVariable = GetValue();
    DoLotsOfCode( importantVariable );
    }
    В общем случае написание нового метода — лучший подход. Однако иногда вы- носить дублированный код в отдельный метод непрактично. В этом случае как обходной маневр можно предложить реструктурирование условных выражений так, чтобы оставить код в том же методе, а не выносить в отдельный:
    Пример совместного использования кода в блоке else без применения goto (C++)
    if ( ( statusOk && dataAvailable ) || !statusOk ) {
    if ( statusOk && dataAvailable ) {
    importantVariable = x;
    }

    400
    ЧАСТЬ IV Операторы else {
    importantVariable = GetValue();
    }
    // Много кода.
    }
    Это точное механическое преобразование логики вариан- та с
    goto. Здесь переменная statusOK дополнительно прове- ряется два раза, а
    dataAvailable — один, но сам код эквива- лентен. Если повторная проверка условия вас беспокоит, об- ратите внимание, что значение
    statusOK не обязательно про- верять дважды в первом условии
    if. Кроме того, вы также можете опустить про- верку
    dataAvailable во втором условии if.
    Краткий итог основных принципов использования goto
    Использование
    goto — это вопрос религии. Моя догма: в современных языках вы легко можете заменить девять из десяти операторов
    goto эк- вивалентными последовательными конструкциями. В этих простых слу- чаях вы должны заменять операторы
    goto просто по привычке. В сложных случа- ях вы также можете изгнать
    goto в девяти случаях из десяти: можно разбить код на меньшие по размеру методы, использовать
    try-finally или вложенные if, прове- рять и перепроверять статусную переменную или реструктурировать условные вы- ражения. Исключить
    goto в таких случаях сложнее, но это хорошее умственное уп- ражнение, а методы, обсуждаемые в этом разделе, предлагают вам инструменты для этих целей.
    В одном случае, оставшемся из 100, в котором применение
    goto — вполне легаль- ное решение задачи, подробно задокументируйте, а затем используйте его. Если у вас на ногах резиновые сапоги, не стоит обходить весь квартал, чтобы не за- пачкаться в грязной луже. Но не отвергайте варианты избавления от
    goto, предла- гаемые другими программистами. Они могут заметить то, на что вы не обратили внимания.
    Вот сводка принципов использования
    goto.

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

    Не используйте
    goto, если доступна эквивалентная встроенная конструкция.

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

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

    ГЛАВА 17 Нестандартные управляющие структуры
    401

    Используйте операторы
    goto так, чтобы их переходы были только вперед, а не назад, если только вы не эмулируете управляющие конструкции.

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

    Убедитесь, что
    goto не приводит к созданию недостижимого кода.

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

    неограниченное использование операторов
    goto;

    возможность вычислять метку перехода
    goto динамически и переходить по этому адресу;

    возможность применения
    goto для перехода из середины одного метода в се- редину другого;

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

    возможность генерации кода программой на лету и немедленного его выпол- нения.
    В свое время каждая из этих идей считалась приемлемой или даже желательной,
    хотя сейчас они все выглядят безнадежно устаревшими или опасными. Область разработки ПО развивается во многом благодаря
    ограничению того, что програм- мисты могут делать со своим кодом. В связи с этим я рассматриваю нетрадици- онные управляющие структуры с большим скептицизмом. Я подозреваю, что боль- шинство конструкций, упомянутых в этой главе, со временем окажется на свалке программистских отходов наряду с вычисляемыми метками
    goto, плавающими точками входа в методы, самомодифицирующимся кодом и другими структура- ми, отдающими предпочтение гибкости и удобству в ущерб структурированнос- ти и возможности управления сложностью.
    Дополнительные ресурсы
    Следующие материалы позволят расширить ваши представ- ления о нестандартных управляющих структурах.
    Возвраты
    Fowler, Martin.
    Refactoring: Improving the Design of Existing Code. Reading, MA: Addison-
    Wesley, 1999. В описании метода рефакторинга под названием «Замените вложенные http://cc2e.com/1792

    402
    ЧАСТЬ IV Операторы условные выражения сторожевыми операторами» Фаулер предлагает использовать множественные возвраты из методов для уменьшения вложенности набора
    if-вы- ражений. Фаулер приводит доводы в защиту того, что множественные операторы
    return — подходящий способ улучшения ясности кода и нет никакого вреда в том,
    чтобы иметь несколько выходов из метода.
    Операторы goto
    Следующие статьи содержат полное обсуждение
    goto. Этот спор до сих пор воз- никает время от времени на рабочих местах, в учебниках и журналах, но вы не услышите ничего такого, что не было бы полностью исследовано 20 лет назад.
    Dijkstra, Edsger. «Go To Statement Considered Harmful».
    Com-
    munications of the ACM 11, no. 3 (March, 1968): 147–148, так- же доступно по адресу
    www.cs.utexas.edu/users/EWD/. Это то самое знаменитое письмо, которым Дейкстра поднес спичку к бумаге и воспла- менил одну из самых долгих дискуссий в истории разработки ПО.
    Wulf, W. A. «A Case Against the GOTO».
    Proceedings of the 25th National ACM Confer-
    ence, August 1972: 791–797. Эта статья — еще один аргумент против беспорядоч- ного использования
    goto. Вульф утверждает, что, если языки программирования будут содержать необходимые управляющие структуры, необходимость в
    goto
    исчезнет. С 1972 г., когда была написана эта статья, такие языки, как C++, Java и
    Visual Basic, доказали свою корректность по Вульфу.
    Knuth, Donald. «Structured Programming with go to Statements», 1974.
    Classics in
    Software Engineering, edited by Edward Yourdon. Englewood Cliffs, NJ: Yourdon Press,
    1979. Эта длинная статья не полностью посвящена
    goto, но содержит кучу приме- ров кода, который становится эффективнее после исключения
    goto, и еще одну кучу примеров кода, который становится эффективней после добавления
    goto.
    Rubin, Frank. «‘GOTO Considered Harmful’ Considered Harmful».
    Communications of
    the ACM 30, no. 3 (March, 1987): 195–196. В этой несколько резкой статье, обра- щенной к редактору, Рубин утверждает, что программирование без
    goto стоило бизнесу «сотни миллионов долларов». Затем он предлагает краткий фрагмент кода,
    использующего
    goto, и утверждает, что он превосходит свои аналоги без goto.
    Ответы, полученные на письмо Рубина, представляют больший интерес, чем само письмо. Пять месяцев журнал «Communications of the ACM» (CACM) публиковал письма, предлагающие разные версии программы Рубина из семи строк. Ответы равномерно распределились между защитниками и хулителями
    goto. Читатели предложили приблизительно 17 вариантов преобразования, которые полностью покрывают все подходы к исключению
    goto. Редактор CACM заметил, что это письмо вызвало больше откликов, чем любой другой вопрос, когда-либо обсуждавшийся на страницах CACM.
    Последовавшие письма можно найти в номерах:

    Communications of the ACM 30, no. 5 (May, 1987): 351–355;

    Communications of the ACM 30, no. 6 (June, 1987): 475–478;

    Communications of the ACM 30, no. 7 (July, 1987): 632–634;

    Communications of the ACM 30, no. 8 (August, 1987): 659–662;

    Communications of the ACM 30, no. 12 (December, 1987): 997, 1085.
    http://cc2e.com/1799

    ГЛАВА 17 Нестандартные управляющие структуры
    403
    Clark, R. Lawrence, «A Linguistic Contribution of GOTO-less
    Programming».
    Datamation, December 1973. Эта классическая статья с юмором предлагает заменить термин «go to» (пе- рейти к) на «come from» (перешел от). Она также была перепечатана в номере CACM
    в апреле 1974 года.
    Контрольный список: нестандартные
    управляющие структуры
    Возвраты
     Используют ли методы операции возврата только при необходимости?
     Улучшают ли операторы возврата читабельность?
    Рекурсия
     Содержит ли рекурсивный метод код для прекращения рекурсии?
     Использует ли метод счетчик безопасности для гарантии того, что выпол- нение будет завершено?
     Ограничена ли рекурсия одним методом?
     Соответствует ли глубина рекурсии ограничениям, налагаемым размерами стека программы?
     Является ли рекурсия лучшим способом реализации метода? Не лучше ли использовать простые итерации?
    goto
     Используются ли операторы goto только как последнее средство и лишь для того, чтобы сделать код удобнее для чтения и сопровождения?
     Если goto используется ради эффективности, был ли прирост эффективно- сти измерен и задокументирован?
     Ограничено ли использование goto одной меткой на метод?
     Выполняются ли переходы goto только вперед, а не назад?
     Все ли метки goto используются?
    Ключевые моменты

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

    Рекурсия предлагает изящное решение для небольшого набора задач. Ее тоже нужно использовать аккуратно.

    Иногда операторы
    goto — лучший способ облегчить чтение и сопровождение кода. Таких случаев очень немного. Используйте
    goto только как последнее средство.
    http://cc2e.com/1706
    http://cc2e.com/1713

    404
    ЧАСТЬ IV Операторы
    Г Л А В А 1 8
    Табличные методы
    Содержание

    18.1. Основные вопросы использования табличных методов

    18.2. Таблицы с прямым доступом

    18.3. Таблицы с индексированным доступом

    18.4. Таблицы со ступенчатым доступом

    18.5. Другие примеры табличного поиска
    Связанные темы

    Сокрытие информации: подраздел «Скрывайте секреты (к вопросу о сокрытии информации)» раздела 5.3

    Проектирование классов: глава 6

    Использование таблиц решений для замены сложной логики: раздел 19.1

    Замена сложных выражений табличным поиском: раздел 26.1
    Табличный метод — это схема, позволяющая искать информацию в таблице, а не использовать для этого логические выражения, такие как
    if и case. Практически все, что вы можете выбирать посредством логических операторов, можно выби- рать, применяя таблицы. В простых случаях логические выражения проще и по- нятней. Но при усложнении логических построений таблицы становятся все при- влекательнее.
    Если вы уже знакомы с табличными методами, считайте эту главу обзором. В этом случае вы можете изучить «Пример гибкого формата сообщения» (раздел 18.2)
    в качестве иллюстрации того факта, что объектно-ориентированный дизайн не обязательно лучше других вариантов только потому, что он объектно-ориенти- рованный. После этого можете переходить к обсуждению общих вопросов управ- ления в главе 19.
    http://cc2e.com/1865

    ГЛАВА 18 Табличные методы
    405
    18.1. Основные вопросы применения
    табличных методов
    При определенных обстоятельствах табличный код проще, чем сложные логические выражения, легче поддается изменению и эффективнее. До- пустим, вы хотите классифицировать символы, выделив буквы, знаки пре- пинания и цифры. Вы можете использовать сложную логическую последователь- ность вроде этой:
    Пример использования сложной логики для классификации символов (Java)
    if ( ( ( ‘a’ <= inputChar ) && ( inputChar <= ‘z’ ) ) ||
    ( ( ‘A’ <= inputChar ) && ( inputChar <= ‘Z’ ) ) ) {
    charType = CharacterType.Letter;
    }
    else if ( ( inputChar == ‘ ‘ ) || ( inputChar == ‘,’ ) ||
    ( inputChar == ‘.’ ) || ( inputChar == ‘!’ ) || ( inputChar == ‘(‘ ) ||
    ( inputChar == ‘)’ ) || ( inputChar == ‘:’ ) || ( inputChar == ‘;’ ) ||
    ( inputChar == ‘?’ ) || ( inputChar == ‘-’ ) ) {
    charType = CharacterType.Punctuation;
    }
    else if ( ( ‘0’ <= inputChar ) && ( inputChar <= ‘9’ ) ) {
    charType = CharacterType.Digit;
    }
    Если бы вместо этого фрагмента вы использовали таблицу подстановки, то помес- тили бы тип каждого элемента в массив и обращались бы к нему по коду символа.
    Сложный фрагмент кода, представленный выше, заменялся бы на такое выражение:
    Пример использования таблицы подстановки для классификации символов (Java)
    charType = charTypeTable[ inputChar ];
    Этот фрагмент предполагает, что массив
    charTypeTable был заранее заполнен. Вы поместили знания, доступные программе, в данные, а не в логику: в таблицу, а не в условия
    if.
    Два вопроса применения табличных методов
    При применении табличных методов перед вами стоят два основных вопроса. Во- первых, вам надо решить, как будет выполняться поиск записей в таблице. Вы можете использовать какие-либо данные для прямого доступа к таблице. Так, если вам нужно систематизировать данные по месяцам, то выбор ключа для таблицы месяцев очевиден. Вы можете использовать массив с индексом от 1 до 12.
    Другие данные затруднительно использовать для прямого поиска таблич- ной записи. Так, для классификации информации по номеру социального страхования (SSN) вы не можете использовать этот номер в качестве ключа непосредственно, если, конечно, вы не собираетесь хранить в таблице
    999-99-9999 записей. Вам понадобится более сложный подход. Вот какие спосо- бы, применяются для поиска записи в таблице:

    406
    ЧАСТЬ IV Операторы

    прямой доступ;

    индексированный доступ;

    ступенчатый доступ.
    Каждый из этих вариантов доступа подробно описан ниже.
    Второй вопрос, который нужно решить при использовании табличных методов: что хранить в таблице. Иногда результатом поиска в таблице являются данные — тогда можно хранить в таблице сами данные. Если же результатом поиска является действие, код, который описывает это действие,
    можно хранить, а в некоторых языках можно хранить ссылку на метод, выпол- няющий это действие. В каждом из этих случаев таблицы усложняются.
    18.2. Таблицы с прямым доступом
    Как и все таблицы подстановки, таблицы с прямым доступом предназначены для замены более сложных логических структур. Они имеют «прямой доступ», пото- му что вам не нужно ходить по кругу, чтобы найти в таблице необходимую ин- формацию. Вы можете непосредственно выбрать нужную запись (рис. 18-1).
    Рис. 18-1. Как следует из ее имени, таблица с прямым доступом позволяет
    обращаться к требуемому элементу напрямую
    Пример определения количества дней в месяце
    Допустим, вы хотите получить число дней в месяце (для простоты забудем о ви- сокосных годах). Разумеется, создание большого условия
    if — неуклюжий способ решения этой проблемы:
    Пример неуклюжего способа определения количества дней в месяце (Visual Basic)
    If ( month = 1 ) Then days = 31
    ElseIf ( month = 2 ) Then days = 28
    ElseIf ( month = 3 ) Then days = 31
    ElseIf ( month = 4 ) Then days = 30
    ElseIf ( month = 5 ) Then days = 31
    ElseIf ( month = 6 ) Then

    1   ...   46   47   48   49   50   51   52   53   ...   104


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