Как стать автором
Обновить

Комментарии 68

Ну а чо? Молодец. В целом верно.
Сколько комплексов и насаждений чужих мыслей. Не статья, а — морализаторство на тему программирования.

Думайте свой головой. Опыт придёт со временем. И он будет гораздо лучше того, что здесь тезисно описан.
Когда будет опыт, то да. Но часто начинающие начинают лепить такое месиво из рефлексии, многопоточности и замудреной иерархии, что черт ногу сломит…
Как «налепит», так и выкинет, думая своей головой. В противном случае привыкнет, что за ним придут другие и будут поддерживать всё его творение. Научиться сначала видеть и прогнозировать жизненный цикл результатов своего труда — это на начальном этапе поважнее «морализаторства» на тему как писать нужно и не нужно.
Напишет и уйдет, кому-то поддерживать. Такой способ может и нормален, если человек сам по себе работает.
Учиться на своих ошибках можно, но это дорого.
Такие статьи мало что дают новичкам.
Как правило — прививание чужого мнения и, как следствие, перекрытие своего личного опыта этим самым «авторитетным» мнением.
Поэтому братья за подобные измышления всерьез имеет смысл только в том случае, если Вы действительно хотите написать полноценный, качественный труд, рецензированный и одобренный профессионалами, редакторами и т.д. и т.п.
Вы не поверите, очень многие программисты среднего и чуть выше среднего уровня знают по своему опыту все то, что Вы можете предложить в такой вот короткой форме, без конкретных сравнений «за» и «против» в каждом отдельном случае. А программистам низкого уровня категорически не советую читать подобные статьи. Лучше воспользоваться авторитетным источником — там гораздо более полно и толково описано, и при этом поясняется, почему — а именно это понимание и нужно новичкам.
>>Такие статьи мало что дают новичкам.
Как минимум, они дают направление. Например, когда человек с опытом разработки N лет, не знает элементарных практик, так как не читал ни книг, ни статей, не обменивался опытом (варился в «своем соку»), потому как не знал обо всем этом — «справочная» инф. может дать хороший толчок в правильном направлении.

>>что Вы можете предложить в такой вот короткой форме
Это не моя статья и не мой перевод…
И правильно, в общем, делают. Надо упражняться и осваивать. Лучше, правда, «не за счёт меня», но тут так или иначе «кому-то придётся быть первым».
Отличная статья.
Про мокито, я бы так перевел:
>>If your business application (not framework or library, even Mockito uses these techniques!) requires byte code generation or manipulation (e…
Если ваше бизнес-приложение (не фреймворк или библиотека, как раз Mockito использует эту технику!) требует генерации байт-кода или манипуляции с ним (напр…
>>a painful lack of traits in Java
тут, видимо, имеется ввиду типаж (traits, трейты )
Перевод: ", особенно ввиду болезненного отсутствия трейтов в Java"
Желающие trait'ов, кстати, могут радоваться — некоторое их подобие будет в Java 8 (default-методы в интерфейсах).
спасибо за перевод
<facepalm>
Для каждодневного использования

Шаблоны проектирования
Семейство Atomic-*

Для периодического использования

Наследование и абстрактные классы
</facepalm>

Программистов с шаблонометом к коду подпускать не то что на пушечный выстрел нельзя, их надо истреблять в раннем детстве из рогатки. Хромают ваши рекомендации.
Наследование ломает контракт. Если другие классы воспринимают родительский класс как набор публичных методов, то классу потомку открывается реализация, что бывает нужно далеко-далеко не всегда.
Для контрактов существуют интерфейсы.
Шаблоны проектирования — это печальное следствие не-функциональности языка программирования. Когда блок кода нельзя передать как параметр, приходится копипастить «обвязку» для него. Если бы не это, шаблоны проектирования выродились бы в библиотечные функции или вообще в идиомы языка программирования, как шаблон «стратегия» в языке Java.
«Да-да, размещайте свой код в классах» — да, это классная рекомендация. Еще бы знать, как можно разместить код вне класса.

Наследование, абстрактные классы и дженерики — это как раз для постоянного использования. Видимо, автор еще не освоил эти темы, и подсознательно пытается себя оправдать.
>Еще бы знать, как можно разместить код вне класса.

Я так понял, автор имел в виду, что классы без логики и классы без данных — это не «настоящие» классы. И рекомендует использовать именно канонические, с логикой и данными.
а что, правда WeakReference и SoftReference в бизнес-логике не нужны? оО
Редкостная чушь.

Такое чувство, что совсем молодой иностранный специалист написал свои первичные соображения после относительно непродолжительного использования Java. Абстрактные классы оказывается есть вещь второй важности. Как и наследование с generics. :)

А наш человек старательно перевёл это, ибо полагает, что всё что пишет иностранец — есть круто по определению. Как при Петре 1. Этот молодой иностранный парень имеет право писать всё что хочет в своём блоге. Но к чему это распространять?

Если кто-то считает, что оригинальный автор уже силён в разработке, откройте по ссылке его блог и ужаснитесь как это всё забавно выглядит. :)

