Обновить
31
Антон Куранов@Throwable

Пользователь

13
Подписчики
Отправить сообщение

Передача по ссылке или значению — это архаизмы реализаций компиляторов и языков прошлых лет, когда это имело смысл. На сегодняшний день важно лишь, что описываемая сущность является мутабельной или иммутабельной. В случае иммутабельной сущности нам не важно передается ли она по значению или по ссылке и где физически она находится в памяти — это уже внутренние проблемы компилятора и рантайма. При операциях с иммутабельными сущностями каждый раз создается новый экземпляр. Таким образом иммутабельные классы полностью идентичны value type (подразумевается наличие equals()/hashCode()).


В случае же мутабельной сущности, мы имеем каждый раз дело с экземпляром, занимающим определенное место в памяти, и поэтому осознанно работаем с ним по ссылке (некоему хэндлу, если угодно). Для передачи информации о внутреннем состоянии мутабельного объекта вовне есть два способа: хороший — обернуть его иммутабельным врапером, либо плохой: просто сделать defensive copy и отдать на растерзание. Структуры в Свифте — это просто реализация defencive copying по умолчанию, тогда как в Котлине это нужно делать въявную. Мне кажется не стоило делать отдельный класс объектов языка только лишь для реализации одного паттерна. Тем более, что изначально структуры (записи) в языках программирования означали немножко другое и появились задолго до классов.

Когда ответственность за функционирование и сопровождение системы ложится полностью на девелоперов, отмазаться будет сложно: ваша система периодически падает — чините как хотите, тестируйте, мониторьте — ваши проблемы.
С другого конца, когда требования устанавливает некий дядя, называющий себя product owner-ом, девелопер никогда не видит реальных результатов своей работы, не получает должного feedback (кроме бесконечных ошибок и изменений), то его не особо беспокоит качество кода, нефункциональные требования и пр. — пусть об этом голова болит у дяди, ему за это платят. Цель девелопера в этом случае становится не сделать продукт, а закрыть issue.
Так что чем больше посредников в процессе разработки, тем больше разделение ответственности, что всегда негативно сказывается на качестве финального продукта.

Прошла первая очередь, тесты отработали, и все выкатили на продакшн — и все свалилось в первый же день. Причина — какой-нибудь resource или memory leaking в пулах или кешах. Девелоперы винят тестеров, что те не обнаружили. Тестеры отмазываются, что это нефункциональные требования и ничего не хотят об этом знать. Вот если бы девелоперы сами готовили тесты, то метрики пулов скорей всего были бы включены в тесткейс.

Все зависит от глубины изменений. Если речь идет только о новом методе или классе, то самый безболезненный способ — это сделать обычный интерфейс-враппер, а имплементацию подключать динамически через factory или singleton. Уверен, что когда количество изменений вырастет, появятся всякие backport-библиотеки вроде retrolambda. Теперь представьте, что поменялась версия байткода. Тогда по-любому придется делать два билда с разными --target и собирать два разных джарника. Либо если произошла радикальная смена API как в случае с Java8, то вообще придется говорить о двух разных версиях библиотеки, собранной из разных исходников. Multirelease jars же все усложняют: лейаут проекта, компиляцию, сборку и тестирование. Вобщем, поживем-увидим. Пока что пипл не слишком с энтузиазмом относится к фиче.

Проблема DevOps в разработчиках. Они не способны качественно проанализировать требования и протестировать результат, т.к. они смотрят на продукт через призму кода, а не с точки зрения пользователей.

Нифигасе заявление! Да практически все идеи генерятся девелоперами! Просто в большинстве компаний сложилась такая номенклатурная организация, что между пользователем и девелопером стоят люди, называющие себя аналитиками, которые практически никогда в жизни не писали код. Они слабо представляют сам процесс разработки, архитектуры и огранизации системы, возможности кода и трудозатраты, и тем не менее сидят на своем месте и получают зарплаты. Это такие как Tom Smykovski из легендарного Office Space, который заявлял: "I have people skills":
image


Нам нужно сохранить роли аналитиков, тестировщиков и внедрения в команде. Разделение труда ведёт к повышению качества и производительности (вспомните Стаханова и его идею разделения труда в шахте)

