Повторное изучение шаблонов проектирования Java: фактическая модель цепочки ответственности

Java
Повторное изучение шаблонов проектирования Java: фактическая модель цепочки ответственности

Автор: Брат Сяофу
Блог:bugstack.cn - 原创系列专题文章

Осаждайте, делитесь, растите и позвольте себе и другим что-то получить! 😄

Введение

场地和场景的重要性

Стрельбе🏹 нужно ходить в тир, чтобы учиться, лыжам🏂 нужно ехать на снежное поле, чтобы испытать, водить🚗 нужно уметь практиковаться в дороге, а разработка программирования может не только завершить функциональный процесс продукта, но также необходимо обеспечить надежную работу системы. Например, вы можете услышать некоторые показатели системного мониторинга;QPS,TPS,TP99,TP999,可用率,响应时长И так далее, а суммарная оценка этих показателей и есть работоспособность системы. Но если вы вряд ли слышали такие технические термины, и не сталкивались с подобными высокопараллельными сценариями, то очень похоже на предмет 1 водительского удостоверения, но в дорогу не попасть. Нет такой технической сцены, чтобы вы могли тренироваться, чтобы вы могли постоянно испытывать темперамент системы, даже если у вас много идей, вы не можете их реализовать. Поэтому, если вы действительно хотите учиться, вы должны пойти на сцену с практическими действиями, а затем вы можете научиться делать дог-планку, попробовав это в воде.

你的视觉盲区有多大

Та же книга, та же дорога, тот же город, ты правда думаешь, что у жизни есть выбор? Иногда многие варианты — это просто украшения, и вы выбирали ровно столько раз, сколько вам дают. Дело не в том, выбираете вы или нет, а в том, что ваши когнитивные возможности определяют, что вы делаете в следующую секунду, а следующая секунда определяет следующую секунду. Так же, как леопард в трубке, 20% площади чёрное в твоём зрении, и даже невидимое ты игнорируешь, а невидимые 20% - это удача в жизни! Однако люди могут учиться, расти, изобретать себя заново, усердно работать и видеть оставшиеся 20% через повторные преобразования!

没有设计图纸你敢盖楼吗

Лучшее в разработке программирования — это дизайн. Используйте архитектурное мышление, опыт,才华灵感, чтобы построить лучшую систему. Настоящие R&D оценят код, написанный своими руками, как работу.Вы говорите, что это работа, а в глазах таких людей это не работа, а мастерство. Как, может быть, иногда вы будете делать это для себя из-заniubilityДизайн очень смелый, и он будет обновлен, чтобы иметь возможность запускать систему, способную выдерживать 2 миллиона посещений в секунду. Такая гордость подобна строительным кирпичикам, которые снова и снова поднимают подошвы ваших ног, постоянно улучшая ваше зрение, чтобы вы могли видеть дизайн верхнего уровня и знать конструкцию фундамента. Вы можете контролировать общую ситуацию и управлять деталями. Это накопление знаний поможет вам нарисовать план системной архитектуры.

2. Среда разработки

  1. JDK 1.8
  2. Idea + Maven
  3. Участвуют три проекта, которые можно получить, обратив внимание наНет публики:bugstack虫洞栈,Отвечать源码下载Получить (открыть ссылку для получения, найти серийный номер 18)
проект описывать
itstack-demo-design-13-00 Проект моделирования сценария; смоделируйте интерфейс для онлайн-утверждения процесса.
itstack-demo-design-13-01 Используйте фрагмент кода для реализации бизнес-требований
itstack-demo-design-13-02 Оптимизируйте и преобразовывайте код с помощью шаблонов проектирования, создавайте контраст и учитесь

3. Введение в модель цепочки ответственности

责任链模式,图片来自 refactoringguru.cn

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

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

4. Моделирование сценария

场景模拟;618大促场景上线审批场景

В этом случае мы моделируем сценарий процесса онлайн-утверждения бизнес-системы в течение периода продвижения 618.

Например, такие интернет-компании первой линии электронной коммерции, как Ali, JD.com, Pinduoduo и т. д., проведут некоторые оперативные мероприятия и обеспечат подготовку к расширению в период 618 года, точно так же, как красные конверты Baidu во время китайского Нового года. Однако все эти развитые системы нужно запускать одну за другой, потому что иногда бывают какие-то срочные корректировки, которые нужно запускать около 618, но для обеспечения стабильности онлайн-системы, онлайн-система будет сокращена аж на возможно, и одобрение будет соответственно усилено. Также как первичная реакция, вторичная реакция.

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

Затем мы моделируем такой сценарий бизнес-требований и используем шаблон проектирования цепочки ответственности для достижения этой функции.

