LeeQingShui's Blog

  • 标签

  • 分类

  • 归档

  • 关于

Servlet 复习笔记

发表于 2019-04-06 | 更新于 2022-06-21 | 分类于 Java 基础
本文字数: 10k | 阅读时长 ≈ 15 分钟

什么是 Servlet?

  Servlet(Server Applet),全称 Java Servlet,未有中文译文。是用 Java 编写的服务器端程序。其主要功能在于交互式地浏览和修改数据,生成动态Web内容。狭义的 Servlet 是指 Java 语言实现的一个接口,广义的 Servlet 是指任何实现了这个 Servlet 接口的类),一般情况下,人们将 Servlet 理解为后者

  简单来讲,Servlet就是运行在服务端的小程序,用来将客户端发送过来的 HTTP 请求解析封装进相关的Servlet对象中,当开发者对该对象的数据进行处理后又封装到另一个Servlet对象中,转换成 HTTP 响应返回给客户端。

阅读全文 »

设计模式之模版方法(Template Method)模式

发表于 2019-03-29 | 更新于 2023-06-18 | 分类于 设计模式
本文字数: 3.1k | 阅读时长 ≈ 4 分钟

序言——无纸化系统实现办证流程

  为了适应新时代,国家大力推广,支持各大中小企业打造“无纸化系统”。

  企业使用“无纸化办公”系统,将许多传统工作从线下转为线上,以电子文件替代有纸化文件,不仅能降低企业成本,而且对使用者友好,对自然环境友好,一举多得!

  在以前,人们若想办理证件,大致需要经历以下流程:

  • ① 办证人去办事处(A 地点)领取纸质文件,填写对应内容
  • ② 办证人去对应审核中心(B 地点),提交填写完成的文件
  • ③ 办证人等待审核中心审核:
    • 审核中心审核不通过,从 ① 开始重新走流程
    • 审核中心审核通过,给文件盖章后发放给办证人,进入下一步骤
  • ④ 办证人去证件办理中心(C 地点),提交盖章完毕的文件,办证人等待证件办理完成
  • ⑤ 证件办理中心通知证件已办理完成,发短信告知办证人领取
  • ⑥ 办证人去证件办理中心领取证件

  在此流程下办证,人们不仅要在各个地方来回跑,十分花费时间,而且,若文件有一个地方填错了,还得重新走一遍流程。。。

  而现在,将办理证件的流程信息化,在对应电子化系统中进行操作,那么,一切都变得简单了:

  • ① 办证人在系统中,根据办理证件类型,下载所需填写的电子文件模版
  • ② 办证人根据模版将电子文件填写完成后,上传到办证系统中
  • ③ 办证人等待办证系统审核中心工作人员审核:
    • 审核中心审核不通过,从 ① 开始重新填写上传对应文件走流程
    • 审核中心审核通过,文件自动进行电子盖章,自动进入下一步骤
  • ④ 系统判断所有文件电子盖章无误,告知证件办理中心办理证件
  • ⑤ 证件办理中心通知证件已办理完成,发短信告知办证人领取
  • ⑥ 办证人去证件办理中心领取证件

  通过“无纸化办公”系统,人们只需要在网上动动小手,当流程办理完成后,直接去证件办理中心领取,只需要跑一次,大大节约了时间(另外如果系统支持快递送件,那都不用跑了)。

设计与实现

  对于上面谈论的电子化办证流程,其实是需要程序员根据标准化的文档流程进行抽象设计,最终进行代码实现的,针对这个过程,下面我们分析一下。

  首先,程序员需要开发一个审批流系统,按需求规则去定义一个审批流模版,针对办证的各个步骤,转换为审批流当中的一个个节点。

  另外,审批流系统需要对接其他第三方的系统,因为具体办证的业务是政务机构提供的,而审批流系统并不负责此业务。

  分析下来,那么代码实现上,主要时间花费在两块:

  • ① 审批流系统设计
  • ② 对接第三方系统

  本文不谈论第一点,因为市面上现有的审批流系统非常多,具体根据需要去调研实现即可,那么,第二点如何改如何考量呢?

  由于办证相关业务是由政务机构提供,对审批流系统开发人而言,必须按其他机构提供的接口文档进行对接工作,因为需要连调测试工作,是比较耗费时间的。

流程设计

  当然,我们在进行接口文档对接前,首先应该是梳理下大致的流程步骤,那么,我们画一个流程图出来吧:

办证流程图

  从流程图可知,对接第三方系统,大致需要以下接口:

  • 申请接口
  • 文件上传接口
  • 进度状态查询接口
  • 补充资料相关接口(二次申请接口)

