- •Содержание
- •1. Введение
- •2. Создание и уничтожение объектов
- •3. Методы, общие для всех объектов
- •4. Классы и интерфейсы
- •5. Замена конструкций на языке c
- •6. Методы
- •7. Общие вопросы программирования
- •8. Исключения
- •9. Потоки
- •10. Сериализация
- •11. Литература
- •Глава 1
- •Глава 2 Создание и уничтожение объектов
- •Рассмотрите возможность замены конструкторов статическими методами генерации.
- •Свойство синглтона обеспечивайте закрытым конструктором
- •Отсутствие экземпляров обеспечивает закрытый конструктор
- •Не создавайте дублирующих объектов
- •Уничтожайте устаревшие ссыпки (на объекты)
- •Остерегайтесь методов flnalize
- •Глава 3 Методы, общие для всех объектов
- •Переопределяя метод equals, соблюдайте общие соглашения
- •Переопределяя метод equals, всегда переопределяйте hashCode
- •Всегда переопределяйте метод toStrlng
- •Подумайте над реализацией интерфейса ComparabIe
- •Глава 4 Классы и интерфейсы
- •Сводите к минимуму доступность классов и членов
- •Предпочитайте постоянство
- •Предпочитайте компоновку наследованию
- •Предпочитайте интерфейсы абстрактным классам.
- •Используйте интерфейсы только для определения типов
- •Предпочитайте статические классы-члены нестатическим
- •Глава 5
- •3Амена констрvкций на языке с
- •3Аменяйте структуру классом
- •3Амеияйте объедииеиие иерархией классов
- •/* Помещает данные в одно из полей объединения ... */
- •3Аменяйте конструкцию enum классом
- •Указатель на функцию заменяйте кпассом и интерфейсом
- •Глава 6 Методы
- •Проверяйте достоверность параметров
- •При необходимости создавайте резервные копии
- •Тщательно проектируйте сигнатуру метода
- •Перегружая методы, соблюдайте осторожность
- •Возвращайте массив нулевой длины, а не null
- •Для всех открытых элементов арi пишите dос - комментарии
- •Глава 7 Общие вопросы программирования
- •Сводите к минимуму область видимости локальных переменных
- •Изучите библиотеки и пользуйтесь ими
- •Не используйте строку там, где более уместен иной тип
- •При конкатенации строк опасайтесь потери производительности
- •Для ссыпки на объект используйте его интерфейс
- •Соблюдайте осторожность при использовании машинно-зависимых методов
- •Соблюдайте осторожность при оптимизации
- •Глава 8 Исключения
- •Используйте исключения лишь в исключительных ситуациях
- •Применяйте обрабатываемые исключения для восстановления, для программных ошибок используйте исключения времени выполнения
- •Избегайте ненужных обрабатываемых исключений
- •Предпочитайте стандартные исключения
- •Инициируйте исключения, соответствующие абстракции
- •Для каждого метода документируйте все инициируемые исключения
- •В описание исключения добавляйте информацию о сбое
- •Добивайтесь атомарности методов по отношению к сбоям
- •Не игнорируйте исключений
- •Глава 9 Потоки
- •Синхронизируйте доступ потоков к совместно используемым изменяемым данным
- •Избегайте избыточной синхронизации
- •Никогда не вызывайте метод wait вне цикла
- •Не попадайте в зависимость от планировщика потоков
- •При работе с потоками документируйте уровень безопасности
- •Избегайте группировки потоков
- •Глава 10 Сериализация
- •Соблюдайте осторожность при реализации интерфейса SerializabIe
- •Рассмотрите возможность использования специализированной сериализованной формы
- •Метод readObject должен создаваться с защитой
- •При необходимости создавайте метод readResolue
Тщательно проектируйте сигнатуру метода
В этой статье приводятся советы по проектированию API, не удостоившиеся собственной статьи. Собранные вместе, они помогут сделать ваш АРI не dтоль подверженным ошибкам, более удобным и простым в изучении.
118
Тщательно выбирайте названия методов. Названия всегда должны соответствовать стандартным соглашениям по именованию (статья 38). Вашей главной целью должен быть выбор таких имен, которые будут понятны и согласуются с остальными названиями в том же пакете. Второй целью должен быть выбор имен, отвечающих более общим соглашениям, если таковые имеются. В случае сомнений смотрите руководство по API библиотек языка Jаvа. Несмотря на массу противоречий', которые неизбежны если учитывать размер и возможности библиотек, здесь также существует консенсус. Бесценным источником является "The Java Developers Almaпac" Патрика Чана (Patrick Chan) [ChanOO], содержащий декларации всех без исключения методов в библиотеках платформы Jаvа, индексированные в алфавитном порядке, если, к примеру, вы сомневаетесь, назвать ли метод remove или delete, то, бегло про смотрев указатель в этой книге, поймете, что, без всяких сомнений, выбор remove лучше: есть сотни методов, чьи названия начинаются со слова remove, и лишь жалкая горстка имен, которые начинаются со слова delete.
Не заходите слишком далеко в погоне за удобством своих методов. Каждый метод должен выполнять собственную часть работы. Избыток методов делает класс сложным для изучения, использования, описания, тестирования и сопровождения. В отношении интерфейсов это верно вдвойне: большое количество методов усложняет жизнь и разработчикам, и пользовате,лям. для каждого действия, поддерживаемого вашим типом, создайте полнофункциональный метод. Сокращенный вариант операции рассматривайте лищь в том случае, если он будет использоваться часто. Если есть сомнения, забудьте об этом варианте.
Избегайте длинного перечня параметров. Правило таково, что на практике три параметра нужно рассматривать как максимум, и чем параметров меньше, тем лучше. Большинство программистов не способны помнить более длинные списки параметров. Если целый ряд методов превышает этот предел, вашим API невозможно будет пользоваться, не обращаясь беспрестанно к его описанию. Особенно вредны длинные последовательности параметров одного и того же типа. И это не только потому, что ваш пользователь не сможет запомнить порядок их следования. Если он по ошибке поменяет их местами, его программа все равно будет компилироваться и работать. Только вот делать она будет совсем не то, что хотел ее автор.
для сокращения слишком длинных списков параметров можно использовать два приема. Первый заключается в разбиении метода на несколько методов, каждому ИЗ которых нужно лишь какое-то подмножество его параметров. Если делать это неаккуратно, может получиться слишком много методов, однако этот же прием помогает сократить количество методов путем увеличения их ортогональности. Например, рассмотрим интерфейс java.util.List. У него нет методов для поиска индекса первого и последнего элемента в подсписке, каждому из них потребовалось бы по три параметра. Вместо этого он предлагает метод subList, который принимает два параметра И возвращает представление подсписка. Для получения желаемого результата можно объединить метод subList с методами indexOf и lastlndexOf, принимающими по одному параметру. Более того, метод subList можно сочетать с любыми другими Методами экземпляра List, чтобы выполнять самые разные операции для подсписков. Полученный АРI имеет высокое соотношение мощности и размера.
119
Второй прием сокращения чрезмерно длинных перечней параметров заключается в создании вспомогательных классов, обеспечивающих агрегирование параметров. Обычно эти вспомогательные классы являются статическими классами-членами (статья 18). Данный прием рекомендуется использовать, когда становится понятно, что часто возникающая последовательность параметров на самом деле представляет некую отдельную сущность. Предположим, что вы пишите класс, реализующий карточную игру, и выясняется, что постоянно передается последовательность из двух параметров: достоинство карты и ее масть. И ваш АРI и содержимое вашего класса, вероятно, выиграют, если для представления карты вы создадите вспомогательный класс и каждую такую последовательность параметров замените одним параметром, соответствующим этому вспомогательному классу.
Выбирая тип параметра, отдавайте предпочтение интерфейсу, а не классу. Если для декларации параметра имеется подходящий интерфейс, всегда используйте его, а не класс, который реализует этот интерфейс. Например, нет причин писать метод, принимающий параметр типа Hashtable, лучше использовать Мар. Это позволит вам передавать этому методу Hashtable, HashMap, ТгееМар, подмножество ТгееМар, и вообще любую, пока еще не написанную реализацию интерфейса Мар. Применяя же вместо интерфейса класс, вы навязываете вашему клиенту конкретную реализацию и вынуждаете выполнять ненужное и потенциально трудоемкое копирование в том случае, если входные данные будут представлены в какой-либо иной форме.
Объекты-функции (статья 22) применяйте с осторожностью. Некоторые языки, в частности Smalltalk и различные диалекты Lisp, поощряют стиль программирования, изобилующий объектами, представляющими функции, которые можно применять к другим объектам. Программисты, имеющие опыт работы с такими языками, могут поддаться соблазну и использовать тот же стиль для Java, но это не слишком хороший выбор. Проще всего создавать объект-функцию с помощью анонимного класса (статья 18), однако даже это приводит к некоторому загромождению синтаксиса и ограничивает мощность и производительность в сравнении со встроенными управляющими конструкциями (inline control construct). Более того, стиль программирования, когда вы постоянно создаете объекты-функции и передаете их из одного метода в другой, расходится с господствующей тенденцией, а потому, если вы будете придерживаться этого стиля, другим программистам будет трудно разобраться в вашем коде. Это не означает, что объекты-функции не имеют права на использование. Напротив, они важны для многих мощных шаблонов, таких как Strategy [Саmmа95, стр. 315] и Visitor [Саmmа95, стр. 331]. Точнее говоря, объекты-функции следует применять только при наличии веских причин.