1. Проект моделирования сценария

itstack-demo-design-13-00
└── src
    └── main
        └── java
            └── org.itstack.demo.design
                └── AuthService.java
  • Структура кода здесь относительно проста, всего один класс службы для имитации аудита и запроса результатов аудита. Это эквивалентно вызову этого класса для аудита проекта и получения структуры аудита.Эта часть информации о результатах моделируется и записывается в реализацию кэша.

2. Краткое описание сцены

2.1 Служба имитации аудита

public class AuthService {

    private static Map<String, Date> authMap = new ConcurrentHashMap<String, Date>();

    public static Date queryAuthInfo(String uId, String orderId) {
        return authMap.get(uId.concat(orderId));
    }

    public static void auth(String uId, String orderId) {
        authMap.put(uId.concat(orderId), new Date());
    }

}
  • Здесь предусмотрено два интерфейса, один из которых предназначен для запроса результатов аудита (queryAuthInfo), другой обрабатывает аудит (auth).
  • Эта часть предназначена для записи того, кто просматривал и просматривал один идентификатор в качестве уникального значения ключа в структуре карты памяти.

5. Реализуйте с помощью фрагмента кода

这里我们先使用最直接的方式来实现功能

В соответствии с нашим процессом утверждения требований, система обычно нуждается только в утверждении ответственного лица третьего уровня, но во время повышения 618 ответственное лицо второго уровня и ответственное лицо первого уровня должны присоединиться процесс утверждения системы онлайн вместе. Здесь мы используем очень прямой метод суждения, чтобы выполнить такое требование.

1. Инженерное сооружение

itstack-demo-design-13-01
└── src
    └── main
        └── java
            └── org.itstack.demo.design
                └── AuthController.java
  • Эта часть очень проста и содержит только класс управления аудитом, так же, как некоторые партнеры начинают писать код, один класс пишет все требования.

2. Реализация кода

public class AuthController {

    private SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");// 时间格式化

    public AuthInfo doAuth(String uId, String orderId, Date authDate) throws ParseException {

        // 三级审批
        Date date = AuthService.queryAuthInfo("1000013", orderId);
        if (null == date) return new AuthInfo("0001", "单号:", orderId, " 状态:待三级审批负责人 ", "王工");

        // 二级审批
        if (authDate.after(f.parse("2020-06-01 00:00:00")) && authDate.before(f.parse("2020-06-25 23:59:59"))) {
            date = AuthService.queryAuthInfo("1000012", orderId);
            if (null == date) return new AuthInfo("0001", "单号:", orderId, " 状态:待二级审批负责人 ", "张经理");
        }

        // 一级审批
        if (authDate.after(f.parse("2020-06-11 00:00:00")) && authDate.before(f.parse("2020-06-20 23:59:59"))) {
            date = AuthService.queryAuthInfo("1000011", orderId);
            if (null == date) return new AuthInfo("0001", "单号:", orderId, " 状态:待一级审批负责人 ", "段总");
        }

        return new AuthInfo("0001", "单号:", orderId, " 状态:审批完成");
    }

}
  • Здесь оценивается сверху вниз, что утверждение осуществляется разными людьми в течение указанного диапазона времени.Точно так же, как когда 618 подключается к сети, необходимо, чтобы все три ответственных лица утвердили его, прежде чем система сможет подключиться к сети.
  • Такая функция выглядит простой, но в реальном бизнесе есть много отделов, но если она реализована таким образом, ее трудно расширять, а также очень хлопотно изменять и расширять.

3. Тестовая проверка

3.1 Написание тестовых классов

@Test
public void test_AuthController() throws ParseException {
    AuthController authController = new AuthController();  

    // 模拟三级负责人审批
    logger.info("测试结果:{}", JSON.toJSONString(authController.doAuth("小傅哥", "1000998004813441", new Date())));
    logger.info("测试结果:{}", "模拟三级负责人审批,王工");
    AuthService.auth("1000013", "1000998004813441");  

    // 模拟二级负责人审批                                 
    logger.info("测试结果:{}", JSON.toJSONString(authController.doAuth("小傅哥", "1000998004813441", new Date())));
    logger.info("测试结果:{}", "模拟二级负责人审批,张经理");
    AuthService.auth("1000012", "1000998004813441");    

    // 模拟一级负责人审批
    logger.info("测试结果:{}", JSON.toJSONString(authController.doAuth("小傅哥", "1000998004813441", new Date())));
    logger.info("测试结果:{}", "模拟一级负责人审批,段总");
    AuthService.auth("1000011", "1000998004813441");            

    logger.info("测试结果:{}", "审批完成");
}
  • Это имитирует завершение утверждения каждого запроса.При утверждении разных узлов операция утверждения будет продолжать выполняться разными ответственными лицами.
  • authController.doAuth, это узел процесса для просмотра утверждения,AuthService.auth, — это метод утверждения, используемый для управления состоянием процесса узла.