Вы упускаете ОЧЕНЬ важный момент: разделение труда работает только в тех системах, где взаимодействие между членами минимально, и упрощено насколько это возможно, а сам труд реально разделяем — тогда это работает. Инженерная же идея, тем более достаточно сложная, очень плохо скалируется по головам. Если команда девелоперов придумала и закодила функционал, нужно будет объяснять все детали реализации команде тестировщиков, чтобы те, из того, что они поняли, смогли написать кое-какие тесты. А кто лучше сможет написать тесты и покрыть больше кейсов, чем сами девелоперы?


Из негативных эффектов разделения труда — это круговая порука и сбагривание ответственности. В девопсе же девелопер несет ответственность до конца.


P.S. Говоря о девопсе, вы полностью упустили сам "опс", например команду админов, которая файерволом стоит между девелоперами и продакшном и фактически не дает девелоперам видеть, что происходит с их кодом на сервере.

Ещё одна доработка большой фичи, появившейся в Java 9: Multi-Release JARs. Напомню, это возможность в одном JAR-файле иметь несколько версий одного и того же класса или ресурса, соответствующих разной версии Java.

Мне одному кажется, что это заведомо рудиментарная вещь? Она идет вразрез с имеющимися билд тулзами, процессом сборки и выкатывания, и вообще со здравым смыслом. Версионирование должно быть на уровне всей библиотеки, как это делается сейчас, а не отдельных классов. А уже рантайм должен (был) уметь цеплять версию библиотеки, соответствующей релизу, если такая присутствует.


Общее впечатление: я так понимаю, эволюция Java Api остановилась, и будущие изменения будут затрагивать по большей части JVM?

Согласно моему опыту основной ошибкой клиента всегда было неправильное внедрение BPM. Последнее предполагает не только смену методики работы компании, но и определенную реструктуризацию, на всех уровнях. А к этому бывают далеко не все готовы. Кроме того есть ряд тормозящих политических факторов, например, топ-менеджеры и управляющие кадры, которые не очень счастливы, чтобы с их работы снимали различные метрики эффективности либо как-то реструктурировали. Поэтому на "попробовать" всегда берут какой-нибудь департамент из Operations и насильно втюхивают туда BPMS, где его вообще в гробу видели, и где большинство процессов уже автоматизировано на 100%. В итоге параллельно с уже работающим стеком, вторая бригада пыхтит над тем, чтобы перевести все процессы на BPMS, вызывая лютый баттхерт у первой по причине постоянного ощущения инородного тела в заднице. По прошествии n-дцать месяцев то, что добивается данная бригада, это сделать тупую прокси-надстройку с парой линейных "процессов" над уже работающими сервисами, гордо объявив это все "Orchestration Layer". После этого отчитываются наверх об успешном запуске, получают медаль и на этом все завершается.

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

Изначальную инструкцию хорошо использовать для составления тестов к функционалу, чтобы удостовериться, что код работает именно так, как написано. А уж имплементить ее именно таким образом никто не заставляет. Адекватный программист (не занимающийся BPM) вместо "процесса рассчета стоимости номера в пансионате" заимплементил бы простую и наглядную функцию с тремя условиями на семь строчек кода. А адекватный аналист пришел бы к клиенту и сказал: "а давайте вместо этой фигни попробуем сделаем простую наглядную таблицу коэффициентов для стоимости номера, которую к тому же можно будет менять в любой момент". И только BPM аналист, у которого в голове одни схемы, станет изобретать некий "процесс" для вычисления простой функции, вместо того, чтобы записать простую формулу.


Итак, что же на самом деле может получить компания, если пропишет сверху донизу все свои бизнес-процессы, что должно явиться целью подобной весьма громоздкой работы?

Это понятно, упорядочение, автоматизация и все такое. И BPMS здесь как бы даже сбоку. Скажите лучше о граблях:


  • уменьшение общей гибкости взаимодействия — все жестко поставлено на рельсы, по полю уже не проедешь
  • увеличение рисков нештатных ситуаций (что-то пошло не по процессу) и отсутствие соответствующих инструкций для реагирования
  • формализация управления и разделение труда ведет к тому, что меньше людей имеет общее представление о решаемой задаче, что ведет к общей бюрократизации
  • несмотря на предоставляемый автоматический мониторинг и обилие метрик, выявление неэффективностей в процессах будет затруднено и более того, реально никто этим заниматься не собирается. Метрики используются исключительно для составления бесполезных отчетов, а не для анализа.
    И когда ситуация ухудшается полностью, вызывают бригаду "оптимизаторов бизнес процессов", которые обнаруживают, что несмотря на всю автоматизацию и BPM, треть персонала фактически ничем не занята, а все бизнеспроцессы надо (опять) менять. Иногда это помогает.

