Получи случайную криптовалюту за регистрацию!

Java: fill the gaps

Логотип телеграм канала @java_fillthegaps — Java: fill the gaps J
Логотип телеграм канала @java_fillthegaps — Java: fill the gaps
Адрес канала: @java_fillthegaps
Категории: Технологии
Язык: Русский
Количество подписчиков: 10.33K
Описание канала:

Привет! Меня зовут Диана, и я занимаюсь java разработкой с 2013г.
Делюсь опытом/знаниями по темам:
- Java Core
- Вопросы с собеседований
- Best practices
Комплименты, вопросы, предложения: @utki_letyat

Рейтинги и Отзывы

4.50

2 отзыва

Оценить канал java_fillthegaps и оставить отзыв — могут только зарегестрированные пользователи. Все отзывы проходят модерацию.

5 звезд

1

4 звезд

1

3 звезд

0

2 звезд

0

1 звезд

0


Последние сообщения 9

2021-12-15 09:00:52 ​Сколько объектов Number[] будет создано в этом коде?
1.5K views06:00
Открыть/Комментировать
2021-12-09 09:00:12 Что такое Serverless, часть 2

В прошлом посте рассмотрели, как инфраструктура понемногу переходила на аутсорс. Serverless — следующий этап такого перехода.

AWS Lambda — самая первая и популярная платформа для Serverless, поэтому дальше буду говорить про неё.

Важно! Есть ещё термин Lambda Architecture — это вообще про другое.

Итак, в чём суть.

Любое приложение — это набор функций. Допустим, в интернет-магазине три функции:
Добавить товары
Сделать заказ
Вывести список текущих заказов

Можно запихать всё в один артефакт — получится монолит. Можно каждую функцию обернуть в отдельный сервис — получатся микросервисы.

Serverless абстрагируется от этой проблемы. Мы не распределяем функциональность по артефактам и работаем просто с функциями. Проектируем не сборник фич, а отдельные маленькие компоненты.

Поэтому Serverless считается архитектурой. Или отдельной моделью проектирования, если термин "архитектура" кажется вам неподходящим.

Платформы, на которых запускаются Serverless приложения, называют FaaS — Functions as a Service.

В Петербурге Serverless используют около 10 компаний, что не очень много. Но тренд растёт, даже Сбер уже сделал свою FaaS платформу.

Как это работает?

Каждая функция состоит из:
Исполняемого кода
Списка зависимостей
Списка событий-триггеров
Конфигурации — количество памяти, необходимые права, время жизни функции и тд

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

Станет ли код проще?

Точно нет. Проектировать систему с изолированными функциями гораздо сложнее, чем слепить всё в монолит или десяток сервисов

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

Зачем переходить на Serverless?

Если микросервисы в PaaS или докере нормально справляются, то должна быть веская причина что-то менять. Таких причин может быть две:

Функции развёртываются по необходимости. Облака это удобно, но иногда очень дорого. FaaS модель неплохо снижает стоимость при переменной нагрузке.

Масштабирование по умолчанию. В AWS Lambda автоматически масштабируются и сервисы, и остальные компоненты, например, БД. В докере и PaaS для этого нужно прилагать немало усилий.

Сколько стоит?

Допустим бэкенд мобильного приложения принимает за месяц 3 миллиона запросов, среднее время — 120мс. Выделим для одной функции 1536 МБ и процессор х86.
Плата за месяц в таком случае будет 2,7 доллара.

Это только AWS Lambda, остальные компоненты оплачиваются отдельно.

Что ещё

Никаких Ansible, Docker, bash скриптов
Базовый мониторинг и аналитика из коробки
Spring Cloud и плагин IDEA AWS Toolkit сильно облегчают разработку

Нужны отличные навыки проектирования
Своеобразное тестирование

Сложно адаптировать уже существующие приложения
Больше задержек, response time может увеличиться

В целом Serverless — очевидный тренд, который набирает обороты. Жду в ближайшие два года тонну докладов на конференциях на эту тему, как когда-то было про микросервисы и реактивное программирование
1.5K views06:00
Открыть/Комментировать
2021-12-07 09:00:14 Что такое Serverless. Часть 1: предыстория

По статистике 2021 года около 10% проектов уже используют архитектуру Serverless.

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

— Зачем переходить с микросервисов на Serverless? Что мы от этого получим?

Для ответа немного углубимся в историю инфраструктуры.