3.2 Результаты испытаний

23:25:00.363 [main] INFO  org.itstack.demo.design.test.ApiTest - 测试结果:{"code":"0001","info":"单号:1000998004813441 状态:待三级审批负责人 王工"}
23:25:00.366 [main] INFO  org.itstack.demo.design.test.ApiTest - 测试结果:模拟三级负责人审批,王工
23:25:00.367 [main] INFO  org.itstack.demo.design.test.ApiTest - 测试结果:{"code":"0001","info":"单号:1000998004813441 状态:待二级审批负责人 张经理"}
23:25:00.367 [main] INFO  org.itstack.demo.design.test.ApiTest - 测试结果:模拟二级负责人审批,张经理
23:25:00.368 [main] INFO  org.itstack.demo.design.test.ApiTest - 测试结果:{"code":"0001","info":"单号:1000998004813441 状态:待一级审批负责人 段总"}
23:25:00.368 [main] INFO  org.itstack.demo.design.test.ApiTest - 测试结果:模拟一级负责人审批,段总
23:25:00.368 [main] INFO  org.itstack.demo.design.test.ApiTest - 测试结果:审批完成

Process finished with exit code 0
  • Из результатов тестирования видно, что слои утверждаются разными сотрудниками, и после завершения утверждения этим занимается следующий человек. Просто смотреть на результат — это удовлетворять нашим требованиям, но трудно расширять и настраивать процесс, который эквивалентен написанию кода.

6. Код рефакторинга модели цепочки ответственности

接下来使用装饰器模式来进行代码优化,也算是一次很小的重构。

Модель цепочки ответственности может сделать каждый сервисный модуль более понятным, и каждый модуль может пройтиnextспособ получения. и каждыйnextРеализуется унаследованным унифицированным абстрактным классом. В конце концов, обязанности всех классов могут быть динамически поставлены и использованы, а процесс хореографии можно сделать настраиваемым.

1. Инженерное сооружение

itstack-demo-design-13-02
└── src
    └── main
        └── java
            └── org.itstack.demo.design
                ├── impl
                │    ├── Level1AuthLink.java
                │    ├── Level2AuthLink.java
                │    └── Level3AuthLink.java
                ├── AuthInfo.java
                └── AuthLink.java

Структура модели шаблона цепочки ответственности

责任链模式模型结构

  • Приведенный выше рисунок является основной частью структуры цепочки ответственности в этой бизнес-модели, которая реализует единый абстрактный класс через триAuthLinkразличные правила, а затем выполнить договоренность об ответственности, чтобы имитировать ссылку. Это звено — цепочка ответственности в бизнесе.
  • Как правило, при использовании цепочки ответственности, если сцена относительно фиксирована, ее можно инициализировать, прописав ее в коде. Однако, если бизнес-сценарий часто меняется, его можно обработать с помощью конфигурации xml или инициализировать в библиотеке.

2. Реализация кода

2.1 Определение возвращаемых объектов в цепочке ответственности

public class AuthInfo {

    private String code;
    private String info = "";

    public AuthInfo(String code, String ...infos) {
        this.code = code;
        for (String str:infos){
            this.info = this.info.concat(str);
        }
    }
    
    // ...get/set
}
  • Этот класс является классом, который упаковывает результаты, возвращаемые в процессе цепочки ответственности, и обрабатывает возвращаемую информацию каждой цепочки ответственности.

2.2 Определение абстрактного класса ссылки

public abstract class AuthLink {

    protected Logger logger = LoggerFactory.getLogger(AuthLink.class);

    protected SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");// 时间格式化
    protected String levelUserId;                           // 级别人员ID
    protected String levelUserName;                         // 级别人员姓名
    private AuthLink next;                                  // 责任链

    public AuthLink(String levelUserId, String levelUserName) {
        this.levelUserId = levelUserId;
        this.levelUserName = levelUserName;
    }

    public AuthLink next() {
        return next;
    }

    public AuthLink appendNext(AuthLink next) {
        this.next = next;
        return this;
    }

