All streams
Search
Write a publication
Pull to refresh
31
0
Антон Куранов @Throwable

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

Send message

Раз уж сказ пошел о крепостях, не могу не отметить Ростовский Кремль. Даже с Google Maps отчетливо видно невооруженным взглядом звезчатую крепость с еще несрытыми бастионами: https://www.google.es/maps/@57.1858048,39.4141099,945m/data=!3m1!1e3


Курьез заключается в том, что вообще нигде ни слова не упомянуто, что это именно звезчатая крепость с бастионами, и даже в местных музеях очень сильно удивляются картинке. По официальной версии это архиерейский двор, который строился не с целью обороны, и поэтому укреплен достаточно лайтово. https://ru.wikipedia.org/wiki/%D0%A0%D0%BE%D1%81%D1%82%D0%BE%D0%B2%D1%81%D0%BA%D0%B8%D0%B9_%D0%BA%D1%80%D0%B5%D0%BC%D0%BB%D1%8C


Отсюда три правомерных вопроса:


  • Кто, когда и с какой целью возвел эти бастионы?
  • Почему они не умомянуты в официальных источниках?
  • От кого так серьезно собрались обороняться в конце XVII века в центре России? Если учесть, что соседний Ярославль вообще не имел никаких стен и норм.

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


Кроме того, говоря о "беспристрастности" в научном методе, во многих областях следует ставить во внимание морально-этические нормы и тенденции, а также политическую и экономическую коньюнктуру.


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

И все вышесказанное так или иначе также формирует "официальную" точку зрения.

Именно так. Удивлен, что это для вас является открытием. Действительно при университетах и любых научно-исследовательских организациях есть специальная пресс-служба, которая занимается публикацией материала о проделанной работе на языке, доступном для массового читателя. В США это вроде как даже по закону обязуют делать. И эта точка зрения моментально распространяется всеми СМИ (правда зачастую с искажениями). А благодаря авторитету огранизации с одной стороны, а также отсутствию у читателя экспертной базы с другой — эта точка зрения массово принимается на веру и быстро становится "официальной".

По поводу пункта 3: а почему всегда общепризнанная версия подразумевается наиболее вероятной? И вообще как объективно оценить вероятность правильности той или иной теории? Если по количеству адептов, то самая вероятная теория возникновения мира — это божественное творение.


Сегодня каждый может, не отрывая задницу от кресла, получать доступ к фактическому материалу, обрабатывать его в больших объемах и находить явные несоответствия или противоречия признанной версии. Полное игнорирование, неудовлетворительность объяснения с позиции официальной точки зрения, или что еще хуже — взаимные выпады на оппонента, — закрадывают определенные сомнения в состоятельности официальной версии и вынуждают автора искать спекулятивные альтернативы. Поскольку автор не обладает всеми ресурсами и инструментами, которыми обладает официальная наука, альтернативная версия получается достаточно сомнительной, хотя и очень занимательной.


И в основном это касается ретроспективных наук: история, геология, теория эволюции, etc, ибо все они имеют проблемы с фальсифицируемостью. История вообще практически по всем пунктам подходит под понятие псевдонауки:
https://ru.wikipedia.org/wiki/%D0%9F%D1%81%D0%B5%D0%B2%D0%B4%D0%BE%D0%BD%D0%B0%D1%83%D0%BA%D0%B0#%D0%9E%D1%82%D0%BB%D0%B8%D1%87%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D1%8B%D0%B5_%D0%BE%D1%81%D0%BE%D0%B1%D0%B5%D0%BD%D0%BD%D0%BE%D1%81%D1%82%D0%B8

Я имел ввиду, что команду нужно оценивать не только по моральным и социальным, но и по профессиональным качествам. А лучший способ судить об этом — прощупать и прикинуть все то дерьмо, которое они успели намолотить.

Это вам еще повезло. Хуже, когда выясняется, что весь продукт до сих пор пакуется и деплоится на Tomcat или какой-нибудь коммерческий сервак типа Weblogic. Далее выясняется, что веб сделан на JSF в лучших традициях замеса темплейтов с динамическим JS-кодом. И не предусмотрена возможность все это пустить и дебажить на локальной тачке: единственная форма тестирования — навтыкать везде принтов, запаковать, задеплоить на сервак, прокликать функционал и затем скурпулезно втыкать в логи. Путем подобных наводящих вопросов становится все более заметной растущая из проекта борода. Зато все вежливые, корректные в общении, позитивные, думают о команде...

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