Всё своё

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

Много расходов на оборудование и персонал
Железки надо закупать, подключать и нельзя сдать обратно. Сложно наращивать мощности или оптимизировать нагрузку

IasS — Infrastructure as a Service

В 2006 появился Amazon EC2 и начался тренд на IaaS: код запускается не на собственных серверах, а на арендованных.

Это самый простой вариант облачной инфраструктуры. Сейчас подобную услугу предлагают AWS, Google, DigitalOcean, Microsoft, IBM, SAP, для патриотов есть Яндекс и Сбер.

Легко добавлять и сокращать ресурсы
Меньше расходы на персонал
Не надо беспокоиться об отключении электроэнергии и потопах

Инфраструктура всё ещё требует внимания — на арендованной виртуалке надо установить ОС, JVM, все службы и обновления, настроить компоненты и развернуть сервисы.

Большая часть этих операций почти у всех одинакова. Так что дальше расходимся на две ветки:

PaaS — Platform as a Service

PaaS = IaaS + ОС + базовый мониторинг + легко подключаемые компоненты

Облачные провайдеры берут на себя больше рутинных операций. Выглядит как будто работаешь с удалённой машиной. Можно довольно легко добавить БД, мониторинг, кэши, очереди и связать их между собой в настройках.

Примеры PaaS: Heroku, AWS Elastic Beanstalk и тд

Много готовых компонентов — можно быстро настроить работающую систему
Набор компонентов большой, но всё же ограниченный. Если использовать что-то непопулярное, то придётся искать обходные пути
Сильная привязка к вендору. Выбрал PaaS от амазона — скорее всего файловое хранилище, очереди и БД тоже будут амазон.

Докер

Популярная альтернатива PaaS — докер-контейнеры в предоставленных виртуалках. В каждом контейнере свой runtime и все нужные для сервиса файлы.

Для управления контейнерами есть куча инструментов — Kubernetes, Mesos, Google Container Engine. Amazon ECR и Google CR помогают с хранением докер-образов, AWS Fargate — с масштабированием.

Супер гибкость. Можно собрать любые компоненты с любыми настройками и связать их как угодно
Cложно выбирать и долго настраивать

Заметили, что не было ни единого слова про архитектуру? PaaS и Docker только упрощают сборку инфраструктуры. Пока что нет разницы, что запускать внутри — гигантский монолит или сервис из трёх файлов.

В следующем посте перейдём уже к Serverless. Поговорим, почему это называется архитектурой и что ещё готовы взять на себя облачные провайдеры.
1.9K views06:00
Открыть/Комментировать
2021-12-02 09:00:12 DRY для джуниора и сеньора

Раз уж пошли по базовым принципам, то сегодня разберём DRY: Don't Repeat Yourself.

Такой популярный и такой обманчиво простой.

Отношение к DRY эволюционирует по мере роста разработчика и проходит через четыре этапа:

Этап 1. Стажёр

Боготворит DRY, считает дублирование кода ужасным грехом. Действительно, зачем писать несколько раз одно и то же?

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

Этап 2. Джуниор

Любит DRY, но понимает, что любить — значит страдать.

Проект становится больше, а бизнес-логика — сложнее. Добавить в метод ещё один if уже не так просто. Сложно разбираться в коде, сложно писать тесты, но чего не сделаешь ради хорошего кода. А хороший код на этом этапе — это максимально сжатый код

Этап 3. Мидл

Всё ещё любит DRY, но более возвышенно — на уровне иерархий и паттернов. Когда приходится дублировать код, то грустит, что на проекте плохая архитектура.

Этап 4. Сеньор

Распилил монолит на сервисы, реализовал десятки крупных фич и отдал сердце SOLID.

А теперь по делу

Часто начинающие разработчики считают, что хороший код — это суперконцентрированный код с кучей паттернов и хитрых приёмов.

В больших проектах хороший код — это тот, который легко читать, тестировать и поддерживать. Оптимизации и приёмчики - это совсем небольшая часть кодовой базы.

Если бизнес-процессы не пересекаются, то связывать их искусственно с помощью кода — плохая идея.

Класс User c 20 полями. 10 полей используются в 1 сервисе, другие 10 - в другом, половина объекта всегда пустая
Универсальный метод с 7 параметрами под разные случаи
Сложная иерархия с кучей шаблонных методов