Я согласен с тем что статья — «Редкостная чушь.», но все же в чем я согласен с автором статьи — в отношении к абстрактным классам.
Как бы периодически занимаясь review кода коллег понимаешь, что этот паттерн как правило применяется крайне неудачно — в лучшем случае код не получает бенефитов от наследования, в худшем — получает существенные минусы. Связано с тем что люди часто используют абстрактные классы тогда, когда их можно было бы успешно заменить композицией, что плохо
Абстрактный класс не является паттерном. Абстрактный класс, это синтаксическая возможность языка объявить некий служебный класс (который не может инстанциироваться), который может быть воткнут в иерархию между интерфейсом и полной его, интерфейса, реализацией, дабы застолбить базовую реализацию некоторых методов. Может служить заменой интерфейсу (и служит, скажем, в с++). Никакого отношения к композиции он не имеет. Выбор между наследованием и композицией это немножко из другой оперы.
Если в каких-то кусках кода можно выделить общие черты, значит это паттерн. Это единственное логичное определение, в конце концов, вы же не считаете что «паттерн» это все что было упомянуто в трех-четырех книжках, в названии которых встречается слово «паттерн»?
Ну и собственно суть моего поста, к которому вы возразили, была в том, что абстрактные классы часто применяются не в тему, по крайней мере по моим наблюдениям.
Не нравится называть это паттерном, можете называть хоть артишоком.
Абстрактный класс реализуется на уровне синтаксиса языка. Туда выносится код, который потенциально будет общим и полезным для большого количества наследников. Просто общий код класса можно в любой метод вынести.

Логически, постулирование некоего класса как абстрактный, конечно же можно назвать паттерном. Но в «быту», его таковым уже не называют, в силу того, что он давно перешёл в разряд тривиально естественных и полезных вещей, ценность которых давно не оспаривается.
> Туда выносится код, который потенциально будет общим и полезным для большого количества наследников

О, господи. Т.е. наличие общего кода между наследниками означает, что нужно кидаться выделять некий абстрактный класс, который бы этот код содержал? Извините, но из-за таких как вы и возникают совершенно бессмысленные и перегруженные иерархии классов. Вы просто не понимаете, зачем нужно наследование.
А из-за таких как вы на проекте начинается паника на ровном месте. :)

Бездумно кидаться и что-то там лихорадочно «выделять» вобще в жизни никогда не нужно, Везде нужно вначале думать.

А абстрактный класс действительно позволяет собрать в одно место те базовые реализации методов, которые будут полезны потомкам. Видя, что некий метод имеет реализацию в абстрактном классе, вы уже знаете, что скорее всего эта реализация закрывает большинство потребностей и вам самим её делать не нужно. Опять же, на уровне абстрактного класса происходит некое внутреннее структурирование методов (интерфейс то ведь содержит только видимые паблик методы). Таким образом, вы сразу видите ту логику, которую желательно разнести по разным методам. Опять же, чётко выделяются те методы, которые вы просто обязаны реализовать.
Таким образом, абстрактный класс позволяет организовать код логически более понятно и безопасно. Ни к какой перегруженности иерархии эта возможность сама по себе не ведёт. Иерархию можно легко сделать избыточной и не имея возможности постулировать класс абстрактным.
> А абстрактный класс действительно позволяет собрать в одно место те базовые реализации методов, которые будут полезны потомкам

Это все следствие. Нужно понимать саму суть наследования. Применять абстрактные классы нужно тогда, когда действительно между потомком и родителем есть отношение «is». Пролистайте, пожалуйста, презентацию www.slideshare.net/biBIGine/agile-7158754 (слайды 62-75). Там описаны примеры неправильного использования наследования. Если вы согласны с тем, что там написано, значит мы просто спорим об одном и том же, но говорим на разных языках
Вот к чему вы мешаете в одну кучу понятие абстрактного класса и сам общий принцип наследования?

