Effective Java tmprogramming Language GuideJ o s h u a b lo c h
Скачать 1.05 Mb.
|
*/ public Cheese[] getCheeses() { геtuгп (Cheese[] сhееsеsInStосk.tоАггау(NULL_СНЕЕSЕ_АRRАУ); В этой идиоме константа в виде массива нулевой длины передается методу toArray для того, чтобы показать, какой тип он должен возвратить. Обычно метод toArray выделяет место в памяти для возвращаемого массива, однако если коллекция пуста, 126 она размещается во входном массиве, а спецификация Сollесtion,tоАггау(ОЬjесt[] дает гарантию, что если входной' массив будет достаточно вместителен, чтобы содержать коллекцию, возвращен будет именно он. Поэтому представленная идиома никогда не будет сама размещать в памяти массив нулевой длины, а в качестве такового использует "константу с указанием типа. Подведем итоги. Нет никаких причин для того, чтобы работающий с массивами метод возвращал значение null, а не массив нулевой длины. Такая идиома, по-видимому, проистекает из языка программирования С, где длина массива возвращается отдельно от самого массива. В языке С бесполезно выделять память под массив нулевой длины. Для всех открытых элементов АР пишите ос- комментарии Если АР будет использоваться, его нужно описывать. Обычно документация к АР пишется вручную, и поддержание соответствия между документацией и программным кодом - весьма неприятная работа. Среда программирования Java облегчает эту задачу с помощью утилиты, называемой /avadoc. Она автоматически генерирует документацию к API, отталкиваясь от исходного текста программы, дополненного специальным образом оформленными комментариями к документации (documentation comment), которые чаще называют ос - комментариями (doc comment). Утилита Javadoc предлагает простой, эффективный способ документирования АР и используется повсеместно. Если вы еще незнакомы с соглашениями для ос - комментариев, то обязаны их изучить. Эти соглашения не являются частью языка программирования Java, но де-факто они образуют свой API, который обязан знать каждый программист. Соглашения сформулированы в "The Javadoc Tool Ноте Page" [Javadoc-b]. Чтобы должным образом документировать API, следует предварять doc - комментарием каждую предоставляемую пользователям декларацию класса, интерфейса, конструктора, метода и поля. Единственное исключение обсуждается в конце статьи. Если ос - комментарий отсутствует, самое лучшее, что может сделать Javadoc,- это воспроизвести декларацию элемента АР как единственно возможную для него документацию. Работа су которого нет комментариев к документации, чревата ошибками. Чтобы создать программный код, приемлемый для сопровождения, вы должны написать ос - комментарии даже для тех классов, интерфейсов, конструкторов, методов и полей, которые не предоставляются пользователям. ос - комментарий для метода должен лаконично описывать соглашения между методом и его клиентами. Соглашение должно оговаривать, что делает данный метода не как он это делает. Исключение составляют лишь методы в классах, предназначенных для наследования (статья 15). В ос - комментарии необходимо перечислить все предусловия (precondition), те. утверждения, которые должны 127 быть истинными для того, чтобы клиент мог вызвать этот метод, и постусловия (postcondition), те. утверждения, которые будут истинными после успешного завершения вызова. Обычно предусловия неявно описываются тегами @throws для необработанных исключений. Каждое необработанное исключение соответствует нарушению некоего предусловия. Предусловия также могут быть указаны вместе с параметрами, которых они касаются, в соответствующих тегах @раrаm. Помимо пред- и постусловий для методов должны быть также документированы любые побочные эффекты. Побочный эффект (side effect) - это поддающееся наблюдению изменение состояния системы, которое является неявным условием для достижения постусловия. Например, если метод запускает. фоновый поток, это должно быть отражено в документации. Наконец, комментарии к документации должны описывать безопасность класса при работе с потоками (thread safety) , которая обсуждается в статье 52. В целях полного описания соглашений ос - комментарий для метода должен включать в себя тег рака для каждого параметра, тег @return, если только метод не возвращает тип void, и тег @throws для каждого исключения, инициируемого этим методом, как обработанного, так инеобработанного статья 44). По соглашению, текст, который следует за тегом @раrаm или @return, представляет собой именную конструкцию (noun phrase - термин грамматики английского языка. Описывающую значение данного параметра или возвращаемое значение. Текст, следующий за тегом @throws, должен состоять из слова if и именной конструкции, описывающей условия, при которых инициируется данное исключение. Иногда вместо именных конструкций используются арифметические выражения. Все эти соглашения иллюстрирует следующий краткий ос - комментарий из интерфейса List: /** * Возвращает элемент, который занимает заданную позицию * в указанном списке. * @param index - индекс элемента, который нужно возвратить индекс должен * быть неотрицательными его значение должно быть * меньше размера списка. * @return элемент, занимающий в списке указанную позицию. * @throws IndexOutOfBoundsException, если индекс лежит вне диапазона. * ( index < () || index > = this.size()) */ Object get(int index); Заметим, что в этом ос - комментарии используются метасимволы и теги языка HTML. Утилита Javadoc преобразует dос-комментарии в код HTML, и любые содержавшиеся в ос - комментариях элементы HTML оказываются в полученном НТМL - документе. Иногда программисты заходят настолько далеко, что встраивают в свои ос - комментарии таблицы HTML, хотя это не является общепринятым. Чаще всего применяются следующие теги р для разделения параграфов, и для представления фрагментов кода, <рге> для более длинных фрагментов кода. Теги согласно спецификации HTML 4.01, является более предпочтительным, поскольку - это элемент стилевого оформления шрифта. Пользоваться элементами стилевого оформления шрифтов здесь не рекомендуется, предпочтение отдается каскадным таблицам стилей [HTML401]). Некоторые программисты все же предпочитают тег , поскольку он короче и не столь агрессивен. Не забывайте, что для генерации метасимволов HTML, таких как знак "меньше" « ), знак "больше" (» и амперсанд (&), необходимы еsсаре - последовательности. Чтобы получить знак "меньше, используйте еsсаре - последовательность "< ", знак "больше" - последовательность "> ", знак амперсанда - последовательность "ар ". В предыдущем ос - комментарии еsсаре последовательность применена в теге @throws. Наконец, отметим появление в ос - комментарии слова "this". По соглашению, слово "this" всегда ссылается на тот объект, которому принадлежит вызываемый метод, соответствующий данному ос - комментарию. Первым предложением любого ос - комментария является общее описание (summary description) того элемента, к которому этот комментарий относится. Общее описание должно позиционироваться как описывающее функции соответствующей сущности. Во избежание путаницы никакие два члена или конструктора водном классе или интерфейсе не должны иметь одинакового общего описания. Особое внимание обращайте на перезагруженные методы, описание которых часто хочется начать с одного итого же предложения. Внимательно следите затем, чтобы в первом предложении ос - комментария не было точки. В противном случае общее описание будет завершено прежде времени. Например, комментарий к документации, который начинается с фразы "A college ее, such as В. S., М. S., о Ph. D. ", приведет к появлению в общем описании фразы Аи В" Во избежание подобных проблем лучше не использовать в общем описании сокращений и десятичных дробей. Для получения символа точки нужно заменить его числовым представлением (и encoding) ". ". Хотя это работает, исходный текст программы приобретает не слишком красивый вид /** * A college degree, such as В, М о * Ph.D. */ public class ее { ... } Тезис, что первым предложением в doc - комментарии является общее описание, отчасти вводит в заблуждение. По соглашению, оно редко бывает законченным предложением. Общее описание методов и конструкторов должно представлять собой глагольную конструкцию (verb phrase - термин грамматики английского языка, которая описывает операцию, осуществляемую этим методом. Например • ArrayList(int initialCapacity) - создает пустой список с заданной начальной емкостью. • Collection. size() - возвращает количество элементов в указанной коллекции. Общее описание классов, интерфейсов и полей должно быть именной конструкцией, описывающей сущность, которую представляет экземпляр этого класса, интерфейса или само поле. Например Т - задача, которая может быть спланирована классом Тiтeг для однократного или повторяющегося исполнения. Р - значение типа double, наиболее близкое к числу (отношение длины окружности к ее диаметру. В этой статье рассмотрены лишь основные соглашения, касающиеся doc - комментариев, существует целый ряд других соглашений. Имеется несколько руководств по стилю написания ос - комментариев [Javadoc-a, VermeulenOO]. Есть также утилиты, проверяющие соблюдение этих правил [Qoclint]. Начиная с версии 1.2.2, утилита Javadoc имеет возможность "автоматически использовать вновь" и "наследовать" комментарии к методам. Если метод не имеет ос - комментария, Javadoc находит среди приемлемых наиболее близкий комментарий, отдавая при это предпочтение интерфейсам, а не суперклассам. Подробности алгоритма поиска комментариев приводятся в "The Javadoc M anual". Это означает, что классы могут заимствовать ос - комментарии из реализуемых ими интерфейсов вместо того, чтобы копировать комментарии у себя. Такая возможность способна сократить или вовсе снять бремя поддержки многочисленных наборов почти идентичных ос - комментариев, ноу нее есть одно ограничение. Наследование ос – комментариев слишком бескомпромиссно наследующий метод никак не может поменять унаследованный doc - комментарий. Между тем метод нередко уточняет соглашения, унаследованные от интерфейс ив этом случае он действительно нуждается в собственном ос - комментарии. Простейший способ уменьшить вероятность появления ошибок в комментариях к документации - это пропустить НТМL - файлы, сгенерированные утилитой Javadoc, через программу проверки кода HTML (HTML validity checker). При этом будет обнаружено множество случаев неправильного использования тегов и метасимволов HTML, которые нужно исправить. Некоторые из программ проверки кода HTML, например шеbliпt [WebIint], доступны в Интернете. Следует привести еще одно предостережение, связанное с комментариями к документации. Комментарии должны сопровождать все элементы внешнего API, но этого не всегда достаточно. для сложных API, состоящих из множества взаимосвязанных классов, комментарии к документации часто требуется дополнять внешним документом, описывающим общую архитектуру данного API. Если такой документ существует, то комментарии к документации в соответствующем классе или пакете должны ссылаться на него. Подведем итоги. Комментарии к документации - самый лучший, самый Эффективный способ документирования API. Написание комментариев нужно считать обязательным для всех элементов внешнего API. Выберите стиль, который не противоречит стандартным соглашениям. Помните, что в комментариях к документации можно использовать любой код HTML, причем метасимволы HTML необходимо маскировать еsсаре - последовательностями. Глава 7 Общие вопросы программирования Данная глава посвящена обсуждению основных элементов языка Java. В ней рассматриваются интерпретация локальных переменных, использование библиотеки различных типов данных, а также две выходящие за рамки языка возможности отражение (reflection) и машинно-зависимые методы (native method). Наконец, обсуждаются оптимизация и соглашения по именованию. Сводите км и ним уму область видимости локальных переменных bЭта статья по своей сути схожа со статьей 12 "Сводите к минимуму доступность классов и членов. Сужая область видимости локальных переменных, вы повышаете удобство чтения и сопровождения вашего кода, сокращаете вероятность возникновения ошибок. Язык программирования указывает, что локальные переменные должны декларироваться вначале блока. И программисты продолжают придерживаться этого порядка, хотя от него уже нужно отказываться. Напомним, что язык программирования Java позволяет объявлять переменную в любом месте, где может стоять оператор. Самый сильный прием сужения области видимости локальной перемен заключается в декларировании ее в том месте, где она впервые используется. Декларация переменной до ее использования только засоряет программу появляется еще одна строка, отвлекающая читателя, который пытается разобраться в том, что делает программа. К тому моменту, когда переменная применяется, читатель может уже не помнить ни ее тип, ни начальное значение. Если программа совершенствуется и переменная больше ненужна, легко забыть убрать ее декларацию, если та находится далеко от места первого использования переменной. к расширению области видимости локальной переменной приводит не только слишком ранее, но и слишком позднее ее декларирование. Область видимости локальной переменной начинается в том месте, где она декларируется, и заканчивается с завершением блока, содержавшего эту декларацию. Если переменная декларирована за пределами блока, где она используется, то она остается видимой и после того, как программа выйдет из этого блока. Если переменная случайно была использована доили после области, в которой она должна была применяться, последствия могут быть катастрофическими. Почти каждая декларация локальной переменной должна содержать инициализатор. Если у вас недостаточно информации для правильной инициализации переменной, вы должны отложить декларацию до той поры, пока она не появится. Исключение из этого правила связано с использованием операторов tгу / catch. Если для инициализации переменной применяется метод, инициирующий появление обрабатываемого исключения, то инициализация переменной должна осуществляться внутри блока try. Если переменная должна использоваться за пределами блока try, декларировать ее следует перед блоком try, там, где она еще не может быть "правильно инициализирована" (статья 35). Цикл предоставляет уникальную возможность для сужения области видимости переменных. Цикл for позволяет объявлять переменные цикла (loop variabIe), ограничивая их видимость ровнотой областью, где они нужны. (Эта область состоит из собственно тела цикла, а также из предшествующих ему полей инициализации, проверки и обновления) Следовательно, если после завершения цикла значения его переменных ненужны, предпочтение следует отдавать циклам for, а не while. Представим, например, предпочтительную идиому для организации цикла по некоей коллекции for (Iterator i = с i.hasNext(); ) { doSomething(i.next()); } для пояснения, почему данный цикл for предпочтительнее более очевидного цикла while, рассмотрим следующий фрагмент кода, в котором содержатся два цикла while и одна ошибка Iterator i = c.iterator(); while (i.hasNext()) { doSomething(i.next()); } Iterator i2 = c2.1terator(); while (i.hasNext()) { // Ошибка Второй цикл содержит ошибку копирования фрагмента программы инициализируется новая переменная цикла i2, но используется старая i, которая, к сожалению, остается в поле видимости. Полученный код компилируется без замечаний и выполняется без инициирования исключительных ситуаций, только вот делает не то, что нужно. Вместо того чтобы организовывать итерацию пос, второй цикл завершается немедленно, создавая ложное впечатление, что коллекция с пуста. И поскольку программа ничего об этой ошибке не сообщает, та может оставаться незамеченной долгое время. Если бы аналогичная ошибка копирования была допущена при применении цикла for, полученный код не был бы даже скомпилирован. для той области, где располагается второй цикл, переменная первого цикла уже была бы за пределами видимости for (Iterator i = с. iterator(); i. hasNext(); ) { doSomething(i.next()); } // Ошибка компиляции- символ не может быть идентифицирован Более того, если вы пользуетесь идиомой цикла for, уменьшается вероятность того, что вы допустите ошибку копирования, поскольку нет причин использовать в двух этих циклах различные названия переменных. Эти циклы абсолютно независимы, а потому нет никакого вреда от повторного применения названия для переменной цикла. На самом деле это даже стильно. Идиома цикла for имеет еще одно преимущество перед идиомой цикла while, хотя и не столь существенное. Идиома цикла for короче на одну строку, что помогает при редактировании уместить содержащий ее метод в окне фиксированного размера и повышает удобство чтения. Приведем еще одну идиом цикла для просмотра списка, которая минимизирует область видимости локальных переменных // Высокопроизводительная идиома для просмотра списков спр ои зв о ль н ы м доступом Эта идиома полезна для реализаций интерфейсов List с произвольным доступом, таких как ArrayList и Vector, поскольку для таких списков она, скорее всего, работает быстрее, чем приведенная выше "предпочтительная идиома. По поводу этой идиомы важно заметить, что в ней используются две переменные цикла i и n, и обе имеют абсолютно правильную область видимости. Вторая переменная важна для производительности идиомы. Без нее метод size пришлось бы вызывать при каждом Проходе цикла, что отрицательно сказалось бы на производительности. Если вы уверены, что список действительно предоставляет произвольный доступ, пользуйтесь этой идиомой. В противном случае производительность цикла будет падать в квадратичной зависимости от размера списка. Похожие идиомы есть и для других задач с циклами, например for (int i = 0, n = expensiveComputation(); i < n; i++) { doSomething(i) ; Ив этой идиоме применяются две переменные цикла. Вторая из них - n - служит для исключения' ненужных вычислений при повторных проходах цикла. Как правило, этой идиомой вы должны пользоваться в тех случаях, когда условие цикла содержит вызов некоего метода, но этот метод при каждом проходе гарантированно возвращает один и тот же результат. Последний прием, позволяющий уменьшить область видимости локальных переменных, заключается в создании небольших, четко позиционированных методов. Если в пределах одного итого же метода вы сочетаете две операции, то локальные переменные, относящиеся к одной из них, могут попасть в 'область видимости другой. Во избежание этого разделите метод на два, по одному методу для каждой операции. Изучите библиотеки и пользуйтесь ими Предположим, что нужно генерировать случайные целые числа в диапазоне от нуля до некоторой верхней границы. Столкнувшись с такой распространенной задачей, многие программисты написали бы небольшой метод примерно следующего содержания static Random rnd = new Random(); |