Мой любимый развод спрингера, JEE-шника и любого фреймворкера на собеседовании: можно ли использовать в приложении следующий код:


@Singleton
public class MyService {
    @PersistenceContext
    EntityManager entityManager;

    @Transactional
    public void doSomething() {
        // используем EntityManager
    }
}

После недолгого обдумывания поциент соглашается — "а почему нет"? На что я обращаю внимание, что бин у нас Singleton, и задаю следующие наводящие вопросы:


  • Сколько раз и когда инъектится EntityManager в поле singleton-бина? Правильный ответ: 1, при создании самого бина.
  • В JPA EntityManager — это thread-safe объект? Правильный ответ: НЕТ.
  • Может ли здесь возникнуть конфликт, когда две параллельные транзакции обращаются к одному не thread-safe объекту EntityManager? Правильный ответ: НЕТ, т.к. в данном случае в поле entityManager инъектируется обертка над реальным объектом EntityManager, который в свою очередь привязан к текущей транзакции. Управление осуществляется контейнером/фреймворком.

Большинство валится на третьем вопросе, отвечая, что код некорректен. Ну и как бы общий вопрос на понимание DI:


  • Когда бин с областью "prototype" инъектируется в "singleton", какую область видимости будет он иметь? Правильный ответ: SINGLETON за исключением, если въявную не включен proxying (ScopedProxyMode, etc).

Наивные хомячки никогда не видят тонны геморроя, которые тенью стоят за очередной хайпуемой модной фичей. Это бинарный формат. Вы задолбаетесь писать интеграционные тесты к своему сраному сервису, скурпулезно заполняя все объекты данными вместо простого чтения из файла. Забудьте про текстовый редактор и postman, а вооружитесь хорошим hex-editor-ом, когда при InvalidProtocolBufferException будете скурпулезно просматривать байты, ища несоответствие. Новое поколение уже не помнит CORBA и готово вновь наступать на те же грабли.


Ну и как java-бэкендеру мне лень городить огород на левом языке. Вот если бы была библиотека аннотаций, которая бы генерировала файлы .protobuf из моей модели на java, тогда еще можно было бы поиграться...

Производители современных картриджей типа LTO обычно гарантируют сохранность информации от 15 до 30 лет.

Если постоянно считывать, то слой быстро размагничивается (хотя возможно головка как-то подмагничивает пленку), плюс на пленке остаются механические повреждения. Кроме того, если постоянно перезаписывать ленту, то срок службы станет еще меньше.

А сами-то они будут потоки создавать

И пусть создают, если нужно. Задача не избавиться на 100% от всех потоков, а увеличить масштабируемость приложения. Для этого нужно заменить потоки лишь в основном пуле воркеров, который растет линейно от количества параллельных реквестов. При этом ничего плохого нет, если например jdbc datasource запустит свой отдельный нативный тред, который время от времени собирает незакрытые коннекшны и операторы — он будет один на всё приложение и никак не зависит от количества реквестов. Если же ваша библиотека на каждый вызов открывает дополнительно еще один тред — смело можете от нее избавляться уже сейчас.

Это как раз не проблема. Thread вы заменяете на файбер (оригинальное более удачное название виртуального треда) в своем коде, который уже дергает всякие IO-библиотечки типа JDBC и пр., а те в свою очередь не догадываются в каком треде работают. Если же у вас какой-нибудь фреймворк со встроенным сервером, или сторонний Executor, то практически везде можно указать свой ThreadFactory, который будет создавать файберы вместо тредов.


Да, и драйвер JDBC — это еще не самый плохой случай

… но не в случае, например, с Oracle JDBC...

На самом деле это не очень критично. По той простой причине, что всё, что сегодня использует мониторы Java, можно механически преобразовать из использования synchronized и wait-notify в использование блокировок из java.util.concurrent.

Вообще-то очень критично. Если изначальная идея была прозрачно для всего кода заменить Thread на VirtualThread, то здесь она заведомо обломается. Большинство IO-кода, используемого сегодня, относится к сторонним библиотекам и фреймворкам, которые модифицировать будет проблематично. И никто не давал гарантий, что очередная библиотека типа jdbc драйвера будет использовать java.util.concurrent вместо synchronized. Насколько я понял из доклада Сергея Куксенко, синхронизации в Loom сильно мешает biased locking, который в скором будет выпилен из java. Но до тех пор Loom остается практически не юзабелен. Возможно вместе с Loom стоило бы ввести автоматические преобразование synchronized в java.util.concurrent в рантайме, пусть даже с регрессиями.


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