Вот эта ваша фраза:… Применять абстрактные классы нужно тогда, когда действительно между потомком и родителем есть отношение «is»… она звучит странно, ибо касается не непосредственно абстрактных классов, а постулирует принцип использования наследования как приём проектирования в целом. И причём тут это?

Когда мы создаём абстрактный класс, мы предполагаем, что в этой части проекта наследование УЖЕ выбрано как удобный и подходящий подход. Правильно это было сделано или нет, но это проектное решение другого уровня.

Абстрактный же класс (как выразительная особенность языка) позволяет определить наследование более безопасно и понятно.
Моя цепочка суждений такова: абстрактные классы — это базовые классы (абстрактные классы без наследников бессмысленны, не правда ли?). Базовые классы выделяются тогда, когда есть отношение «частное-общее» + общее само по себе не бесполезно (т.е. например, если есть программа, в которой все объекты — это кошки, то бессмысленно выделять класс Животное, если оно ни в одном месте в коде не будет использоваться). Так вот, такое правильное отношения наследования, на самом деле, встречается довольно редко (по моему опыту). А значит, абстрактные классы нужно применять очень умеренно, о чем и говорит автор статьи.
Угу. Если у них есть есть абстрактный класс, то поди и наследование присутствует. А раз присутствует наследование, то значит они ж его, стопудов, неправильно сделали. Прочту-ка и им лекцию на всякий случай на будущее…

Муж жене:
— Дорогая, ты не права.

— Мама, он меня сукой обозвал!!!
Мне показалось, что вы не понимаете сути наследования, вот и «прочитал лекцию». Вы же, кстати, никак не защитили свой тезис о том, что абстрактные классы — вещь первой важности. Наехали на автора, а доводов никаких не привели
Перечитайте внимательно. Я пояснил для чего они нужны. И да, если я принял решение об использовании наследования, то далее решаю не вынести ли что-то в абстрактный класс.
На основании чего вы решили, что я не понимаю сути наследования, мне осталось непонятным.
Вы пояснили, для чего они нужны. Но не пояснили, почему не прав автор.
Вот именно — я пояснил для чего они нужны. А вот автор не пояснил, а посоветовал типа не использовать часто. Глубокий такой совет… :)
Ну т.е. проблема в том, что он не расписал более подробно. Это да
А что, тогда, по Вашему мнению делать с принципом DRY? Делегировать?
Вы можете сами ответить на свой вопрос. Есть два класса, совершенно никак не связанных друг с другом. Так получилось, что у них есть общий код. Что вы в этом случае сделаете? Выделите общую функциональность в синтетический базовый абстрактый класс, который по своей сути лишен смысла и нигде в проекте не будет фигурировать как отдельная сущность?
И что с того, что у них общий код? С чего вы решили, что все вокруг такие балваны, что из-за общего кода сразу бросаются городить иерархию классов, и вам непременно нужно начать крайне решительно с этим бороться? :D
> все вокруг такие балваны
Так вот именно, что полные болваны. В университете людям прочитали курс, в котором было сказано, что «Туда выносится код, который потенциально будет общим и полезным для большого количества наследников». А про то, что надо думать головой, не было сказано ничего. Вот и выносят что попало в базовые классы. Проект пухнет от десятиуровневой иерархии наследования, и хочется кого-нибудь пристрелить.
Бывает и такое, но в данном-то случае речь шла совсем о другом. Не так-ли? Посему и не понятно к чему вы нам лекцию о правильном употреблении наследования решили вдруг прочесть. :)
Речь изначально шла о частоте применения абстрактных классов. Я утверждаю, что это частота обусловлена лишь тем, что люди просто-напросто втыкают эти абстрактные классы где попало. На самом же деле, если бы они применяли абстрактные классы только там, где это действительно необходимо, то тогда эта частота резко бы уменьшилась. А это означает, что ваше мнение о статье как о «редкостной чуши», все-таки несколько гиперболизировано, не находите? Разве не прав в том, что наследование нужно применять крайне осторожно?

Цитата:

«Для периодического использования
Описанные далее свойства Java можно и нужно использовать, но без фанатизма»

