Ответы пользователя по тегу Паттерны проектирования
  • Почему толстые контроллеры – это плохо?

    @Akela_wolf
    Extreme Programmer
    Если строго следовать принципам SOLID, а именно SRP (принцип единственной ответственности), то задачей контроллера является презентовать какую-то часть приложения в виде HTTP endpoint (в частном случае REST).

    Собственно логика не должна находиться в контроллере, потому что способы обращения к этой логике (через HTTP, Service Bus, Telegram, Discord, периодический вызов по крону и т.д.) ортогональны собственно бизнес-логике.

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

    @Akela_wolf
    Extreme Programmer
    В случае Spring Boot JPA вам не нужно создавать реализацию интерфейса репозитория. Достаточно написать интерфейс - реализацию спринг создаст сам.

    Если же хочется сделать руками - то да, 4 интерфейса, 4 реализации. У реализаций может быть общий абстрактный предок, в который будет вынесен общий для всех реализаций код (тут generic-и в помощь).

    Вообще, если следовать принципам чистой архитектуры, то интерфейсы репозиториев должны относиться к модулю бизнес-логики, а реализации - к модулю системы хранения. Тогда, простой заменой одного модуля хранения на другой можно добиться "бесплатной" подмены всех реализаций (потому что на практике обычно не бывает такого, что часть сущностей в файлах, часть в БД, часть в сетевом хранилище - как правило все в каком-то одном хранилище находится)
    Ответ написан
    2 комментария
  • Как избежать преждевременной оптимизации? Или как не зная что будет потом в проекте, обрабатывать исключения?

    @Akela_wolf
    Extreme Programmer
    Для вашей ситуации придумали интерфейсы.

    Вот смотрите, есть логика вывода (Котлин, надеюсь понятно что этот код делает):
    interface Report {
      fun getAverageTime(): BigDecimal
    }
    
    fun printReport(report: Report) {
      println("Average time: ${report.getAverageTime()}") 
    }

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

    Теперь у нас возникает необходимость добавить еще один параметр в отчет:
    interface Report {
      fun getAverageTime(): BigDecimal
      fun getCountWithZeroTime(): Int
    }
    
    fun printReport(report: Report) {
      println("Average time: ${report.getAverageTime()}") 
      println("Participants have zero-time: ${report.getCountWithZeroTime()}")
    }


    И никакой преждевременной оптимизации, только структурированный код.
    Ответ написан
    Комментировать
  • Стоит ли добавлять декоратор для обработки исключений?

    @Akela_wolf
    Extreme Programmer
    Зависит от того какая в обработчиках исключений выполняется логика. Если там просто логирование исключений, никак не связанное с логикой консьюмера - то вполне оправданно эту логику отделить. Но можно и не отделять. Тут все зависит от желания переиспользовать консьюмер и декоратор. Также оправданно отделять подобные независимые вещи для облегчения юнит-тестирования.

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

    @Akela_wolf
    Extreme Programmer
    Главная идея принципа инверсии зависимостей "детали зависят от абстракций, но не абстракции от деталей".
    В приведенном вами примере класс Main зависит от всего: от интерфейса INumberOperation и от обоих классов NumberOperation1, NumberOperation2. То есть тут принцип инверсии зависимостей вообще не работает. Никак.

    Проявляется же он в следующем примере. Пусть у меня есть некая абстрактная логика "прочитай число, выполни над ним операцию, запиши результат". Эта абстрактная логика (потому она и абстрактная) ничего не должна знать ни откуда она читает число, ни какую операцию над ним выполняет, ни куда и как записывает результат. Таким образом, у нас есть модуль, состоящий из
    interface NumberInput {
      int read();
    }
    interface NumberProcessor {
      int process(int a);
    }
    interface NumberOutput {
      void write(int a);
    }
    class Processor {
      private final NumberInput input;  
      private final NumberProcessor processor;
      private final NumberOutput output;
    
      public Processor(NumberInput input, NumberProcessor processor, NumberOutput output) {
        this.input = input;
        this.processor = processor;
        this.output = output;
      }
    
      void process() {
        output.write(processor.process(input.read()));
      }
    }

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

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

    Этот принцип очень хорошо объяснен в книге Р.Мартина "Чистая архитектура", по крайней мере у меня все встало на свои места именно после прочтения этой книги.
    Ответ написан
    1 комментарий