Помимо "просто" меня жутко бесит слово "ВСЕ". Употребляется в контексте, когда нужно добавить новый функционал, но заказчик/менеджер не имеет представления о всей конкретике работы системы, либо тупо не хочет заморачиваться.
— В какую форму добавить коментарий? — Во ВСЕ!
— В каком сценарии вызывать этот интерфейс? — Во ВСЕХ!
— Для пользователей каких ролей сделать проверку? — Для ВСЕХ!
— Какие интерфейсы экспортировать для удаленного использования? — ВСЕ!

И если будут обнаружены кражи, одно из первых двух пунктов хоть частично не будет выполнено или конечный результат не будет достигнут


Простите, что значит «если»? Вообще есть куча площадок на этот счет. Пусть идут собирать на кикстартер…

Идея как раз в том, чтобы исключить искусственные методы типа isActive() и isPresent() и соответствующие проверки (они ни чем не лучше проверки на null). Возвращаемый "пустой" объект имеет полностью осмысленное поведение при вызове своих методов, и не требует специальных кейсов в логике программы. Например с пустой сторокой можно делать все те же операции, или например пустой список можно также итерировать в цикле без специальной проверки.


я не понял зачем нужен EMPTY если не имплементируется equals()

Чтобы инициализировать дефолтными значениями поля этого типа в других классах.


public class AnotherClass {
    public MyClass myClass = MyClass.EMPTY;
}

Это в случае если класс MyClass immutable. Тогда проверку на empty можно делать простым сравнением "==", но при желании можно добавить и equals()/hashCode(). Хотя как я уже сказал выше, поведение кода не должно отличаться в случае пустого или непустого объекта и не должно содержать искусственных проверок.


Ну а список можно сделать immutable с помощью Collections.unmodifiableList()

Это заведомо плохой способ, т.к. из сигнатуры метода семантически не ясно, является ли он modifiable или нет. Кроме того в случае интерфейса или абстрактного класса, наследуемые классы могут иметь совершенно разное поведение. Поэтому один и тот же код


userService.getUsers().add(new User());

в зависимости от имплементации UserService в одних случаях будет прокатывать, а в других нет.

Не использовать вообще нулевые значения.
У большинства стандартных типов существует некое дефолтное значение, которым можно заменить null. Например для числовых типов это ноль, для String это пустая строка "", для списка — пустой список, etc.
Поля класса сразу инициализируйте дефолтными значениями.


Для собственных типов можно использовать можно искусственно определить нулевое значение и при необходимости сравнивать с ним.


public class MyClass {
  public static final MyClass EMPTY = new MyClass();

  public final String name;
  // Optional field, "" by default
  public final String address;

  protected MyClass() {this("","");}
  public MyClass(@Nonnull String name) {
    this(name,"");
  }
  public MyClass(@Nonnull String name, @Nonnull String address) {
    this.name= name; this.address = address;
  }
}

Возвращается лист, возможно пустой
Проблема в том, что лист mutable. Лучше возвращать Iterator или Stream.

Извиняюсь, что поздно ответил.
Смысл в том, при пожирании одновременно определенным образом изменяются состояния и собаки и сосиски. Однако первый вариант нарушает инкапсуляцию сосиски: собака должна знать как изменить свое собственное состояние (ок), но также и состояние сосиски. Во втором варианте соответственно нарушается инкапсуляция собаки.
А третий вариант — это тупо процедурное программирование, закамуфлированное под объект, со всеми вытекающими (нарушение инкапсуляции, отсутствие полиморфизма).


С упомянутым Вами удалением сосиски все еще хуже: после пожирания на сосиску остается указатель, поэтому впринципе одну и ту же сосиску можно скормить разным собакам, если не ввести искусственный контроль :)


В более сложной модели собака и сосиска должны обменяться сообщениями типа собака-сосиске: "я тебя ем с такими параметрами", сосиска-собаке: "вот мои питательные вещества". Это будет более корректно. Иными словами собака и сосиска реализуют двунаправленный интерфейс "сожрать"-"быть сожранной" с двумя каналами. Но это уже больше похоже на модель акторов, нежели ООП.

