Статьи

Логика должна прятаться в поле зрения

Я уверен, что вы слышали слова «копаться в коде» раньше. Это первый Второе, что делает разработчик, когда они новички в проекте. Они открывают IDE, ищут точку входа в приложение и начинают нажимать Ctrl +, пока не найдут «бизнес-логику». Затем они начинают читать код, чтобы понять, как марионетки в кодовой базе оживают.

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

Все, что вы читаете в этом блоге до сих пор, несовместимо с идеей «уровня обслуживания» или «бизнес-классов». Как только объекты оживают и занимаются своим делом, просто бессмысленно пытаться разорвать их на части. Все ваши классы должны быть маленькими, представлять что-то и выполнять небольшую часть работы для вас, в то время как ваш код в целом должен выглядеть как дерево объектов, и вы должны видеть new оператор гораздо чаще, чем в традиционном приложении.

Разница в этом подходе заключается в том, что когда вы нажимаете Ctrl + клик от одного метода к другому, бизнес-логика не становится понятнее; наоборот, вы должны найти еще одну абстракцию и так далее, пока не найдете самую низкую абстракцию (например, открытие File ), которая может не иметь ничего общего с общей картиной.

Так что, если все копание только дает дальнейшие абстракции, как мы можем понять, как все работает вместе? Ответ таков: сделав шаг назад и наблюдая, как создаются объекты. В объектно-ориентированном программировании бизнес-логика не должна быть видна в методе Сервиса. Вместо этого должно быть видно, как некоторые объекты сочетаются / украшаются вместе с другими объектами! Код в методе никогда не должен быть достаточно, чтобы понять слишком много бизнеса. Я считаю, что если это так, то необходим некоторый рефакторинг: должны быть добавлены новые реализации, часть работы этого объекта должна быть делегирована и т. Д.

Взгляните на следующий класс (ctor опущен):

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public final class Hello implements Knowledge {
 
    private Knowledge notHello;
 
    @Override
    public Steps start(final Command com, final Log log) {
        final Steps resolved;
        if("hello".equalsIgnoreCase(com.type())) {
            resolved =  new GithubSteps(
                new SendReply(
                    String.format(
                        com.language().response("hello.comment"),
                        com.author()
                    )
                ),
                com
            );
        } else {
            resolved = this.notHello.start(com, log);
        }
        return resolved;
    }
}

Это одно из знаний чат-бота, в какой-то момент он скажет «привет». Но каковы общие шаги, что он делает дальше, или что происходит, если команда не «привет»? Если вы this.notHello.start(...) в this.notHello.start(...) вы просто this.notHello.start(...) интерфейс Knowledge . Более того, почему имя метода start ? Разочарование. Однако, если вы вернетесь немного назад, вы должны увидеть, как этот объект используется:

1
2
3
4
5
6
7
8
final Conversation talk = new Conversation(
    new Hello(
        new RunScript(
            new Confused()
        )
    )
);
talk.start(command);

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

Из вышеприведенной идеи можно сделать вывод, что каждый объект должен иметь четкую цель. Таким образом, никогда не должно быть объекта, который «не стоит тестировать» (например, модель get/set ): если фрагмент кода не стоит тестировать, то это означает, что он не имеет реального места в архитектуре. и не должно существовать.

Если бы я проектировал бота традиционным способом, приведенная выше конструкция, вероятно, была бы заменена следующим:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
/**
 * Fetch the steps for different situations.
 \*/
public class StepsService {
 
    public Steps getHello(final Command com) {
      //...
    }
 
    public Steps getRunScript(final Command com) {
      //...
    }
 
    public Steps getConfused(final Command com) {
      //...
    }
 
}

Помимо процедурного кода, который создает этот класс (теперь клиенту предстоит выяснить, какие шаги ему нужны и когда), у нас есть еще одна проблема: мы не знаем, кто использует вышеуказанную «логику» в приложении – любой клиент может где-то внедрить его, и если мы изменим какую-либо строку кода в любом из 4 методов get* , у некоторых клиентов могут возникнуть проблемы.

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

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

Мнения, высказанные участниками Java Code Geeks, являются их собственными.