- PVSM.RU - https://www.pvsm.ru -
Всем привет!
Вашему вниманию предлагается перевод статьи уже известного на Хабре [1] автора. На этот раз он делится своими видением того, как часто нужно применять в своей повседневной разработке те или иные свойства языка Java.
Это не список обязательных к использованию каждым программистом особенностей языка. Скорее наоборот. Я разделил их на 3 группы: "для каждодневного использования", "для периодического использования" и "только для фреймворков и библиотек!". Правило простое: если вы понимаете, что используете указанные свойства чаще, чем рекомендуется, то, скорее всего, ваш код развивается по неправильному пути. Если же наоборот — вы редко используете какие-то свойства, чем я рекомендую, значит вы упускаете какие-то интересные и важные возможности языка.
Обратите внимание, что я говорю о разработке типичных серверных бизнес-приложений (JVM, JDK, вот это все) и не даю рекомендаций относительно каких бы то ни было фреймворков.
Классы, интерфесы, пакеты
Да-да, размещайте свой код в классах. Ещё со времени учебы вы наверяка помните, что класс — это данные и методы для работы с этими данными. Класс, который содержит только данные, называется «структурой». Класс, в котором только лишь методы, по сути просто логически объединяет некоторую функциональность. Используйте интерфейсы там, где это необходимо. Но подумайте дважды перед тем как создать интерфейс с одной единственной реализацией. Может стоит избавиться от посредника? Ну и наконец — размещайте классы и интерфейсы в пакетах, не забывая следовать соглашениям об именовании [4].
Статические методы
Не бойтесь их, но используйте только в качестве утилитных методов, которые не подразумевают наличие состояния. Никакой бизнес-логики в статических методах!
ExecutorService, thread pool
Нужно обязательно понимать и правильно использовать пулы потоков [5], очередей и объектов типа Future<T> [6] Не изобретайте велосипедов, реализуя собственные пулы. Они должны в первую очередь приходить вам в голову, когда вы слышите о Producer-Consumer
Семейство Atomic-*
Не используйте synchronized
для чтения/изменения счетчиков и ссылок. Семейство Atomic-*
весьма эффективно реализовано на основе «сравнения с обменом» [7] Убедитесь, что вы понимаете гарантии, которые предоставляют эти классы.
Шаблоны проектирования
Технически они не являются частью языка Java, но я обязан упомянуть о них в виду их важности. Вы должны знать, понимать и свободно использовать их, но в разумных пределах. (Так же как и интерфейсы, к слову). Шаблоны «Банды четырёх» [8] и корпоративных приложений [9] должны быть широко представлены в вашем коде. Но их использование должно подчиняться нуждам вашего приложения, а не наоборот.
Стандартные коллекции, в том числе многопоточные
Вам абсолютно необходимо знать и использовать встроенные коллекции, понимать разницу между List
, Map
и Set
. Использование потокобезопасных реализаций также не должно быть проблемой. Нужно знать основные характеристики производительности и иметь представление о деталях реализаций. Добавим сюда же знание различных реализаций BlockingQueue [10]. Параллельные вычисления и без того сложны, нужно пользоваться имеющимися средствами языка, а не изобретать свои велосипеды.
Встроенные аннотации
Они уже здесь и это надолго, поэтому смело пользуйтесь @Override
[11] и не забывайте про @Deprecated
[12]
Исключения
Используйте непроверяемые исключения (RuntimeExceptions
) для сообщении об ошибках и ненормальных состояниях системы, когда требуется явная реакция на происходящее. Учитесь жить с проверяемыми исключениями. Учитесь читать стек-трейсы.
try-with-resources
Познакомьтесь с этой замечательной конструкцией [13]. Реализуйте AutoCloseable [14], если вашему классу требуется освобождать ресурсы после завершения своей работы.
Блокирующий ввод/вывод
Пользуйтесь классами Reader [15]/Writer [16], InputStream [17]/OutputStream [18]. Осознавайте в чем отличия между ними, смело используйте буферизацию и другие декораторы.
На этом список «на каждый день» заканчивается. Если вы о чем-то не слышали вовсе или слышали краем уха — изучите, вам это обязательно пригодится.
Описанные далее свойства Java можно и нужно использовать, но без фанатизма. Если вы каждый день уже до обеда успеваете применить что-то из этого раздела — с архитектурой вашего приложения определенно что-то не так. Повторюсь — с точки зрения разработчкика back-end эти вещи бывают полезны, но редко.
Наследование и абстрактные классы
Честно, я редко пользуюсь наследованием и не скажу, что сильно скучаю по нему. Полиморфизм весьма гибко реализуется на основе интерфейсов, особенно с учетом всех недостатков абстракции в Java(* [19]) Также я предпочитаю наследованию композицию [20]. Слишком большие иерархии порождают трудно поддерживаемый код
[21]
Регулярные выражения
Некоторые программисты, когда встречаются с проблемой, думают «О, я воспользуюсь регулярными выражениями!» И получают две проблемы. [22] Мир без регулярных выражений был бы гораздо более скучным и громоздким. Они прекрасно подходят для парсинга регулярных множеств [23] (кроме HTML [24]), но опять же — с ними очень легко переусердствовать. Если вы целыми днями оперируете регулярными выражениями, вы выбрали неправильный инструмент. Хит всех времен:
public static boolean isNegative(int x) {
return Integer.toString(x).matches("-[0-9]+");
}
Semaphore [25], CountDownLatch [26], CyclicBarrier [27] и т.д.
Эти классы, конечно, на порядок более полезные, чем печально известная парочка wait()/notify()
. Но и они не защищают вас от ошибок при использовании многопоточности. Если вы часто используете этот механизм для синхронизации — самое время посмотреть в сторону потоко-безопасных коллекций или специализированных фреймворков.
Параметризованные типы (generics) в пользовательском коде
Вообще использование встроенных коллекций и других типов данных, поддерживающих параметризацию — обычное дело для программиста. Но здесь я говорю об использовании параметризации в вашем собственном коде, когда ваши методы принимают или возвращают generic types. Например:
public <T, F> ContractValidator<T extends Contract> T validate(Validator<T>, F object)
Иногда это необходимо, но опять таки — важно не перегнуть палку. Статическая типизация и безопасное преобразование типов всегда будут во главе угла, но чрезмерной параметризации лучше избегать.
Скриптовые языки в JVM
Знаете ли вы, что JDK имеет встроенный интерпретатор JavaScript [28]? И что вы можете на лету подключить другие языки, вроде Groovy и JRuby? В век стремительно меняющихся рынков порой не хватает времени даже на то, чтобы передеплоить приложение, и внедрение небольшого скрипта с возможностью его редактирования конечным пользователем бывает хорошей идеей. Но помните, что если количество скриптов превышает 1% от общего количества строк в системе, вам нужно быть готовым к трудностям при поддержке.
Java NIO [29]
Сложно использовать его правильно и ещё сложнее действительно получить от него выгоду. Иногда вы должны его использовать, чтобы выжать максимум по производительности и масштабированию. Но лучше все-таки пользоваться специализированными библиотеками, тем более что в большинстве случаев вполне достаточно обычного ввода-вывода.
synchronized
Не нужно злоупотреблять им по простой причине — чем больше таких блоков, тем чаще они выполняются и тем ниже производительность. Плюс всегда нужно быть хорошо уверенным в том, какой именно объект является мьютексом. Лучше используйте thread-safe коллекции и Atomic-*
.
Итак, я считаю перечисленные в этом разделе свойства языка важными и полезными, но не необходимыми для каждодневного использования. Если вы пользуетесь ими постоянно — это признак перегруженной архитектуры… либо неопытного разработчика. Способность упрощать приходит с опытом. Но если к вашей системе предъявляются какие-то особенные требования — тогда самое время перейти к третьей группе.
Для эффективного использования фреймворков и библиотек вы должны понимать принципы перечисленных ниже свойств языка. StackOverflow забит вопросами, ответы на которые можно легко найти в исходном коде самих фреймворков. Но «понимание» не обязательно означает «использование». Все эти штуки — они достаточно низкоуровневые и сложные, и даже в небольших количествах могут доставить немалую головную боль.
сокеты
да-да, вы не ослышались, именно сокеты. Вы должны понимать как работает стек TCP/IP, сессии, потоки, уметь правильно интерпретировать данные. Но избегайте прямой работы на сетевом уровне. Существуют сотни высокоуровневых библиотек для HTTP, FTP, NTP, SMB, e-mail… взять тот же Apache Commons net [30]. Вы вряд ли подозреваете, насколько сложно написать приличный HTTP клиент или сервер. А если вам нужен сервер для какого-то собственного протокола — я рекомендую ознакомиться с Netty [31]
reflection
В прикладном коде нет места работе на уровне внутреннего устройства классов и методов. Это жизненно необходимо фреймворкам, но совершенно не нужно лично мне. Reflection делает ваш код низкоуровневым, небезопасным и просто… неприятным. АОР решает большинство проблем. Я бы даже сказал, что простая работа с экземплярами типа Class<?>
уже «плохо пахнет».
динамические прокси и работа с байт-кодом
Proxy [32] — это здорово, но, как и reflection, лучше оставить работу с ним на откуп фреймворкам и библиотекам. Прокси — основа построения легковесного АОР. Если ваше бизнес-приложение непосредственно работает с байт-кодом(** [33]) (например через ASM [34] или CGLIB [35]) — вы в ж*пе мне остается только молиться за вас.
classloaders
… и все что с ними связано — в топку! опять таки — нужно понимать, как они работают, иерархию, байткод и т.д. Но если вы пишете свой загрузчик — это дорога в ад. Не то чтобы это было так сложно, но просто зачем? Пусть этим занимаются сервера приложений.
Object.clone()
Не помню, использовал ли я хоть раз этот метод за всю свою практику. А, вспомнил — совершенно точно ни разу не использовал! И даже не могу придумать, зачем он мне может понадобиться. Предпочитаю явные конструкторы копирования, а ещё лучше — неизменяемые объекты. А вам нужен именно clone? Похоже, кто-то застрял в девяностых…
native методы
В JDK можно найти несколько таких, в частности функцию вычисления синуса [36] Но Java давно уже не тот тугодум, что раньше, даже наоборот. И я не могу придумать, какие задачи не могут решить стандартная или сторонние библиотеки. Плюс, нативные методы трудны сами по себе, порождают множество низкоуровневых ошибок, особенно в части работы с памятью.
самописные коллекции
правильно реализовать коллекцию в соответствии со всеми контрактами оригинального JavaDoc (внезапно) непростая задача [37] Hibernate использует собственные реализации, но зачем они могут понадобиться вам — я не знаю.
ThreadLocal [38]
Фреймворки и библиотеки используют эту технику довольно часто, но вы должны избегать этого по двум причинам. Первая — под маской ThreadLocal часто скрывается эдакая полу-глобальная переменная. Это усложняет понимание и тестирование кода. Вторая — ThreadLocal порождает утечку памяти в случае некоректной очистки. Для примера читаем тут [39], тут [40], тут [41] и ещё вот тут [42] и так далее…
WeakReference [43] и SoftReference [44]
Эти классы достаточно низкоуровневые и хорошо подходят для реализации кэшей, плотно интегрированных со сборщиком мусора. К счастью, существует множество open-source библиотек для подобных кэшей, так что нет нужды писать ещё один самостоятельно. Достаточно просто знать, что такие классы есть и представлять, как они работают.
Пакеты com.sun.*
и sun.*
, особенно sun.misc.Unsafe
[45]
Держитесь подальше от торфяных болот этих пакетов, потому что… да просто подальше и все! Это сугубо специальные, недокументированные классы без гарантии сохранения обратной совместимости в будущем. Просто представьте, что их нет. Да и зачем бы вам мог понадобиться Unsafe
[46]?
Собственно, на этом все. Конечно, все это моё абсолютнейшее ИМХО. Если вы чувствуете, что что-то не на своем месте, либо я забыл о чем-то существенном — прошу в комментарии, чтобы в дальнейшем я мог составить некое справочное руководство для проведения code-review или для помощи в оценке проекта.
(*) приветствуется более точный перевод выражения "a painful lack of traits in Java"
Mocito [47] я тоже не осилил
Автор: monzdrpower
Источник [48]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/java/28001
Ссылки в тексте:
[1] известного на Хабре: http://habrahabr.ru/search/?q=Nurkiewicz
[2] Image: http://1.bp.blogspot.com/-d114xzObirE/UI2i1VPARNI/AAAAAAAAAog/ipIpS0hLNfE/s1600/triangle.png
[3] «Большая сила налагает большую ответственность»: http://en.wikipedia.org/wiki/Uncle_Ben#.22With_great_power_comes_great_responsibility.22
[4] соглашениям об именовании: http://docs.oracle.com/javase/tutorial/java/package/namingpkgs.html
[5] пулы потоков: http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Executors.html
[6] Future<T>: http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Future.html
[7] «сравнения с обменом»: http://ru.wikipedia.org/wiki/%D0%A1%D1%80%D0%B0%D0%B2%D0%BD%D0%B5%D0%BD%D0%B8%D0%B5_%D1%81_%D0%BE%D0%B1%D0%BC%D0%B5%D0%BD%D0%BE%D0%BC
[8] «Банды четырёх»: http://www.amazon.com/gp/product/0201633612/ref=as_li_ss_tl?ie=UTF8&tag=javaandneighb-20&linkCode=as2&camp=1789&creative=390957&creativeASIN=0201633612
[9] корпоративных приложений: http://rcm.amazon.com/e/cm?lt1=_blank&bc1=000000&IS2=1&bg1=FFFFFF&fc1=000000&lc1=0000FF&t=javaandneighb-20&o=1&p=8&l=as4&m=amazon&f=ifr&ref=ss_til&asins=0321200683
[10] BlockingQueue: http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/BlockingQueue.html
[11] @Override
: http://docs.oracle.com/javase/7/docs/api/java/lang/Override.html
[12] @Deprecated
: http://docs.oracle.com/javase/7/docs/api/java/lang/Deprecated.html
[13] этой замечательной конструкцией: http://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html
[14] AutoCloseable: http://docs.oracle.com/javase/7/docs/api/java/lang/AutoCloseable.html
[15] Reader: http://docs.oracle.com/javase/7/docs/api/java/io/Reader.html
[16] Writer: http://docs.oracle.com/javase/7/docs/api/java/io/Writer.html
[17] InputStream: http://docs.oracle.com/javase/7/docs/api/java/io/InputStream.html
[18] OutputStream: http://docs.oracle.com/javase/7/docs/api/java/io/OutputStream.html
[19] *: #note1
[20] наследованию композицию: http://en.wikipedia.org/wiki/Composition_over_inheritance
[21] Image: http://1.bp.blogspot.com/-Qh7Yx5wvkEg/UI2i0dHdHhI/AAAAAAAAAoc/ezF1j5UUpT4/s1600/spring.png
[22] Некоторые программисты, когда встречаются с проблемой, думают «О, я воспользуюсь регулярными выражениями!» И получают две проблемы.: http://en.wikiquote.org/wiki/Jamie_Zawinski
[23] регулярных множеств: http://ru.wikipedia.org/wiki/%D0%A0%D0%B5%D0%B3%D1%83%D0%BB%D1%8F%D1%80%D0%BD%D0%BE%D0%B5_%D0%BC%D0%BD%D0%BE%D0%B6%D0%B5%D1%81%D1%82%D0%B2%D0%
[24] кроме HTML: http://stackoverflow.com/a/1732454
[25] Semaphore: http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Semaphore.html
[26] CountDownLatch: http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/CountDownLatch.html
[27] CyclicBarrier: http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/CyclicBarrier.html
[28] JDK имеет встроенный интерпретатор JavaScript: http://docs.oracle.com/javase/6/docs/technotes/guides/scripting/programmer_guide/index.html
[29] Java NIO: http://docs.oracle.com/javase/7/docs/api/java/nio/package-summary.html
[30] Apache Commons net: http://commons.apache.org/net/
[31] Netty: https://netty.io/
[32] Proxy: http://docs.oracle.com/javase/7/docs/api/java/lang/reflect/Proxy.html
[33] **: #note2
[34] ASM: http://asm.ow2.org/
[35] CGLIB: http://cglib.sourceforge.net/
[36] функцию вычисления синуса: http://docs.oracle.com/javase/7/docs/api/java/lang/StrictMath.html#sin(double)
[37] непростая задача: http://stackoverflow.com/questions/12761532
[38] ThreadLocal: http://docs.oracle.com/javase/7/docs/api/java/lang/ThreadLocal.html
[39] тут: https://jira.mongodb.org/browse/JAVA-130
[40] тут: http://stackoverflow.com/questions/5292349
[41] тут: https://issues.apache.org/jira/browse/AXIS-935
[42] тут: http://jira.qos.ch/browse/LOGBACK-450
[43] WeakReference: http://docs.oracle.com/javase/7/docs/api/java/lang/ref/WeakReference.html
[44] SoftReference: http://docs.oracle.com/javase/7/docs/api/java/lang/ref/SoftReference.html
[45] sun.misc.Unsafe
: http://www.docjar.com/docs/api/sun/misc/Unsafe.html
[46] зачем бы вам мог понадобиться Unsafe
: http://stackoverflow.com/questions/5574241
[47] Mocito: http://code.google.com/p/mockito/
[48] Источник: http://habrahabr.ru/post/167291/
Нажмите здесь для печати.