Задать вопрос
  • Как правильно все перенести в микросервисы и сделать устойчивую архитектуру?

    inoise
    @inoise
    Solution Architect, AWS Certified, Serverless
    Итак, куча всего смешана, давайте разбираться. У нас имеется:
    - X различных сервисов с различными интерфейсами
    - отсутствие транзакционности
    - отсутствие гарантии доступности сервисов
    - необходимость это все контролировать

    Как в реальности это решается:
    - Saga Pattern - отличная вещь, появилась именно как микросервисная транзакционность
    - нам потребуется оркестрация. Я не очень в курсе что сейчас по on-prem решениям но из моего мира есть AWS StepFunctions. Ищем аналоги для своего энва
    - если нет готовых решений то придется строить свою событийную архитектуру на очередях с брокерами и медиаторами
    - для проблемы не доступности сервисов придется использовать exponential backoff или exponential retry. Опять, же, в моем мире это решает AWS SNS.

    Вообще, во времена до облаков я такое делал на RabbitMQ и смекалке, но с любыми такими системами встает проблема валидация контрактов, так что только вам решать на какую часть переносить сложность.
    Ответ написан
  • Что является реализацией PreparedStatement?

    @Kot1que
    пишу на жаве
    Реализации предоставляет JDBC драйвер конкретной базы данных. Пример PostgreSQL

    Делается это для того, чтобы не быть зависимыми от конкретного драйвера БД. Если вы поменяете драйвер на другой (например, смените базу данных), то при использовании интерфейсов ваш код вообще никак не поменяется.
    Ответ написан
    Комментировать
  • Это и есть полиморфизм?

    Fesor
    @Fesor
    Full-stack developer (Symfony, Angular)
    Нет.

    Полиморфизм, как следует из названия, это когда что-то маскируется под что-то другое. Это свойство системы типов языка на котором вы пишите, он может позволять вам делать вещи, маскирующие свой внешний вид ("названия") но все же это не та же вещь. Ну и стоит заметить что у полиморфизма есть еще разные виды. Например:

    Параметрический полиморфизм. Это когда мы можем написать один код, с одним набором имен, которые работает с разными типами аргументов. Пример - шаблоны из C++ или дженерики в Java. То есть "имена" методов одинаковые, потому что они в одном экземпляре. Реализация одна, одно поведение. А вот аргументы могут отличаться.

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

    Полиморфизм подтипов. При этом у нас как раз таки будет одни и те же имена и может быть совершенно разное поведение. Например мы можем создать какой-то базовый тип, который, к примеру, отправляет email-ы, и создать его подтип, который вместо отправки email-ов записывает в лог то что мы хотели отправить, после чего уже отправляет данные дальше другому объекту этого типа (шаблон проектирования декоратор).

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

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

    Ad-hoc полиморфизм - это пожалуй самый интересный вид полиморфизма с которым можно долго холиварить. По сути при этом виде полиморфизма, у нас одинаковые имена, а поведение зависит от входящих аргументов. Пример - перегрузка методов в C++. Интересен этот вид полиморфизма в основном тем, что он не является "настоящим".

    При динамической системе типов не требуется никаких дополнительных возможностей вроде той же перегрузки методов для достижения ad-hoc полиморфизма. Тупо кидаем что хотим в функцию, а там уже if-ами рагребаем или же приводим к какому-то одному варианту. Отдельные конструкции нужны в языках со статической системой типов. То есть нам нужно еще на этапе компиляции кода знать какие именно типы могут приходить в наши методы, и в зависимости от оных вызывать тот или иной код.

    Среди PHP-разработчиков немало тех, кто мечтает увидеть в этом языке с динамической системой типов честную перегрузку методов как например в Java или C++. Просто так, потому что if-ы это плохо и лучше уж пусть они будут неявные на уровне компилятора/рантайма.

    Полиморфизм с приведением типов - еще один вид "не настоящего" полиморфизма. Мы "эмулируем" полиморфизм за счет того, что на уровне рантайма языка происходят касты действительного в желаемое. Например в PHP мы можем выставить у функции тайпхинтинг string, и можем внутри иметь одно и то же поведение для всех входящих аргументов. Передать же в качестве аргумента мы можем все что можно скастить в строку.

    Собственно, это очень похоже на параметрический полиморфизм по смыслу, но далеко не так гибко и не дает того контроля за системой.
    Ответ написан
    Комментировать
  • В чем преимущества процессов над потоками?

    wataru
    @wataru
    Разработчик на С++, экс-олимпиадник.
    Главное приемущество: независимость процессов. Потоки делят между собой одну память и ресурсы системы (всякие хандлеры в винде, например).

    Если один из процессов завершится или, что чаще происходит, упадет - остальные не будут затронуты. Плюс эта независимость позволяет делать песочницы для безопасности. Так, все современные браузеры запускают js и вообще каждую вкладку в отдельном процессе. Даже если куллхацкер полностью взломает браузер через специальный сайт, он окажется в процессе, который особо прав никаких не имеет, библиотеки особо интересные туда не загружены, а все общение с внешним миром - через жестко прописанные протоколы ipc (inter-process communication). Так что злодею придется взламывать еще и их.

    Эта же независимость позволяет выполнять работу даже после завершения основного процесса. Так, если вы хотите сделать автообновятор программы, то после скачки/установки нового приложения, надо будет перезапустить основное приложение, чтобы перезаписать исполняемый файл (по крайней мере в винде). Но поток завершится вместе с программой и кто же тогда потом будет ее запускать? А вот процесс останется работать.

    Недостатки тут - ресурсоемкость. С точки зрения системы поднять и поддерживать процесс гораздо больше работы чем сделать это с потоком. Плюс упомянутое выше ipc - это лишняя работа на каждый чих.
    Ответ написан
    Комментировать
  • Процесс, поток, отличия?

    gbg
    @gbg
    Любые ответы на любые вопросы
    В современных ОС процесссы используют виртуальную память - они не видят память других процессов. То есть, если у процесса A по адресу 0x100 находится байт 0x08, а у процессе B по адресу 0x100 находится байт 0x42, процесс A будет всегда читать 0x8, а процесс B - 0x42.
    Каждый процесс сидит в своем адресном пространстве.

    Потоки сидят внутри процесса и видят одну память - адресное пространство этого процесса. Это и называется шеринг - раз адресное пространство общее, то и память общая. Если поток a в процессе A запишет по адресу 0x100 девятку, все другие потоки тоже считают оттуда девятку. Процесс B же по этому адресу продолжит читать 0x42

    Почитайте по ключевым словам "Защищенный режим, "Виртуальная память", MMU, "Процесс", "Поток"
    Ответ написан
    Комментировать
  • Какая разница между генераторами и массивами?

    @Garry_Galler
    Почему генераторы потребляют меньше памяти, чем массивы?

    Простой ответ.

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

    Но генератор это и не структура данных, чтобы как-то напрямую их сравнивать.
    Генератор это паттерн программирования, который может быть реализован как особая функция.
    Очевидно, что реализация генератора должна иметь некоторые предпосылки. Нельзя просто так взять и перестать пользоваться массивами и писать только генераторы.
    Когда у нас есть возможность (или настоятельная необходимость) не считывать все данные (сеть, диск) полностью в ОЗУ, а забирать их чанками (порциями), чтобы обработать, отправить результат в вызывающую функцию и тут же забыть его, вот тогда стоит подумать о возможности реализации своего генератора\итератора, если таковой не предоставляется библиотекой языка.

    Сложный ответ (для тех, кто умеет читать много букв)

    Генератор это подвид итератора. Итератор в контексте программирования это поведенческий паттерн, описанный в знаменитой книге "банды четырех". Его суть - в предоставлении "возможности последовательно обходить элементы составных объектов, не раскрывая их внутреннего представления."

    Итераторы в разных языках программирования как правило представлены специальным интерфейсом (или протоколом), который нужно реализовать для создания собственных итераторов.

    Генератор это частный случай итератора, который, как правило, создается функцией возвращающей объект итератора. Для упрощения создания генераторов языки программирования предоставляют программистам специальное ключевое слово yield, которое должно использоваться вместо return (либо совместно с ним). yield создает итератор под капотом, позволяя не реализовывать весь протокол Итератора вручную.

    Справка из википедии:
    Одним из способов реализации итераторов является использование сопроцедур, которые могут возвращать управление (и вычисленные результаты) несколько раз, запоминая своё состояние и точку возврата в предыдущем вызове. В некоторых языках сопроцедуры могут быть представлены особого вида функциями, называемыми генераторами. Генератор — функция, которая помнит, в каком месте был предыдущий return (yield), и при следующем вызове возобновляет работу с прерванного места
    .

    Еще одно определение генераторов:
    Генератор — это объект, который сразу при создании не вычисляет значения всех своих элементов.
    Он хранит в памяти только последний вычисленный элемент, правило перехода к следующему и условие, при котором выполнение прерывается.
    Вычисление следующего значения происходит лишь при выполнении метода next(). Предыдущее значение при этом теряется.
    Этим генераторы отличаются от списков — те хранят в памяти все свои элементы, и удалить их можно только программно. Вычисления с помощью генераторов называются ленивыми, они экономят память


    Суть работы yield одинакова для всех ЯП.
    Вот описание принципа работы yield для языка C# ( вы же все знаете Джона Скита? - главного эксперта stackoverflow и автора известных книг), где это ключевое слово появилось довольно давно (в 2005-м, в Python -2001-м, а вообще - в 1975 году) и многие другие ЯП со временем переняли его.

    При наличии оператора yield return внутри метода компилятор строит на основе данного метода конечный автомат. Для реализации итератора конечный автомат обладает следующими свойствами:

    1. Он имеет некое начальное состояние
    2. При вызове MoveNext() выполняется код из метода GetEnumerator() по тех по, пока не будет достигнут оператор yield return;
    3. Когда используется свойство Current, он возвращает последнее выданное значение.
    4. Он должен знать, когда выдача значений завершена, чтобы метод MoveNext() мог возвратить false;



    Генераторы как подкласс итераторов имеют огромное значение для так называемых "ленивых вычислений".

    Они позволяют создавать (вычислять) значения на лету и тем самым экономить память, в отличие от "энергичных вычислений".
    Таким образом, на основе генераторов можно реализовать создание бесконечных последовательностей, чтение из бесконечного потока, чтение данных не вмещающихся в ОЗУ компьютера, ленивые вычисления, конвейерную обработку данных (можно создавать целые pipelines из генераторов), асинхронные корутины и т .д.

    P.S. Генераторы не имеют никакого отношения к циклам (цикл это только лишь один из способов обхода генераторных коллекций - извлечения значений из них ). Также генераторы не являются никаким синтаксическим сахаром ("красивой оберткой для цикла"), как было написано в других ответах. Синтаксических сахаром является исключительно само ключевое слово yield. При выполнении кода с yield он компилируется в Finite State Machine .
    Ответ написан
    6 комментариев
  • Что такое обратный вызов в программировании?

    andreydobrin
    @andreydobrin
    Сложно , но это пока
    Все очень просто:
    Вы правильно сказали, что это функция, передающаяся как аргумент в другую функцию:
    пример с js
    function  first(callbak){
    ----здесь пишем код функции----
    ---- здесь можно записать код "функции обратного вызова" 
    и она сработает тогда, когда выполнится функция first()----
    }

    Простым языком - есть одна функция и у нее в параметре другая. Сперва выполнится сама функция, а потом та, которая является аргументом этой функции.
    Ответ написан
    Комментировать
  • Callback и listener?

    zagayevskiy
    @zagayevskiy Куратор тега Kotlin
    Android developer at Yandex
    Listener(слушатель) это callback(обратный вызов), ждущий наступления некоего события. То что ты используешь лямбду вообще никаким образом не связано с потоками, с тем, кто перед кем вызовется. Когда вызовешь, тогда и будет.
    Listener и Observer это вопрос терминологии и наименования. Слушатель и обозреватель. По сути одно и тоже.
    Ответ написан
    Комментировать
  • Почему потоки в Java ведут себя так неочевидно?

    EugeneP2
    @EugeneP2
    Java Dev
    Для создание потока нужно определенное время и для его работы должно быть выделено процессорное время.

    Метод Thread.start() не ждет создание потока, а сразу же отпускает поток вызвавший его.

    Основной поток Main уже создан и выполняется, потому он срабатывает быстрее.

    Egg - не демон-поток, потому джава будет ждать его завершения, т.е. вывода на экран "EGG !"
    Ответ написан
    Комментировать
  • Как запускать 10+ потоков в java без повторяемости кода?

    leahch
    @leahch
    3D специалист. Dолго, Dорого, Dерьмово.
    Как-то так....
    ArrayList<Thread> mythreads = new ArrayList<Thread>();
    int tcount = 1000;
    for(int i=0; i< tcount; i++) {
         Thread thr = new myThread();
         mythreads.add(thr);
         thr.start();
    }
    for (Thread thr : mythreads) {
         thr.joint();
    }

    или по модному
    ExecutorService es = Executors.newCachedThreadPool();
    for(int i=0;i<5000;i++)
        es.execute(new Runnable() { /*  your task */ });
    es.shutdown();
    boolean finshed = es.awaitTermination(1, TimeUnit.MINUTES);
    Ответ написан
    Комментировать
  • Как работают потоки в Java?

    zagayevskiy
    @zagayevskiy Куратор тега Java
    Android developer at Yandex
    join() блокирует родительский поток до тех пор, пока не завершится поток для которого вызван join(). Собственно, именно потому, что после вызова join() родитель не идёт дальше, он и не завершится раньше.
    Для иллюстрации попробуйте в вашем Calculate вставить бесконечный цикл, а после thread.join(); - System. out.println("joined");

    На тему кода - всё ок, кроме того, что не учтён InterruptedException, который может выбросить join
    Ответ написан
    5 комментариев
  • Приведение к типу интерфейса: какой смысл?

    Вы не понимаете механику полиморфизма и виртуальных вызовов.

    Интерфейс - это окно, через которое вы смотрите на реальный объект. Механизм позднего связывания, а именно - виртуальных функций - позволяет вам иметь ссылку типа ISome, которая, тем не менее, ссылается на РЕАЛЬНЫЙ объект с РЕАЛИЗАЦИЯМИ методов интфейса ISome, и вызывать эти методы через интерфейс.

    Обратите внимание - компилятор не позволит вам написать new ISome; (можете попробовать), т.к. невозможно инстанциировать интерфейс. Это всего лишь договор о взаимодействии, и для того, чтобы взаимодействие произошло, нужно чтобы этому договору подчинялись какие-либо реальные объекты.

    Когда вы приводите тип ссылки от SomeObject к ISome, сама ссылка НЕ МЕНЯЕТСЯ (по кр. мере вы этого не увидите), переменная нового типа ссылается на ТОТ ЖЕ САМЫЙ объект, а не на "блок методов". Ссылки всегда указывают на конкретные объекты, невозможно сослаться на "блок методов", не имеющий реализации, язык так не работает. Суть интерфейсов в том, что они соединяют в себе идею позднего связывания и строгую типизацию одновременно. Используя ссылку типа ISome вместо SomeObject, вы как бы смотрите на тот же объект через другое "окно", и видите у него другой набор методов. Это важнейший этап в процедуре абстрагирования - найти у объектов похожие черты, и реализовать ряд алгоритмов на этой основе. Как вы и сами уже заметили - единообразно работать с объектами разных типов.
    Ответ написан
    1 комментарий
  • Новости с категориями, нужна вторая нормальная форма SQL?

    flapflapjack
    @flapflapjack
    на треть я прав
    Есть новости. У новостей есть категории.


    Просто создайте столбец category_id в таблице с новостями и всё. Зачем еще одна таблица?

    P.S.

    Третья таблица имеет смысл, только если одной новости может быть назначено сразу несколько категорий, по аналогии с тегами на тостере.
    Ответ написан
    1 комментарий
  • Нормализация БД. Зло или добро?

    Fesor
    @Fesor
    Full-stack developer (Symfony, Angular)
    Все зависит от контекста высказывания (задачи разные бывают). Бросаться в крайности это глупо (только ситхи все возводят в абсолют (с) Оби)

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

    p.s. уточните о чем был проект или скиньте ссылку, любопытно посмотреть на безумца или понять его хотя бы.
    Ответ написан
    Комментировать
  • В чем принципиальное отличие второй нормальной формы от третьей?

    Rsa97
    @Rsa97
    Для правильного вопроса надо знать половину ответа
    Вторая форма ликвидирует зависимости неключевых полей от части ключа.
    Третья нормальная форма исключает зависимость неключевых полей от других неключевых полей.
    Ответ написан
    Комментировать
  • Чем отличается вторая форма нормализации от третьей?

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

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

    Т.к. по серийному номеру мы можем определить модель, а по модели - цену, то цена также зависит от серийного номера: СерийныйНомер -> Цена. Однако эта зависимость транзитивная: цена зависит не непосредственно (!) от СерийногоНомера, а от Модели. Следовательно, это отношение не находится в третьей нормальной форме (хотя находится во второй). Для приведения отношения в 3НФ его нужно разбить на два - МодельАвтомобиля(СерийныйНомер, Модель) и ЦенаМодели(Модель, Цена).

    Следует заметить, что если бы предметная область определяла бы иные зависимости, то и рассмотренное отношение могло быть в другой НФ. Например, если бы у нас все-таки продавались подержаные автомобили, и цену на каждый из них мы бы устанавливали отдельно, то зависимости Модель -> Цена уже б не было, а была бы прямая (нетранзитивная зависимость) СерийныйНомер -> Цена, и отношение подходило под критерии третьей нормальной формы.
    Ответ написан
    Комментировать
  • Что такое нормализация базы данных простыми словами?

    sarapinit
    @sarapinit
    Точу водой камень
    БД нужна чтобы хранить утверждения о состоянии информационной системы.
    Ситуацию в которой утверждений о состоянии какой-то части системы больше чем одно мы считаем дублированием информации. (У вас эта ситуация называется "избыточность").
    Если есть дублирование информации, то возможна ситуация когда утверждения могут противоречить друг другу. В этом случае БД неконсистентна и информационная система может работать некорректна.
    Нормализация БД - устранение дублирования информации в БД с целью достижения консистентности, в первую очередь. Нормальные формы - это шаги которые нужно пройти на пути к консистентности (делай раз, делай два ...)
    Побочным эффектом нормализации является уменьшение размера БД.
    В случаях когда производительность важнее консистентности, выполняют денормализацию БД (внесение избыточности) под определённые запросы, чтобы избежать объединений таблиц (JOIN).
    Ответ написан
    2 комментария
  • Зачем нужны функциональные интерфейсы в java?

    zagayevskiy
    @zagayevskiy Куратор тега Java
    Android developer at Yandex
    Для лямбд - это правильный ответ.
    Создатели джавы не осилили(постеснялись/посчитали ненужным/etc) сделать отдельный кусок синтаксиса для нормального описания функциональных типов, вместо этого сделали так, что интерфейс можно смаппить в лямбду, если у него(интерфейса) ровно один абстрактный метод. Собственно, все эти BiFunction, Predicate, ToLongBiFunction и прочие вырвиглазности - это костыли, чтобы ты мог лямбды передавать куда-то. Их, конечно, не хватит, и придется объявлять свои костыли с подходящей сигнатурой.
    Всё это обвесили аннотацией @FunctionalInterface, чтобы компилятор мог ругаться, что ты делаешь ошибки при объявлении функционального интерфейса. Правда, здорово?(нет).
    Ответ написан
    Комментировать
  • Зачем нужны функциональные интерфейсы в java?

    sergey-gornostaev
    @sergey-gornostaev Куратор тега Java
    Седой и строгий
    Функциональный интерфейс - это не что-то техническое, что реализуется в языке или виртуальной машине. Это просто абстракция, которая была в Java всегда.

    Java задумывалась как исключительно объектно ориентированный язык, поэтому функций в ней никогда не было и нет. Всё поведение располагается в методах. Но для обработчиков событий, например, нужно только поведение, но не состояние, поэтому объект "оборачивающий" метод бесполезен. Обычно обработчик выглядел так:
    someButton.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e) {
            someTextField.setText("Кнопка нажата");
        }
    });

    ActionListener имеет функциональный интерфейс. То есть интерфейс с одним методом, как бы заявляющий "я не класс, я функция".

    В Java 8 просто появились способы более удобно работать с функциональными интерфейсами - в языке появились лямбды, у виртуальной машины появились средства, позволяющие генерировать из лямбд классы с функциональным интерфейсом, а в стандартной библиотеке появился пакет java.util.function, содержащий набор универсальных функциональных интерфейсов, позволяющих передавать и возвращать лямбды между методами в коде разных разработчиков.
    Ответ написан
    1 комментарий
  • Есть ли где-то современный туториал по Spring?

    piatachki
    @piatachki
    Спринг содержит столько проектов, что одним туториалом не обойдёшься. Если автор только начинает осваивать Spring, то рекомендую обратиться к старому циклу вебинаров Евгения Борисова, начать со спринг-потрошителя, там базовая инфа о жизненном цикле бинов и принципах работы контейнера. дальше ютьюб подскажет. Концепции с тех не поменялись. А одного туториала, как следует охватывающего весь фреймворк быть не может. Инфа - 146%.

    Ну и официальная документация. Без неё никуда.
    Ответ написан
    2 комментария