    public abstract AuthInfo doAuth(String uId, String orderId, Date authDate);

}
  • Эта часть представляет собой цепочку ответственности,связатьосновная часть.AuthLink next, дело в том, чтоnextспособ получить узел, который необходимо обработать в следующей ссылке.
  • levelUserId,levelUserName, является общедоступной информацией в цепочке ответственности, маркирующей кадровую информацию каждого узла аудита.
  • Абстрактный метод определяется в абстрактном классе,abstract AuthInfo doAuthЭто класс, который необходимо реализовать, а разные уровни аудита имеют разные службы.

2.3 Три класса реализации аудита

Level1AuthLink

public class Level1AuthLink extends AuthLink {

    public Level1AuthLink(String levelUserId, String levelUserName) {
        super(levelUserId, levelUserName);
    }

    public AuthInfo doAuth(String uId, String orderId, Date authDate) {
        Date date = AuthService.queryAuthInfo(levelUserId, orderId);
        if (null == date) {
            return new AuthInfo("0001", "单号:", orderId, " 状态:待一级审批负责人 ", levelUserName);
        }
        AuthLink next = super.next();
        if (null == next) {
            return new AuthInfo("0000", "单号:", orderId, " 状态:一级审批完成负责人", " 时间:", f.format(date), " 审批人:", levelUserName);
        }

        return next.doAuth(uId, orderId, authDate);
    }

}

Level2AuthLink

public class Level2AuthLink extends AuthLink {

    private Date beginDate = f.parse("2020-06-11 00:00:00");
    private Date endDate = f.parse("2020-06-20 23:59:59");

    public Level2AuthLink(String levelUserId, String levelUserName) throws ParseException {
        super(levelUserId, levelUserName);
    }

    public AuthInfo doAuth(String uId, String orderId, Date authDate) {
        Date date = AuthService.queryAuthInfo(levelUserId, orderId);
        if (null == date) {
            return new AuthInfo("0001", "单号:", orderId, " 状态:待二级审批负责人 ", levelUserName);
        }
        AuthLink next = super.next();
        if (null == next) {
            return new AuthInfo("0000", "单号:", orderId, " 状态:二级审批完成负责人", " 时间:", f.format(date), " 审批人:", levelUserName);
        }

        if (authDate.before(beginDate) || authDate.after(endDate)) {
            return new AuthInfo("0000", "单号:", orderId, " 状态:二级审批完成负责人", " 时间:", f.format(date), " 审批人:", levelUserName);
        }

        return next.doAuth(uId, orderId, authDate);
    }

}

Level3AuthLink

public class Level3AuthLink extends AuthLink {

    private Date beginDate = f.parse("2020-06-01 00:00:00");
    private Date endDate = f.parse("2020-06-25 23:59:59");

    public Level3AuthLink(String levelUserId, String levelUserName) throws ParseException {
        super(levelUserId, levelUserName);
    }

    public AuthInfo doAuth(String uId, String orderId, Date authDate) {
        Date date = AuthService.queryAuthInfo(levelUserId, orderId);
        if (null == date) {
            return new AuthInfo("0001", "单号:", orderId, " 状态:待三级审批负责人 ", levelUserName);
        }
        AuthLink next = super.next();
        if (null == next) {
            return new AuthInfo("0000", "单号:", orderId, " 状态:三级审批负责人完成", " 时间:", f.format(date), " 审批人:", levelUserName);
        }

        if (authDate.before(beginDate) || authDate.after(endDate)) {
            return new AuthInfo("0000", "单号:", orderId, " 状态:三级审批负责人完成", " 时间:", f.format(date), " 审批人:", levelUserName);
        }

        return next.doAuth(uId, orderId, authDate);
    }

}
  • Вышеуказанные три категории;Level1AuthLink,Level2AuthLink,Level3AuthLink, который реализует простую логику обработки на разных уровнях аудита.
  • Например, в первом классе аудита он сначала определит, пройден ли аудит, и если он не пройден, результат будет возвращен вызывающей стороне для руководства аудитом.(Информация о времени не является пустой после простого обзора моделирования здесь, в качестве условия суждения)
  • После завершения оценки получается следующий узел аудита;super.next();, если следующего узла нет, вернуть результат напрямую.
  • После этого необходимо решить, необходимо ли это в соответствии с различными временными периодами бизнеса, и проводятся проверки второго и первого уровня.
  • Наконец, вернуть следующий результат аудита;next.doAuth(uId, orderId, authDate);, что-то вроде рекурсивного вызова.

3. Тестовая проверка

3.1 Написание тестовых классов