Mockito и подобные фреймворки используют динамическую генерацию классов, а не проксирование. Этот org.own.ArticleExample1$Apple$MockitoMock$692964397 — сгенерированный наследник класса ArticleExample1.Apple. Поэтому если вы его объявите как final, то все упадет.
Proxy может делать динамические классы только для интерфейсов, что сильно ограничивает его использование.


Apple.class.getInterfaces(), (proxy, method, args1) -> {
    System.out.println("Called getColor() method on Apple");
    return method.invoke(new Apple(), args1);
});

В этом хэндлере есть нетривиальная ошибка, связанная с исключениями, про которую всегда все забывают. Вызов method.invoke() в случае исключения обернет его в InvocationTargetException, на который может не сработать хендлер в вызывающем коде. Правильно будет так:


Apple.class.getInterfaces(), (proxy, method, args1) -> {
    System.out.println("Called getColor() method on Apple");
    try {
        return method.invoke(new Apple(), args1);
    } catch (InvocationTargetException e) {
        throw e.getTargetException();
    }
});
Одна проблема — не все клиенты умеют во все кодировки. Соответственно, гарантии переносимости нет.

UTF-8 и ISO умеют все. Если вам необходимо использовать экзотическую кодировку типа CP866 или KOI-8, то это конкретно проблема соглашений между вами и вашими клиентами.


Который создаёт туеву хучу проблем с безопасностью. Умудриться сделать RCE в языке разметки — это, конечно, вин.

Как бы это не язык разметки, а кривой дырявый парсер LSP4XML. С таким же успехом можно назвать дырявым и html и javascript и css и вообще все, что ссылается на внешние ресурсы. Причем в самой Java парсер устроен правильно — необходимо въявную указать свой ResourceResolver который будет подтаскивать внешние импорты.


Начерта нужны атрибуты отдельно от дочерних нод — Тим Бернерс-Ли его знает.

Чтобы понять, попробуйте скотвертировать типичную html страницу во что-то JSON-подобное и посмотрите что получится. Есть понятия данных и метаданных. В атрибутах хорошо хранить метаданные, например тип поля, а в тэгах — само значение.


нужно блин знать все неймспейсы, которые в этом XML будут

Никакого нарушения инкапсулации: неймспейс — такая же часть имени тэга, поэтому его нужно знать при парсинге. Геморроя больше с неймспейсами, согласен, однако он оправдан, когда у вас гетерогенная платформа с кучей систем и различных гетерогенных интерфейсов.


JSON — типизирован сам по себе. В нём по формату есть числа, строки, true, false и null.
А уж какие шикарные приёмы изобретают для отдачи null в xml — это ж просто сказка. Один xsl:nil чего стоит. null-евое значение с атрибутами — как вам концепт?

Как и товарищ выше по треду, вы не осознаете разницу между разметкой и семантикой. Просто в JS было зашито только 4 типа, и эти типы смогли перекочевать в json, который изначально и был куском JS кода. Но вот у вас появилось поле Date, и парсер уже бессилен сконвертить его, в результате чего приходится добавочно где-то писать логику, что данное поле следует интерпретировать как Date, а не как String, или писать для него конвертер — т.е. задавать семантику отдельно от самой разметки. А раз так, чем таким выделились boolean, number и null, чтобы для них делать отдельные правила разметки? Ведь в других языках даже для простого числа сущесвует 10-15 различных типов.


То же самое с нулом. Это не более чем "стандартное" соглашение в языке разметки. Как вы его семантически будете интерпретировать null — ваше личное дело. Кроме того, есть языки где значение NULL также может быть типизировано. Для этого могут потребоваться дополнительные атрибуты на тэге.


В 9 из 10 языков программирования я могу написать json_value["field_name"][0]["nested_name"] и не заморачиваться никакими *Path-ами

Не можете, ибо JSON — это не более чем строка с разметкой, а здесь у вас объект, созданный парсером на вашем любимом ЯП. Только не забывем делать проверку на null и index out of bound после каждого шага. XQuery, XPATH, XSLT не привязаны ни к какому языку и сами собой представляют определенный язык.