Разве вы не согласны с этим?
Я не руководствуюсь критериями типа «с фанатизмом», «без фанатизма»…

Нужно, буду использовать. Не нужно — не буду. Если нужно — буду использовать столько сколько нужно. Фанатизм тут не причём. Я фанатизмом нигде не руководствуюсь и абстрактные советы типа… используйте но без фанатизма… полезными не считаю.
А как вы определяете, что это нужно, а то не нужно? Проектирование — вещь грязная: один программист может сделать отличную программу одним способом, другой — тоже отличную программу совершенно по-другому. Первый может каждый второй класс сделать дженериком, второй — вообще их не использовать. Нету однозначного критерия «правильно — неправильно»
'has a' and 'is a'
Дом является Строением — используем наследование.
Дом обладает ванной — используем композицию.

Субъективно, наследование делает код сложночитаемым и плохо поддерживаемым.
Особенно если принципы дизайна (открытости\закрытости и пр.) не учитывались при написании кода.
Прошу прощения, но generic'и в Java ужасны чуть менее, чем полностью. Чрезмерное их использование может превратить проект в ад. Так что в этом плане я согласен с автором. Ну и «редкостная чушь» — это вы перегнули. В статье есть много полезных советов (хоть и не все советы полезны). Учитесь читать не по диагонали.
О! Несомненно! Поругать джава generics есть дело чести любого квалифицированного специалиста, ознакомившегося с этим языком (тэскэть, визитная карточка настоящего профи!), но, справедливости ради, должен заметить, что джава generics не только вредны, но и полезны.

Их просто не нужно путать с C++ templates. В templates, компилятор создаёт экземпляр для каждого употребления шаблона в коде и информация о типе доступна на рантайме. В джава generics, информация на рантайме уже не доступна.

Посему, не комплексуя по поводу того, что generics слабее чем templates, разумно использовать generics во многих местах, давая информацию о типах компилятору, дабы он смог дополнительно проверить типобезопасность. Ничего плохого в этом нет и пользоваться этим полезно.
Так и автор статьи не спорит об этом. В чем не прав автор, говоря о том, что дженерики можно периодически использовать?
Я же на ваш пост отвечал, а не прямо к статье. Тем самым согласившишь тут с автором.
Прочитал статью. Безумно рад, что в свое время ушел из разработчиков «типичных серверных бизнес-приложений». Их жизнь — скука, которая убивает. Имхо.
Интересная разкладка. Судя по ней, суждено мне быть разработчиком фреймворков =).
А вообще — не согласен, что NIO переместили в периодичное. В плане работы с сетью (да и сфайлами, в случае масового одновременного записи/чтения и прочего highload) — это очень приятная штука. Понятно, что для одного файла/соединения нет смысла использовать NIO, но в случаях, когда оно правда нужно — очень облегчает работу.
Хотя может автор исходил из соображений, что не каждый день людям нужно писать приложения, рассчитаные на активный файловый-сетевой IO, тогда согласен.
Зачем использовать NIO, если есть Netty/Grizzly?
Например, из чистого интереса к тому, как и что происходит немного глубже фреймворка. Вообще у меня наверное предрасположеность к написанию велосипедов — большинство фреймворков/библиотек мне кажутся слишком громадными и перегружеными функционалом, как посмотрю к ним в документацию — всякое желание испльзовать пропадает.
… Но Java давно уже не тот тугодум, что раньше, даже наоборот. И я не могу придумать, какие задачи не могут решить стандартная или сторонние библиотеки…

Ох уж этот религиозный фанатизм… слабая фантазия или просто недостаток опыта?
Вот из реальной жизни таск:
Двухканалтьный АЦП подцепленный по USB выдает отсчеты по простенькому протоколу.
Нужно все считать, проверить целостность и визуализировать данные просто осцилограммой и плюс спектр (fft) в другом окне.
Платформа: Вин ХР Pentium4, 512 метров память. кстати писюк еще не полное борохло — может и хужее быть у зака…

И идет этот очередной юный знаток жабы со своими знаниями в анус, ну или учить что-то нативное — типа си или дельфей — но там тоже надо соображать в особенностях платформы иначе будет все равно тормозить…