@Test
public void test_AuthLink() throws ParseException {
    AuthLink authLink = new Level3AuthLink("1000013", "王工")
            .appendNext(new Level2AuthLink("1000012", "张经理")
                    .appendNext(new Level1AuthLink("1000011", "段总")));

    logger.info("测试结果:{}", JSON.toJSONString(authLink.doAuth("小傅哥", "1000998004813441", new Date())));

    // 模拟三级负责人审批
    AuthService.auth("1000013", "1000998004813441");
    logger.info("测试结果:{}", "模拟三级负责人审批,王工");
    logger.info("测试结果:{}", JSON.toJSONString(authLink.doAuth("小傅哥", "1000998004813441", new Date())));

    // 模拟二级负责人审批
    AuthService.auth("1000012", "1000998004813441");
    logger.info("测试结果:{}", "模拟二级负责人审批,张经理");
    logger.info("测试结果:{}", JSON.toJSONString(authLink.doAuth("小傅哥", "1000998004813441", new Date())));

    // 模拟一级负责人审批
    AuthService.auth("1000011", "1000998004813441");
    logger.info("测试结果:{}", "模拟一级负责人审批,段总");
    logger.info("测试结果:{}", JSON.toJSONString(authLink.doAuth("小傅哥", "1000998004813441", new Date())));
}
  • Это включает в себя создание основной цепочки ответственности, которая будет интегрирована в контрольный уровень в реальном бизнесе;AuthLink authLink = new Level3AuthLink("1000013", "王工") .appendNext(new Level2AuthLink("1000012", "张经理") .appendNext(new Level1AuthLink("1000011", "段总")));Путем сборки различных узлов ответственности формируется полная цепочка бизнес-ответственности.
  • Далее продолжаем выполнять ссылку просмотра аудитаauthLink.doAuth(...), и данные будут проверяться руководителями уровней 3, 2 и 1 по возвращенным результатам до тех пор, пока не будет завершена окончательная проверка.

3.2 Результаты испытаний

23:49:46.585 [main] INFO  org.itstack.demo.design.test.ApiTest - 测试结果:{"code":"0001","info":"单号:1000998004813441 状态:待三级审批负责人 王工"}
23:49:46.590 [main] INFO  org.itstack.demo.design.test.ApiTest - 测试结果:模拟三级负责人审批,王工
23:49:46.590 [main] INFO  org.itstack.demo.design.test.ApiTest - 测试结果:{"code":"0001","info":"单号:1000998004813441 状态:待二级审批负责人 张经理"}
23:49:46.590 [main] INFO  org.itstack.demo.design.test.ApiTest - 测试结果:模拟二级负责人审批,张经理
23:49:46.590 [main] INFO  org.itstack.demo.design.test.ApiTest - 测试结果:{"code":"0001","info":"单号:1000998004813441 状态:待一级审批负责人 段总"}
23:49:46.590 [main] INFO  org.itstack.demo.design.test.ApiTest - 测试结果:模拟一级负责人审批,段总
23:49:46.590 [main] INFO  org.itstack.demo.design.test.ApiTest - 测试结果:{"code":"0000","info":"单号:1000998004813441 状态:一级审批完成负责人 时间:2020-06-18 23:49:46 审批人:段总"}

Process finished with exit code 0
  • Из приведенных выше результатов мы видим, что наша цепочка ответственности вступила в силу.Согласно структуре цепочки ответственности, мы будем утверждать ее слой за слоем до окончательного результата от конца утверждения до завершения первого уровень выводится.
  • Таким образом, метод проектирования цепочки ответственности может быть легко расширен и сохранен, а оператор if также устранен.

7. Резюме

  • Из приведенного выше рефакторинга кода от оператора if до разработки с использованием модели цепочки ответственности мы видим, что наша структура кода стала ясной и чистой, а также было решено использование большого количества операторов if. Дело не в том, что оператор if плохой, просто оператор if не подходит для проектирования системных процессов, но он все же очень полезен для обработки суждений и логики поведения.
  • Мы говорили о комбинированном паттерне в нашем предыдущем исследовании структурных паттернов, это похоже на комбинированное дерево, и мы строим процессное дерево решений. На самом деле, эту модель также можно комбинировать и расширять с моделью цепочки ответственности, и основное внимание в этой части уделяется тому, как связатьссылка на ассоциацию, окончательное выполнение находится в выполнении цепочки отношений в середине.
  • Модель цепочки ответственности очень хорошо обрабатывает единую ответственность и принцип открытости-закрытости, упрощает связывание и делает объектные отношения более понятными, а внешнему вызывающему объекту не нужно заботиться о том, как обрабатывается цепочка ответственности. выше, цепочка ответственности может быть объединена в пакет и предоставлена ​​для внешнего использования)*. Но в дополнение к этим преимуществам его также необходимо использовать в соответствующих сценариях, чтобы избежать проблем с производительностью и хаотичных упущений при отладке и тестировании.

8. Рекомендуемое чтение