Не существует никаких классов и сущностей, есть только интерфейсы. Абстрактный водитель может управлять всем чем угодно, что реализует стандартный интерфейс "автомобиль" и имеет руль, педали и прочие приблуды. Более того, для управления нам не нужна никакая сущность типа "абстрактный или конкретный автомобиль": водитель может ездить на тренажере по виртуальной локации еще вместе с десятком таких же водителей. Здесь "сущность", которой управляем, уже выделить сложно. Точно также с другой стороны у нас нет никакой необходимости в сущности "абстрактный водитель" — стандартным такси может управлять механизм, подключенный к единой сети, которая управляет его действиями.


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


  • собака.сожрать(сосиска)
  • сосиска.бытьСожранной(собака)
  • new ПожирательноеДействие(собака, сосиска).сделать()

Прикрутили сбоку модную фичу для галочки.
Никого не напрягает, что на coffie-cup.jpg уже ссылаются из .jsp и что чуть более интеллектуально развитый темплейтер мог бы сам запушать автоматически все необходимые ресурсы? Ну или на крайняк придумать специальную директиву для .jsp, чтобы там же рядышком прописывать все, что надо запушить. А модифицированные сервлеты уж никак не есть естественное место для справления своих потребностей в пуше.

Во-первых, в разработке ПО есть такое понятие как гибкость.

Да, в отличии от инженерного дела разработка ПО не ограничена жестко законами физики, поэтому позволяет девелоперам творить любую фигню — машина все проглотит. Тем не менее, базовые принципы построения проекта никто не отменял.


Он просто позволяет делать качественный продукт в условиях неопределенности.

А как вы себе представляете неопределенность? Продукт призван решать определенные задачи, для этого создаются определенные решения. Единственная область, где я вижу более-менее оправданное использование agile — это типичный веб-проект: страничка, сервер и база данных. Клиент пока не знает что конкретно ему нужно, но вы пока начните, сделайте форму логина, потом утвердим дизайн, определимся с функционалом, добавим пунктиков в меню, табличек в БД, etc… Архитектуры — ноль, зависимости слабые, объем работы скалируется и хорошо делится. Типичная дача.

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

Совершенно верно. Agile — это когда вы строите для клиента дачу. Можно начать с возведения сортира, затем пилить баню, забор, сарай, потом приступить к дому, затем пристройку, потом домик для гостей — все по необходимости, и главное, чтобы народ был занят. А вот многоэтажку таким методом построить уже проблематично.

А дело в том, что Java изначально семантически очень плохо приспособлена для декларативных конструкций и DSL-ей. Фреймворки, построенные на аннотациях — это некий компромисс между декларацией и исполнением, причем очень фиговый по сравнению с тем же DSL. В бекенде еще как-то прокатывает, но во фронтенде, где приходится создавать иерархические конструкции (напр. DOM), в императивном стиле на Java это превращает код в линейную кашу. Поэтому на Java никогда не было и не будет нормальных html-темплейтеров, тем более type-safe как на скале или котлине. Хотя попытки есть: https://j2html.com/

GWT абсолютно ничего не имеет общего с Java-рантаймом. Это распространенное ошибочное мнение. У него есть определенная Runtime Library Emulation, чтобы можно было работать с коллекциями, строками, стримами, лямдами, etc как в Java. Но в рантайме GWT не таскает с собой вообще никакие библиотеки. Вместо этого он анализирует весь код приложения начиная от EntryPoint, включая только те функции из всей кодовой базы, которые используются въявную (а для 100% явности была выпилена reflection). Остальной шлак жестко шринкается. Дополнительными бонусом идет минимизация и обфускация. В итоге для функционального приложения код получается меньше, чем на нативном JS, т.к. последний грузит все используемые JS-библиотеки полностью со всем функционалом. Кстати, GWT поддерживает SourceMaps при запуске в DevMode, поэтому можно дебажить вживую в Chrome или Firefox, а в SourceMaps показывается только Java-код, который GWT оставил после шринкинга, т.е. то, что реально будет в рантайме.


Насколько GWT-код может быть минимальным полностью зависит от того, что вы будете использовать. Откажитесь от стандартных GWT-виджетов и UiBinding, используйте библиотеку Elemental, и Overlay Types. Для манипуляции с DOM-ом для GWT есть аналог JQuery — GwtQuery.

Информация

В рейтинге
Не участвует
Откуда
Madrid, Испания
Зарегистрирован
Активность