В сухом остатке: XML — невероятно переусложенный формат. Его проблема в том, что он не ложится нативно на структуры языка

Он не может ложиться нативно поскольку задумывался как независимый ни от одного из существующих языков, в отличие от Json, который изначально был кусоком JS кода. Есть специальные парсеры, которые мэпят XML в типы и структуры на каком-то языке (JAXB, Jackson, XMLBeans). А вот нативные парсеры для большинства платформ — это да, неудобоваримое дерьмо. Знаю по Java — наверное самый уродский и неудобный XML API, но при этом самый методичный и полный. Многих и отпугивало то, что в XML делать простые вещи стандартными средствами было достаточно сложно и контринтуитивно. Но практически везде есть куча сторонних библиотек для удобной работы.

Поясню на примере

Все-равно не доходит как Optional спасет от неправильного меппинга. Все поля @Nullable, контракт соблюден, а проверка меппинга — это уже дело теста. Гораздо хуже, когда у вас поле Optional, и вы его забыли проинициализировать:


public class B {
  public Optional<String> fieldA;
}
...
B b = ...;
System.out.println(b.fieldA.orElse("").length());  // NPE-нежданчик

И в Java от этого никак не уберечься. Поэтому все категорически не рекомендуют использовать optional-поля.

нельзя отличить неинициализированное по ошибке поле от необязательного без значения

Не понял. Вы собираетесь использовать поле, которое возвращает Optional и null одновременно? Это одна из худших идей. Формально ни одно значение типа Optional никогда не должно быть null, хотя фактически Java запросто допускает такое — забыли проинициализировать и все (в отличие например от Option в Scala).


аннотации для локальных переменных выглядят сильно хуже, чем Optional

Они там и не нужны — IDE автоматически выводит nullability из контрактов над методами или самого кода.


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

Стандартного нет, но любой нестандартный справится на ура. Findbugs, Spotbugs, PMD, Sonarqube. etc… Все работают с nullable аннотациями. Единственная проблема — сами nullable аннотации никем не стандартизированы, и сейчас есть разные библиотеки.

По поводу первого примера: начиная с Java 9 есть удобный метод ifPresentOrElse.

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


Person person = findPerson();
if (person == null) {
   person = new Persion();
   createPerson(person);
} else {
   person.setXXX();
   updatePerson(person);
}
doSomethingWith(person);

Если же возвращается просто какой-то тип, то не всегда понятно, может ли быть здесь null, или нет.

В том-то и дело, что в общем случае у нас есть 3 типа контрактов:


  • Nullable
  • Гарантированно не null
  • Х.з. (на усмотрение юзера)

Вы можете лично у себя в коде использовать какое-то соглашение, избавившись от последнего варианта (напр. везде вместо null возвращаем Optional), но при интеграции со сторонними API и с той же java rtl возникнут проблемы — придется для всего писать обертки Optional.ofNullable(), либо ставить проверки Objects.requireNonNull(). И в этом случае вы также можете забыть сделать соответствующее преобразование.


Кроме того, очень часто nullability бывает контекстно-зависимым, и это нельзя выразить никаким статичным контрактом. Пример из JPA:


@Entity
public class MyJPAEntity {
    @Id @GeneratedValue
    private Long id;
}

Поле id может быть null только при создании объекта, пока он еще не persistent. В 99.9% случаев объект будет вытащен из базы, и поле всегда будет иметь значение. Возвращать для этого поля Optional, отдавая себе отчет, что оно тут ну точно не может быть null, но каждый раз делать лишние приседания по преобразованию, будет сильно отдавать шизофренией и загрязнять код.
И вот к чему приводит подобная практика:


public static Optional<String> toString(Object obj) {
    return Optional.ofNullable(obj).map(Object::toString);
}
...
System.out.println(toString(1).orElseThrow(() -> new RuntimeException("WTF!")))
System.out.println(toString("Privet").orElseThrow(() -> new RuntimeException("WTF!")))
System.out.println(toString(new Persion()).orElseThrow(() -> new RuntimeException("WTF!")))

Это вообще касается всех контрактов (checked exceptions, class cast, etc...) — большинство из них невычислимо в статике. Для этого нужно не бояться ставить проверки и писать тесты вместо параноидального желания все прибить гвоздями.