代码实现探秘

  在审批流系统已完成,但是还未对接第三方系统的情况下,我们先思考一下:关于对接第三方系统的代码,应该写在哪里?
  直接嵌入到各个流程节点当中嘛?

  当然不是,我们应当是定义出一个单独的接口服务,提供给审批流调用。

  那么,我们应该会定义一个这样的接口,然后使用相应的实现类去实现它:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public interface IThirdCardService {

/**
* 申请接口
*
* @param applyParams
*/
void apply(Object applyParams);

/**
* 文件上传接口
*
* @param uploadParams
*/
void upload(Object uploadParams);

/**
* 状态查询接口
*
* @param queryStatusParams
*/
void queryStatus(Object queryStatusParams);

/**
* 二次申请接口
*
* @param reApplyParams
*/
void reApply(Object reApplyParams);

}

  我们这么做会有什么问题吗?

  现在来看,确实是没啥问题的,我们现在对接了 A 办证系统,而 A 办证系统支持办理 A1、A2、A3 等种类的证件。

  但是,客户之后反馈要办只有 B 办证系统支持的 B1、B2、B3 类证件,而 A 办证系统并不支持。

  产品经理说:这肯定不行呀,客户的需求我们得满足呀,开发老弟,麻烦再去接入 B 办证系统吧!

  开发同学:……

  世界上没有两片完全相同的树叶,对 A、B 办证系统同样如此,虽然两个系统大体办证流程差不多,但部分地方还是会存在差异,比如说:

  • ① 接口地址配置需要变化:A、B 办证系统的接口地址一定是不同的
  • ② 接口方法需要变化:B 系统特别增加了文件上传前校验的接口

  开发同学分析了下需求,既然这样,我再写一个原接口的实现类,然后实现一下,这不就是策略模式吗,简单呀!

  哎,这代码怎么看起来不对劲啊?哦,少了个校验接口,emmm,怎么办呢?

  我再去接口里面加个文件校验方法吧。

  嗯?怎么又报错了?哦哦,A 办证系统的实现类没有实现该方法,我再去 A 办证系统的实现类实现下。

  这代码看起来怎么怪怪的,而且,我发现有些代码重复了。

错误的实现

  从前面知道了,现在的代码是有一定问题的:

  • 无法应对变化:办证流程新增一个步骤就需要修改原先每个相关类的代码(接口及实现),不符合开闭原则
  • A 系统并不需要校验文件接口,但是由于接口定义的抽象方法,还是必须在其所有实现类中进行实现,哪怕用不到,与业务冲突

  显而易见,策略模式无法应对这种情况,策略模式关注的是相同行为的抽象,这种情况根本就不应该使用策略模式。

  不过没关系,我们可以使用另一种设计模式——模版方法模式。

优化后的代码

  现在,即使再新增一个 C 办证系统(提供 C1、C2 证件办理),我们也只需要去新增一个模版的实现类去继承模版类即可。

  通过继承,子类可以共用相同的代码,也可以各自实现不同的代码,还可以根据需要,去实现钩子函数(某些相同或不同的代码)。

简介

  模板方法模式是一种行为设计模式, 它在超类中定义了一个算法的框架,使得子类 可以在不改变一个算法的结构的情况下,重定义该算法的某些特定步骤。

优与劣

优点

  • 钩子函数可以应对变化的行为,子类按业务需要确定是否重写
  • 利用模板将不变的行为(或属性)抽离到父类中,提高了代码的复用性,符合开闭原则
  • 将不同的算法逻辑分离到不同的子类中,通过对子类的扩展增加新的行为,提高了代码的可扩展性

缺点

  • 钩子函数破坏了里氏代换原则
  • 由于继承的缺点,若在父类添加新的抽象方法,则所有子类都需再实现一遍
  • 每一个抽象类都需要一个子类实现,这将导致类数量增加,而类数量的增加,将间接地增加了系统实现的复杂度

业务场景

  • 一次性实现一个算法的不变的部分,并将可变的行为留给子类来实现
  • 各子类中公共的行为需要被提取集中到一个公共父类中,以避免代码重复
  • 子类需要应对扩展,可在模板父类中定义只在特定点调用的钩子函数, 以兼容特殊的业务

参考

  • 谭勇德. 设计模式就该这样学 [M]. 电子工业出版社,2020

文章信息

时间 说明
2019-03-29 初版
2022-07-19 完全重构

设计模式之策略(Strategy)模式