Жаба как и НЕТ все тот же тугодум — просто процы стали быстрее :-)
Абсолютно совершенных платформ, закрывающих все типы задач, в жизни не бывает. Можно реализовать вашу задачу на C, но придёт другой умник и веско скажет, что у заказчика то на самом деле калькулятор «Электроника» и писать всё нужно было в его двоичных кодах. А стало быть все остальные языки «сосут» и вообще ерунда и тугодумы…
Ну и так далее.
Какие си, какие дельфи? Вы разве не знаете, что всё, ВСЁ надо переписать на ассемблере?!
А если операционная система не Windows, а другая, поддерживающая USB 3.0 (к примеру FreeBSD или GNU/Linux), то нужно по-любому менять программиста.
А я думал у меня одного clone-офобия.
Композиция очень хорошая штука, но в Java нет автоматического delegate. В итоге приходится для каждого метода вручную писать вызов к delegate.

По возможности использовать только immutable объекты. Особенно там, где дело касается POJO. Помнить, что для сложных структур данных Java Beans — худшее, что можно только придумать, поскольку не делают различия между ссылкой и значением. Геттеры, возвращающие коллекции, должны либо врапить их в unmodifiable() либо создавать копию коллекции.

Использовать преимущественно static factory methods, а не конструкторы для создания новых экземпляров.

Клонирование имеет смысл, когда оно глубокое (весь граф). Его проще сделать через сериализацию-десериализацию.
Использовать преимущественно static factory methods, а не конструкторы для создания новых экземпляров
Почему:
1. Позволяет прозрачно добавить пулинг объектов или кеш в любой момент, или сконвертировать в singleton.
2. При комбинации более понятные названия методов лучше, чем перегрузка в разных конструкторах. Например, такие:
Color.newRGB(...)
Color.newHSL(...)
Color.fromString( String name )
Color.fromCSS( String style )
3. Позволяет подменить возвращаемый объект его специфической имплементацией.
4. Позволяет не писать дважды generics-параметры при инициализации:
Pair<String,Integer> pair = new Pair<String,Integer>( «test», 1 )
Pair<String,Integer> pair = Pair.create( «test», 1 )
где factory method:
public static <T1,T2> Pair<T1,T2> create( T1 v1, T2 v2 );
5. Многие фреймворки требуют дефолтный no-args конструктор для java beans. Если Вы создали свой конструктор с параметрами, также требуется добавить дефолтный no-args. Если же у вас factory methods, то дефолтный конструктор присутствует всегда.
Ну а если у вас не один из этих кейсов, то вы все равно юзаете статические методы, а не конструкторы? По инерции?
Нет ессно.
Ну вот, то есть их не надо «использовать приемущественно», их надо использовать в (вписать).
На практике использование immutable, увы, остается теорией…
А еще в контексте этой статьи, наверное стоит упомянуть имя книги, где одно оглавление напоминает Ваши перечисленные пункты — это Effective Java написанная Joshua Bloch, который в свое время реализовал большинство коллекций в JDK. На мой взгляд — Must Read.
Я подозреваю, что то, про что здесь написано «мне остаётся только молиться за вас» и «это дорога в ад», для практики программирования на С++ — в общем, рядовые вещи…
лучше оставить работу с ним на откуп фреймворкам и библиотекам
Про прокси, рефлексию и классозагрузчики. Надо понимать, чем опасно написание собственных фреймворков. Надо понимать, разницу между, условно говоря, «админским» программированием, когда нужно автоматизировать решение задачи, которую, в принципе, можно решать и вручную, только в 1000 раз дольше, и временнЫе рамки не жёсткие, и «трудовым» программированием в команде, когда стоит задача по максимуму полезно использовать оплачиваемое время программистов. Что такое риски и надёжность и какова их роль, что такое тестирование и насколько важно сохранение работоспособности программы при её изменениях и почему эти изменения неизбежны. Ну и про IDE со встроенным рефакторингом, что делает изменение программ более простым делом. Тогда сразу возникает понимание, что использование рефлексии сильно повышает вероятность неотлавливаемых компилятором ошибок, особенно возникновение их при переименовании членов класса. И что прокси, рефлексию и классозагрузчики нужно тестировать особенно тщательно, поэтому их используют в малом количестве, сосредотачивают в одном месте, и желательно, чтоб за работоспособность этого места нёс ответственность не ты, что разработка фреймворков требует фактически другой организации бизнес-процесса.
Зарегистрируйтесь на Хабре , чтобы оставить комментарий

Публикации

Истории