Единственный плюс — меньше кода. Зато
Плохая читаемость
Сложно писать тесты
Сильная связность. Функцию тяжело поменять или вынести в другой модуль

Я не говорю, что копипаст - единственный шанс на хорошую архитектуру. Переиспользовать код можно, если это действительно универсальные компоненты и та же цепочка бизнес-процессов. Но такое понимание приходит только с опытом.
1.9K views06:00
Открыть/Комментировать
2021-11-30 09:00:22 Чем отличаются Dependency injection, Dependency invertion и Inversion of Control

Прошлый пост про Liskov, как говорится, "взорвал мой директ", поэтому на этой неделе расскажу про ещё два популярных принципа.

Сегодня про букву D из SOLID — Dependency Inversion. Что это, и чем отличается от Dependency injection и Inversion of Control. Понимание пригодится на собеседованиях, при чтении статей по дизайну и архитектуре.

Будем разбираться на простом примере: класс Service записывает логи в файл через класс FileLogger:

class FileLogger {…}
class Service {
FileLogger logger=new FileLogger();
}

Сделаем код чуть лучше с помощью разных принципов:

Dependency injection
— компоненты создаются не внутри класса, а где-то в другом месте.

Как реализовать: перенести инициализацию логгера в конструктор или сеттер:

class Service {
FileLogger logger;
Service (FileLogger logger) {
this.logger=logger;
}
}

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

Историческая справка
Когда Spring ещё не был популярен, в проектах использовался паттерн Service Locator.

Суть: компоненты создаются в классе ServiceLocator, а другие классы получают к ним доступ через статические методы:

class ServiceLocator {
static Logger logger = …
static Logger getLogger() {
return logger;
}
}

class Service {
Logger logger=ServiceLocator.getLogger();
}

Dependency invertion (D из SOLID)
Абстракции не должны зависеть от деталей. Детали должны зависеть от абстракций
Модули верхнего уровня не должны зависеть от модулей нижнего уровня. Оба должны зависеть от абстракции

Как реализовать: использовать интерфейс логгера, а не конкретный класс

interface Logger {…}
class FileLogger implements Logger {…}

class Service {
Logger logger=new FileLogger();
}

Интерфейс проще использовать, так как методов меньше
Реализацию легко заменить
Оба класса проще тестировать

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

IoC - Inversion of Control
В маленьких программах жизнь начинается в методе main. Программист создаёт объекты и вызывает их методы, все шаги явно прописаны.

Inversion of Control — это когда ход выполнения программы задаёт фреймворк. Например, Spring создаёт объекты, принимает запросы и не даёт программе завершиться.

Как реализовать: использовать аннотации фреймворка

@Component class FileLogger {…}
@Component class Service {
@Autowired
FileLogger logger;
}

Меньше скучного кода
Низкая связность — код легко читать, менять и тестировать

Резюме:
Dependency injection — класс не создаёт компоненты напрямую, они передаются через конструктор или сеттер
Dependency invertion — класс работает с другими компонентами через интерфейс
Inversion of Control — ход программы задаёт фреймворк

Ответ на вопрос перед постом:
Это словоблудие относится к Dependency injection
1.2K views06:00
Открыть/Комментировать
2021-11-30 09:00:22
Паттерн проектирования, который в соответствии с принципом единственной обязанности передает другому объекту ответственность построения требуемых ему зависимостей внешнему, специально предназначенному для этого общему механизму - это:
Anonymous Poll
35%
Dependency Injection
10%
Dependency Invertion
42%
Inversion of Control
13%
Factory Method
492 voters1.2K views06:00
Открыть/Комментировать
2021-11-26 09:00:12 L is for Liskov

— SOLID принципы знаете?

Думаю, нет разработчика, который не слышал на собеседовании такой вопрос.

Расшифровку знают многие. А вот практические знания часто ограничиваются Single Responsibility и Interface Segregation.

Как применять остальные буквы в ежедневной работе — не всегда понятно. Больше всего вопросов возникает насчёт L — Liskov Substitution Principle. О нём и будет сегодняшний пост.

Полное определение звучит так:

Пусть q(x) является свойством верным относительно объектов x некоторого типа T. Тогда q(y) также должно быть верным для объектов y типа S, где S является подтипом типа T.

По-простому: если заменить класс А на подкласс B, то система будет работать корректно и без неожиданных сайд-эффектов.

A service1 = new A();
A service2 = new B();