发表于 2019-03-25 | 更新于 2025-02-10 | 分类于 设计模式
本文字数: 3.2k | 阅读时长 ≈ 5 分钟

序言——缺陷的业务代码

  假如现在你需要做一个针对第三方接口的监控预警功能(配置的指标超出阈值进行短信邮件预警),需要计算应用集成的第三方接口的各种指标,比如:

  • 接口费用
  • 接口调用量
  • 接口请求成功率
  • 接口请求失败次数
  • 接口剩余使用天数
  • 接口响应时间超时次数

  由于这些指标分别存储在 MySQL、Redis、ElasticSearch,因此计算的逻辑并不相同,所以你可能会写出下面的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// 指标枚举
public enum IndexStatisticsTypeEnum {
FAILED_QUANTITY("失败次数"),
SUCCESS_RATE("成功率"),
RESPONSE_TIMEOUT_QUANTITY("接口响应时间超时次数"),
TIME_ON_TIME_FLUCTUATION_RATE("接口环比波动比率"),
INVOKE_QUANTITY("接口调用量"),
CHARGE_COST("接口费用"),
;

private final String desc;

IndexStatisticsTypeEnum(String desc) {
this.desc = desc;
}

public String getDesc() {
return desc;
}

public static boolean isExist(int ordinal) {
return Arrays.stream(IndexStatisticsTypeEnum.values()).anyMatch(x -> x.ordinal() == ordinal);
}

public static boolean isNotExist(int ordinal) {
return !isExist(ordinal);
}

public static IndexStatisticsTypeEnum getEnumByValue(int ordinal) {
return Arrays.stream(IndexStatisticsTypeEnum.values()).filter(x -> x.ordinal() == ordinal).findFirst().orElse(null);
}

public static String getRoutedDesc(int ordinal) {
IndexStatisticsTypeEnum em = Arrays.stream(IndexStatisticsTypeEnum.values()).filter(x -> x.ordinal() == ordinal).findFirst().orElse(null);
return em == null ? null : em.desc;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public getCalculateValue(...){
Double calculateValue = 0d;
Integer indexType = index.getType();
switch (IndexStatisticsTypeEnum.getEnumByValue(indexType) {
case FAILED_QUANTITY:
calculateValue = calculateValueRequestFailedQuantity(Object needParams);
break;
case SUCCESS_RATE:
calculateValue = calculateValueRequestSuccessRateIndexHandler(Object needParams);
break;
case RESPONSE_TIMEOUT_QUANTITY:
calculateValue = calculateValueRequestResponseTimeoutQuantityIndexHandler(Object needParams);
break;
case TIME_ON_TIME_FLUCTUATION_RATE:
calculateValue = calculateValueTimeOnTimeFluctuationRateIndexHandler(Object needParams);
break;
case COMPANY_EXPIRE:
calculateValue = calculateValueCompanyExpireIndexHandler(Object needParams);
break;
case INVOKE_QUANTITY:
calculateValue = calculateValueInvokeQuantityIndexHandler(Object needParams);
break;
case CHARGE_COST:
calculateValue = calculateValueChargeCost(Object needParams);
break;
default:
break;
}
return calculateValue;
}

  由于各个指标的计算方式并不相同,所以需要分别写代码进行计算,抽离出了各种方法。

  从代码设计可以发现:

  • 代码复杂性较高:switch (或 if-else) 的分支很多且业务代码很长,即使抽离为单独的方法,但一个类中还是存在了大量的代码
  • 设计上不符合开闭原则(对修改关闭,对扩展开放):未来或许要新增、删除或修改指标。若在一个函数中来回修改无疑是件恐怖的事情,你不知道产品和客户什么想法,可能今天要你删个指标,明天又要去你加回来

  为了降低代码复杂性,使设计解耦,让程序看起来简洁且能应对未来的变化,可以使用策略模式来优化原有代码,最终改造后代码可以变成这样:

1
2
3
4
5
6
7
// 优化后的代码
public getCalculateValue(...){
Integer indexType = index.getType();
IndexStatisticsStrategy routedStrategy = indexStatisticsStrategyContext.getRoutedStrategy(indexType);
Double calculateValue = routedStrategy.calculate(indexType);
return calculateValue;
}

  改造后的代码看起来是不是非常简洁?

  那么,策略模式是什么,又如何使用呢?

  下面跟随本文来了解下吧!

阅读全文 »

设计模式之单例(Single)模式

发表于 2019-03-23 | 更新于 2025-01-17 | 分类于 设计模式
本文字数: 3.2k | 阅读时长 ≈ 5 分钟

序言

  在程序中,有一些对象其实我们只需要一个,比如说:线程池、缓存、处理偏好设置的对象等等。
  事实上,这类对象应该只创建一个实例,若制造出多个实例,就会导致许多问题发生,如程序行为异常,资源使用过量或返回的结果不一致。
  利用静态类变量、静态方法和适当的修饰符,不是也可以达到这种效果嘛?
  确实如此,但其有个缺点,那就是必须在程序一开始就创建好对象,万一这个对象非常耗费资源,而程序又在这次的执行过程中一直没用到它,就会浪费资源。
  虽然鱼和熊掌不可兼得,但我们的单例模式却可以分别实现“鱼”或“熊掌”。

阅读全文 »

(五)Spring MVC

发表于 2019-03-22 | 更新于 2023-07-15 | 分类于 Java Web 框架
本文字数: 21k | 阅读时长 ≈ 29 分钟

简介

  Spring Web MVC 是基于 Servlet API 构建的原始 Web 框架,从一开始就包含在Spring Framework中。正式名称“Spring Web MVC”,来自其源模块(spring-webmvc)的名称,但它通常被称为“Spring MVC”。

阅读全文 »

Java 之 JDK 8 新特性

发表于 2019-03-19 | 更新于 2023-06-19 | 分类于 Java 基础
本文字数: 8.6k | 阅读时长 ≈ 12 分钟

  在 JDK8 中,新增了许多新特性,可以让开发人员更优雅的编写代码,那么下面跟随本文来了解一下吧!

阅读全文 »

Java 之 JDBC、数据库连接池及 DBUtils 工具

发表于 2019-03-16 | 更新于 2024-04-06 | 分类于 Java 基础
本文字数: 8.4k | 阅读时长 ≈ 12 分钟

  此文待重构。

JDBC

什么是 JDBC ?

  JDBC(Java Data Base Connectivity,java数据库连接)是一种用于执行 SQL 语句的 Java API(Application Programming Interface),可以为多种关系数据库提供统一访问,它由一组用 Java 语言编写的类和接口组成。JDBC 提供了一种基准,据此可以构建更高级的工具和接口,使数据库开发人员能够编写数据库应用程序,是连接数据库和Java应用程序的纽带。

阅读全文 »

(四)Spring 事务管理

发表于 2019-03-16 | 更新于 2023-07-26 | 分类于 Java Web 框架
本文字数: 13k | 阅读时长 ≈ 19 分钟

前言

  在实际开发中,操作数据库时都会涉及到事务管理问题,为此 Spring 提供了专门用于事务处理的API。
  Spring 的事务管理通过 AOP 的思想简化了传统的事务管理流程,并在一定程度上减少了开发者的工作量。
  在 Spring 中,提供事务管理的jar包为spring-tx。

阅读全文 »

Java 反射

发表于 2019-03-14 | 更新于 2022-06-21 | 分类于 Java 基础
本文字数: 6.4k | 阅读时长 ≈ 9 分钟

序言

  人类总是趋利避害的,我们会对微笑的人产生亲切感,对抱怨的人产生厌恶感。
  既然人类存在分辨思维,可以对不同的行为作出不同的反应,那么思考一个问题——程序具有思考决策的能力吗?
  世上唯一不变的就是变化,只有变化才能进化,进化才能适应未来,既然如此——程序应当具有思考决策能力。
  那么,作为 Java 程序,又是通过什么机制来实现这种能力的呢?
  反射而已。

什么是反射?

  在计算机科学中,反射是指计算机程序在运行时(Run time)可以访问、检测和修改它本身状态或行为的一种能力。用比喻来说,反射就是程序在运行的时候能够“观察”并且修改自己的行为。

  要注意术语“反射”和“内省)”(type introspection)的关系。内省(或称“自省”)机制仅指程序在运行时对自身信息(称为元数据)的检测;反射机制不仅包括要能在运行时对程序自身信息进行检测,还要求程序能进一步根据这些信息改变程序状态或结构。

阅读全文 »

Java 网络编程

发表于 2019-03-13 | 更新于 2023-08-19 | 分类于 Java 基础
本文字数: 6.6k | 阅读时长 ≈ 9 分钟
  此文待重构

  在使用 Java 进行网络通信之前,我们需要先了解一些网络的概念。

阅读全文 »

1…91011…15
LeeQingShui

LeeQingShui

149 日志
16 分类
69 标签
RSS
© 2018 – 2026 LeeQingShui | 站点总字数: 899k
赣 ICP 备 2022002212 号
本站已运行
本站总访问量 次 | 本站访客 人次
0%