Если интересно, посмотрите давнее видео Андрея Бреслава, как они в Котлине тщетно пытались жестко прикрутить nullable-контракты и какие проблемы возникли с интеграцией с кодом на джаве. В итоге пришлось внутренне на уровне компилятора ввести третий тип с неопределенной nullability (String!!), который приводится к одному из первых двух при первой соответствующей декларации.

Проблема Optional в том, что он пришел из функциональных языков, где сплошь и рядом используется композиция, но он плохо ложится на императивное программирование. Особенно в случае, где необходимо не просто заменить empty дефолтным значением или трансформировать результат в одно действие, а осуществить полноценное ветвление:


Optional<Person> personOpt = findPerson();
if (personOpt.isPresent()) {
    Person person = personOpt.get();
    person.setXXX();
    updatePerson(person);
} else {
    createPerson();
}

Тут у нас появляется одна дополнительная переменная (personOpt) и одно дополнительное действие (personOpt.get()).
Поэтому программирая на Java, практически всегда лучше использовать слабые контракты типа @Nullable/@Nonnull:


@Nullable Person findPerson() {...}
...
Person person = findPerson();
if (person == null) {
   person.setXXX();
   updatePerson(person);
} else {
   createPerson();
}

Так проще и понятнее, а IDE здесь предупредит о возможном нуле, и этого как правило будет достаточно.


Другое преимущество nullable-аннотаций в том, что они не требуют сквозного перелопачивания всего API, как в случае с Optional. Умный компилятор (Kotlin) или IDE (Idea) умеют выводить nullability-контракт транзитивно из исходников, при этом не требуя какого-либо изменения в коде. Кроме того, если использовать только парадигму Optional, то должен быть некий и аналог @Nonnull: что-то типа Required<T>. Однако такой практики нигде не существует. В итоге nullable-аннотациями можно указывать более сильные и гибкие контракты, нежели при помощи Optional, и делать это в более простой форме.


Ну и вообще, как вы подметили в статье, исторически Java экосистема не приспособлена для Optional. Его использование оправдано только как отсутствие результата вычисления функции. Использовать его как контейнер для данных крайне нелепо.


Например, JPA Specification возвращает Optional вместо null.

Я что-то пропустил??? Или вы не различаете Spring Data и JPA?


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

Интересно, сколько времени потребуется еще, чтобы пользователи осознали то же самое про String и перестали делать различие между пустой строкой и нулом.

Если происходит IndexOutOfBoundException, DivisionByZeroException, то их очевидно здесь ловить не надо. Это ошибка программирования, если они происходят, а значит продолжать в принципе небезопасно

Мой пример именно из серии, если бы язык требовал строгое соблюдение контракта, а все исключения были бы проверяемыми. Контракт с List.get() и a/b может кидать исключения, соответственно, следуя вашей идее, вы обязаны его перехватить, даже если по логике он никогда не возникает (List-то об этом не знает!). И в этом случае кинуть какой-нибудь WTFException, который тоже должен являться частью контракта. Ну либо определить IndexOutOfBoundException как возможную ситуацию в контракте метода, выставляя наружу детали реализации. Более того, любой доступ к полям объекта может выдать NPE, поэтому и их тоже по идее нужно заворачивать в exception или пользоваться Optional вместо значения. И это мы еще не коснулись вычислимости типов, где у Java не такие большие возможности, и при каждом кастинге ставить catch ClassCastException. В итоге это все закончится паранойей и шизофренией, которая совершенно не нужна для реальных бизнес задач. Ваш контракт будет нагружен кучей ненужного дерьма, которое не имеет отношения к решаемой задаче и никогда никем не будет испльзоваться.


Кстати, по поводу сервиса и его переезда на RPC: сделайте корректную обработку ошибок, если коннект валится — то всё, это как нуллпойнтер в локальной версии.

Так и сделано. Кидается специальное RuntimeException, которое перехватывается каким-то там верхним обработчиком. А бизнес коду абсолютно наплевать — это именно исключительная ситуация, которая не должна происходить в работающей системе. Бизнес код не знает что делать в этом случае и поэтому не должен ставить никаких обработчиков на этот счет. Поэтому и нагружать контракт различными деталями реализации не есть хорошо.

Information

Rating
Does not participate
Location
Madrid, Испания
Registered
Activity