Для наблюдателя service1 и service2 ведут себя совершенно одинаково. Класс-наследник дополняет поведение родителя, а не замещает его. В результате система работает более предсказуемо.

Как это выглядит на практике:

Выходной тип метода в наследнике такой же как у родителя или расширенный

Базовый класс: Info getInfo()

Наследник:
BigInfo getInfo()
Object getInfo()

Подклассы не бросают дополнительных исключений, но могут уменьшить их список

Базовый класс: void save() throws FileNotFoundException

Наследник:
void save()
void save() throws FileNotFoundEx, InterruptedEx

Java — типизированный язык, поэтому пункты 1 и 2 контролируются компилятором.

Типы входных параметров те же или менее строгие. Пункт для общего понимания, тк для Java это неприменимо

Базовый класс: void add(Account acc)

Наследник:
void add(Object acc)
void add(AdminAccount acc)

Следующие пункты компилятор уже не проверит, это целиком ответственность программиста.

Метод подкласса делает то же, что и метод базового класса

Базовый класс: метод countVisitors считает пользователей

Наследник:
Считает пользователей чуть по-другому
Считает пользователей, обновляет статистику, сохраняет результат в БД

Метод наследника взаимодействует с теми же сущностями:
Метод родителя увеличивает счётчик - подкласс тоже увеличивает
Метод родителя не меняет поле - подкласс тоже не меняет
Метод родителя вызывает другие методы в определённом порядке - подкласс делает то же самое

А что можно вообще?

Если в подклассе объявлены новые поля, то методы подкласса могут делать с ними что угодно. На этом всё

Правила выше - очень строгие. Но и наследование — штука непростая, это самая сильная связь между сущностями. Часто единственный плюс — это краткость кода, но по ходу развития проекта ограничения доставляют всё больше проблем.

Нарушения принципа подстановки — повод пересмотреть иерархию наследования или совсем от неё отказаться.
2.1K views06:00
Открыть/Комментировать
2021-11-18 09:00:03 8 ошибок начинающих разработчиков

Рассмотрим типичную историю начинающего разработчика или стажёра.

Чтобы стать программистом, стажёр долго учился. Прошёл курсы и сделал несколько учебных проектов. У них маленькая кодовая база, один пользователь, а главная задача - отработать алгоритм или попробовать фреймворк.

В этих условиях у стажёра сформировался стиль написания кода.

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

В этом посте я опишу 8 таких паттернов на простых примерах.

Изменение входных параметров

Задача: получить список заказов пользователя. Новичок скорее всего напишет такой код:

List list = new ArrayList();
process(user, list);

Во входной параметр list записывается результат метода. Такой процедурный стиль обычно идёт из учебных заданий по алгоритмам, где экономится каждый бит и максимально сокращается количество действий.

В системах со сложной бизнес-логикой такой подход усложняет чтение кода, тестирование и возможную параллелизацию.

Как лучше: использовать выходные параметры, не менять входные данные, давать осмысленные имена методам:

List orders = getOrders(user);

Сложные универсальные методы

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

Новички часто используют принцип Don't Repeat Yourself на максималках и пишут универсальный метод с кучей параметров и десятком if внутри:

getDiscount(user, true, true, limit, true)

Как лучше: сфокусированные методы для разных ситуаций

getDiscountNew(user)
getDiscountLoyal(user)

К ситуациям выше в комплекте идут

Длинные методы
Любовь к статическим методам

Как лучше: небольшие методы, связанные с конкретным классом. Связность ниже, ошибок меньше.

Сложное проектирование

Задача: завести три типа пользователей - новые, обычные и привилегированные. Новички скорее всего сделают интерфейс, 3 класса и статический класс с фабричными методами и билдером.

Как лучше: как можно проще. Например, один класс пользователя с полем Тип.

PS Все предложенные "как лучше" не всегда лучше. Но думаю, вы поняли идею.

Нулевое или минимальное покрытие тестами
как следствие больших сложных методов и недостаточной инкапсуляции.

Низкий уровень ответственности

Пункт не относится к разработке, но очень актуален для начинающих. Проявляется в двух формах:

Непонятно, что происходит. Человек сидит и молчит до последнего, пока не спросишь статус задачи или про возможные трудности. Он умалчивает проблемы или переносит на других:

— Что с задачей, которую я тебе дала 3 дня назад?
— Я не понял, куда смотреть, потом меня HR позвал бумаги подписывать, потом я настраивал гит, увидел другую задачу и переключился на неё.

Код не решает проблему, а заметает симптомы:

— Тесты мешали сделать пул-реквест, так что я их отключил.

Слабые коммуникативные навыки

— Как ты починил баг с расчётом ставки?
— Там через геттер фабричный метод нашёл, и потом докер с постгрёй поднял посмотреть, в логах был фильтр урезанный, я письмо отправил тебе в цц, но вроде скоуп не тот или тот, короче, запушил
— В чём была ошибка?
— Там два двоеточия вылезло
— Где?
— В дебаге

Эти ошибки ожидаемы в начале работы, и ничего страшного в этом нет. Я их тоже делала Чем быстрее вы перестроитесь на командный стиль разработки, тем вероятнее пройдёте испытательный срок и быстрее вольётесь в проект.
2.0K viewsedited  06:00
Открыть/Комментировать
2021-11-16 09:00:16 VM Options

- это параметры, которые указываются при запуске JVM. В этом посте расскажу, чем они отличаются и как безопасно перейти на новую версию java. В конце будет список самых популярных опций.

Все JVM опции делятся на три группы:

Стандартные
Пишутся через минус и поддерживаются всеми JVM.
Пример: -classpath, -server, -version

Нестандартные
Начинаются на -Х и определяют базовые свойства JVM. Могут не работать во всех JVM, но если поддерживаются, то вряд ли удалятся.
Пример: -Xmx, -Xms

Продвинутые
Начинаются на -ХХ и касаются внутренних механизмов JVM. Не поддерживаются всеми JVM, часто меняются и удаляются.
Пример: -XX:MaxGCPauseMillis=500

Некоторые продвинутые опции требуют дополнительных флажков. Для экспериментальных фич обязателен -XX:+UnlockExperimentalVMOptions. Многие фичи диагностики не заработают без -XX:+UnlockDiagnosticVMOptions

Количество опций часто меняется. В 11 версии OpenJDK 1504 опции, а в 17 на 200 опций меньше.

Цикл отключения опций не совсем стандартный.

Обычно делается как:
Что-то помечается @Deprecated
Спустя время код удаляется

VM Options используют более длинный цикл:

Deprecate: функционал работает, при запуске появляется warning
Obsolete: функция не выполняется, JVM пишет предупреждения
Expired: JVM не запускается

Переход на новую версию java

Опции очень нестабильны и часто меняются. Чтобы безопасно обновить версию java, проверьте ваш набор опций через JaCoLine. Он подстветит устаревшие или бесполезные опции.

Полезные опции для java 11

Память

Начальный размер хипа: -Xms256m в абсолютных значениях, -XX:InitialRAMPercentage=60 - в процентах от RAM
Максимальный размер хипа: -Xmx8g или -XX:MaxRAMPercentage=60
Снять heap dump при переполнении памяти: -XX:+HeapDumpOnOutOfMemoryError, адрес выходного файла задаётся в -XX:HeapDumpPath

Сборщик мусора

Serial GC: -XX:+UseSerialGC
Parallel GC: -XX:+UseParalllGC
CMS: -XX:+UseConcMarkSweepGC
G1: -XX:+UseG1GC (вариант по умолчанию)
ZGC: -XX:+UnlockExperimentalVMOptions -XX:+UseZGC
Shenandoah: -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC

Вывести статистику сборщика при завершении работы: -XX:+UnlockDiagnosticVMOptions ‑XX:NativeMemoryTracking=summary ‑XX:+PrintNMTStatistics

Базовое логгирование коллектора: -Xlog:gc
Максимально информативное: -Xlog:gc*

Посмотреть все доступные опции:
Нестандартные: java -X
Продвинутые: java -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+PrintFlagsFinal
2.4K views06:00
Открыть/Комментировать
2021-11-16 09:00:16
Чем отличаются опции JVM, которые начинаются на -X, от опций, которые начинаются на -XX?
Anonymous Poll
6%
У -Х приоритет над -ХХ
7%
У -XX приоритет над -Х
12%
-X работают на всех операционных системах, -XХ специфичны для отдельных ОС
14%
-ХХ могут быть удалены, -Х вряд ли
62%
-Х задаёт поведение JVM на старте JVM, -ХХ определяет процесс работы
582 voters2.4K views06:00
Открыть/Комментировать