Relearning Design Pattern

重学设计模式

单一职责原则

又称单一功能原则,面向对象五个基本原则之一

  • 原则定义:一个类应该只有一个发生变化的原因
  • 模拟场景:访客用户,普通用户,VIP用户
  • 编码实现:if-else,判断实现,不易维护
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* 视频用户服务
*/
public class VideoUserService {

public void serveGrade(String userType){
if ("VIP用户".equals(userType)){
System.out.println("VIP用户,视频1080P蓝光");
} else if ("普通用户".equals(userType)){
System.out.println("普通用户,视频720P超清");
} else if ("访客用户".equals(userType)){
System.out.println("访客用户,视频480P高清");
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
/**
* 测试验证
*/
public class ApiTest {

public static void main(String[] args) {
VideoUserService service = new VideoUserService();
service.serveGrade("VIP用户");
service.serveGrade("普通用户");
service.serveGrade("访客用户");
}
}

代码重构

1
2
3
4
5
6
7
8
9
10
/**
* 视频用户服务接口
*/
public interface IVideoUserService {

void definition();

void advertisement();

}

impl包下拆分成三个互不影响的类

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* 访客用户
*/
public class GuestVideoUserService implements IVideoUserService {

public void definition() {
System.out.println("访客用户,视频480P高清");
}

public void advertisement() {
System.out.println("访客用户,视频有广告");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* 普通用户
*/
public class OrdinaryVideoUserService implements IVideoUserService {

public void definition() {
System.out.println("普通用户,视频720P超清");
}

public void advertisement() {
System.out.println("普通用户,视频有广告");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* VIP 用户
*/
public class VipVideoUserService implements IVideoUserService {

public void definition() {
System.out.println("VIP用户,视频1080P蓝光");
}

public void advertisement() {
System.out.println("VIP会员,视频无广告");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* 测试验证
*/
public class ApiTest {

public static void main(String[] args) {
// 访客用户
IVideoUserService guest = new GuestVideoUserService();
guest.advertisement();
guest.definition();
// 普通用户
IVideoUserService ordinary = new OrdinaryVideoUserService();
ordinary.advertisement();
ordinary.definition();
// VIP用户
IVideoUserService vip = new VipVideoUserService();
vip.advertisement();
vip.definition();
}
}

开闭原则

软件中的对象(类,模块,函数等)应该对于扩展时开放的,但是对于修改是封闭的

  • 开闭原则:扩展开放,修改封闭
  • 面积计算:长方形,三角形,圆形
  • 编码实现:破坏方法,继承扩展
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
/**
* 面积计算接口
*/
public interface ICalculationArea {

/**
* 计算面积,长方形
*
* @param x 长
* @param y 宽
* @return 面积
*/
double rectangle(double x, double y);

/**
* 计算面积,三角形
* @param x 边长x
* @param y 边长y
* @param z 边长z
* @return 面积
*
* 海伦公式:S=√[p(p-a)(p-b)(p-c)] 其中:p=(a+b+c)/2
*/
double triangle(double x, double y, double z);

/**
* 计算面积,圆形
* @param r 半径
* @return 面积
*
* 圆面积公式:S=πr²
*/
double circular(double r);

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* 面积计算实现
*/
public class CalculationArea implements ICalculationArea {

private final static double π = 3.14D;

public double rectangle(double x, double y) {
return x * y;
}

public double triangle(double x, double y, double z) {
double p = (x + y + z) / 2;
return Math.sqrt(p * (p - x) * (p - y) * (p - z));
}

public double circular(double r) {
return π * r * r;
}
}

开闭原则扩展功能

1
2
3
4
5
6
7
8
9
10
11
12
/**
* 扩展继承,实现自己的需求
*/
public class CalculationAreaExt extends CalculationArea {

private final static double π = 3.141592653D;

@Override
public double circular(double r) {
return π * r * r;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
/**
* 功能测试
*/
public class ApiTest {

@Test
public void test_CalculationAreaExt(){
ICalculationArea area = new CalculationAreaExt();
double circular = area.circular(10);
System.out.println(circular);
}
}

里氏替换原则

继承必须确保超类所拥有的性质在子类中依然成立

  • 里氏替换原则:兼容性,维护性,扩展性
  • 银行卡:信用卡,储蓄卡,地铁卡
  • 编码实现:银行卡类,正向,逆向
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
38
39
40
41
42
43
44
45
46
/**
* 模拟储蓄卡功能
*/
public class CashCard {

private Logger logger = LoggerFactory.getLogger(CashCard.class);

/**
* 提现
*
* @param orderId 单号
* @param amount 金额
* @return 状态码 0000成功、0001失败、0002重复
*/
public String withdrawal(String orderId, BigDecimal amount) {
// 模拟支付成功
logger.info("提现成功,单号:{} 金额:{}", orderId, amount);
return "0000";
}

/**
* 储蓄
*
* @param orderId 单号
* @param amount 金额
*/
public String recharge(String orderId, BigDecimal amount) {
// 模拟充值成功
logger.info("储蓄成功,单号:{} 金额:{}", orderId, amount);
return "0000";
}

/**
* 交易流水查询
* @return 交易流水
*/
public List<String> tradeFlow() {
logger.info("交易流水查询成功");
List<String> tradeList = new ArrayList<String>();
tradeList.add("100001,100.00");
tradeList.add("100001,80.00");
tradeList.add("100001,76.50");
tradeList.add("100001,126.00");
return tradeList;
}
}
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
/**
* 模拟信用卡功能
*/
public class CreditCard extends CashCard {

private Logger logger = LoggerFactory.getLogger(CashCard.class);

@Override
public String withdrawal(String orderId, BigDecimal amount) {
// 校验
if (amount.compareTo(new BigDecimal(1000)) >= 0){
logger.info("贷款金额校验(限额1000元),单号:{} 金额:{}", orderId, amount);
return "0001";
}
// 模拟生成贷款单
logger.info("生成贷款单,单号:{} 金额:{}", orderId, amount);
// 模拟支付成功
logger.info("贷款成功,单号:{} 金额:{}", orderId, amount);
return "0000";
}

@Override
public String recharge(String orderId, BigDecimal amount) {
// 模拟生成还款单
logger.info("生成还款单,单号:{} 金额:{}", orderId, amount);
// 模拟还款成功
logger.info("还款成功,单号:{} 金额:{}", orderId, amount);
return "0000";
}

@Override
public List<String> tradeFlow() {
return super.tradeFlow();
}
}
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 class ApiTest {

private Logger logger = LoggerFactory.getLogger(ApiTest.class);

@Test
public void test_CashCard() {
CashCard cashCard = new CashCard();
// 提现
cashCard.withdrawal("100001", new BigDecimal(100));
// 储蓄
cashCard.recharge("100001", new BigDecimal(100));
// 交易流水
List<String> tradeFlow = cashCard.tradeFlow();
logger.info("查询交易流水,{}", JSON.toJSONString(tradeFlow));
}

@Test
public void test_CreditCard() {
CreditCard creditCard = new CreditCard();
// 支付
creditCard.withdrawal("100001", new BigDecimal(100));
// 还款
creditCard.recharge("100001", new BigDecimal(100));
// 交易流水
List<String> tradeFlow = creditCard.tradeFlow();
logger.info("查询交易流水,{}", JSON.toJSONString(tradeFlow));
}
}

抽象提取相同功能

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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
/**
* 银行卡
*/
public abstract class BankCard {

private Logger logger = LoggerFactory.getLogger(BankCard.class);

private String cardNo; // 卡号
private String cardDate; // 开卡时间

public BankCard(String cardNo, String cardDate) {
this.cardNo = cardNo;
this.cardDate = cardDate;
}

abstract boolean rule(BigDecimal amount);

// 正向入账,+ 钱
public String positive(String orderId, BigDecimal amount) {
// 入款成功,存款、还款
logger.info("卡号{} 入款成功,单号:{} 金额:{}", cardNo, orderId, amount);
return "0000";
}

// 逆向入账,- 钱
public String negative(String orderId, BigDecimal amount) {
// 入款成功,存款、还款
logger.info("卡号{} 出款成功,单号:{} 金额:{}", cardNo, orderId, amount);
return "0000";
}

/**
* 交易流水查询
*
* @return 交易流水
*/
public List<String> tradeFlow() {
logger.info("交易流水查询成功");
List<String> tradeList = new ArrayList<String>();
tradeList.add("100001,100.00");
tradeList.add("100001,80.00");
tradeList.add("100001,76.50");
tradeList.add("100001,126.00");
return tradeList;
}

public String getCardNo() {
return cardNo;
}

public String getCardDate() {
return cardDate;
}
}
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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
/**
* 模拟储蓄卡功能
*/
public class CashCard extends BankCard {

private Logger logger = LoggerFactory.getLogger(CashCard.class);

public CashCard(String cardNo, String cardDate) {
super(cardNo, cardDate);
}

boolean rule(BigDecimal amount) {
return true;
}

/**
* 提现
*
* @param orderId 单号
* @param amount 金额
* @return 状态码 0000成功、0001失败、0002重复
*/
public String withdrawal(String orderId, BigDecimal amount) {
// 模拟支付成功
logger.info("提现成功,单号:{} 金额:{}", orderId, amount);
return super.negative(orderId, amount);
}

/**
* 储蓄
*
* @param orderId 单号
* @param amount 金额
*/
public String recharge(String orderId, BigDecimal amount) {
// 模拟充值成功
logger.info("储蓄成功,单号:{} 金额:{}", orderId, amount);
return super.positive(orderId, amount);
}

/**
* 风险校验
*
* @param cardNo 卡号
* @param orderId 单号
* @param amount 金额
* @return 状态
*/
public boolean checkRisk(String cardNo, String orderId, BigDecimal amount) {
// 模拟风控校验
logger.info("风控校验,卡号:{} 单号:{} 金额:{}", cardNo, orderId, amount);
return true;
}
}
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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
/**
* 信用卡
*/
public class CreditCard extends CashCard {

private Logger logger = LoggerFactory.getLogger(CreditCard.class);

public CreditCard(String cardNo, String cardDate) {
super(cardNo, cardDate);
}

boolean rule2(BigDecimal amount) {
return amount.compareTo(new BigDecimal(1000)) <= 0;
}

/**
* 提现,信用卡贷款
*
* @param orderId 单号
* @param amount 金额
* @return 状态码
*/
public String loan(String orderId, BigDecimal amount) {
boolean rule = rule2(amount);
if (!rule) {
logger.info("生成贷款单失败,金额超限。单号:{} 金额:{}", orderId, amount);
return "0001";
}
// 模拟生成贷款单
logger.info("生成贷款单,单号:{} 金额:{}", orderId, amount);
// 模拟支付成功
logger.info("贷款成功,单号:{} 金额:{}", orderId, amount);
return super.negative(orderId, amount);

}

/**
* 还款,信用卡还款
*
* @param orderId 单号
* @param amount 金额
* @return 状态码
*/
public String repayment(String orderId, BigDecimal amount) {
// 模拟生成还款单
logger.info("生成还款单,单号:{} 金额:{}", orderId, amount);
// 模拟还款成功
logger.info("还款成功,单号:{} 金额:{}", orderId, amount);
return super.positive(orderId, amount);
}
}
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
/**
* 测试
*/
public class ApiTest {

private Logger logger = LoggerFactory.getLogger(ApiTest.class);

@Test
public void test_bankCard() {
logger.info("里氏替换前,CashCard类:");
CashCard bankCard = new CashCard("6214567800989876", "2022-03-05");
// 提现
bankCard.withdrawal("100001", new BigDecimal(100));
// 储蓄
bankCard.recharge("100001", new BigDecimal(100));

logger.info("里氏替换后,CreditCard类:");
CashCard creditCard = new CreditCard("6214567800989876", "2022-03-05");
// 提现
creditCard.withdrawal("100001", new BigDecimal(1000000));
// 储蓄
creditCard.recharge("100001", new BigDecimal(100));
}

@Test
public void test_CreditCard(){
CreditCard creditCard = new CreditCard("6214567800989876", "2022-03-05");
// 支付,贷款
creditCard.loan("100001", new BigDecimal(100));
// 还款
creditCard.repayment("100001", new BigDecimal(100));
}
}

迪米特法则

意义在于降低类之间的耦合。由于每个对象尽量减少对其他对象的了解,因此,很容易使得系统的功能模块功能独立,相互之间不存在(或很少有)依赖关系

  • 迪米特法则:最少知道,减少依赖
  • 学生成绩&排名:校长,教师,学生,成绩,排名
  • 编码实现:高内聚,低耦合
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
38
39
40
41
42
43
44
45
46
47
/**
* 校长
*/
public class Principal {

private Teacher teacher = new Teacher("丽华", "3年1班");

// 查询班级信息,总分数、学生人数、平均值
public Map<String, Object> queryClazzInfo(String clazzId) {
// 获取班级信息;学生总人数、总分、平均分
int stuCount = clazzStudentCount();
double totalScore = clazzTotalScore();
double averageScore = clazzAverageScore();

// 组装对象,实际业务开发会有对应的类
Map<String, Object> mapObj = new HashMap<>();
mapObj.put("班级", teacher.getClazz());
mapObj.put("老师", teacher.getName());
mapObj.put("学生人数", stuCount);
mapObj.put("班级总分数", totalScore);
mapObj.put("班级平均分", averageScore);
return mapObj;
}

// 总分
public double clazzTotalScore() {
double totalScore = 0;
for (Student stu : Teacher.getStudentList()) {
totalScore += stu.getGrade();
}
return totalScore;
}

// 平均分
public double clazzAverageScore(){
double totalScore = 0;
for (Student stu : Teacher.getStudentList()) {
totalScore += stu.getGrade();
}
return totalScore / Teacher.getStudentList().size();
}

// 班级人数
public int clazzStudentCount(){
return Teacher.getStudentList().size();
}
}
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
38
39
40
41
42
/**
* 学生
*/
public class Student {

private String name; // 学生姓名
private int rank; // 考试排名(总排名)
private double grade; // 考试分数(总分)

public Student() {
}

public Student(String name, int rank, double grade) {
this.name = name;
this.rank = rank;
this.grade = grade;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getRank() {
return rank;
}

public void setRank(int rank) {
this.rank = rank;
}

public double getGrade() {
return grade;
}

public void setGrade(double grade) {
this.grade = grade;
}
}
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
38
/**
* 老师
*/
public class Teacher {

private String name; // 老师名称
private String clazz; // 班级
private static List<Student> studentList; // 学生

public Teacher() {
}

public Teacher(String name, String clazz) {
this.name = name;
this.clazz = clazz;
}

static {
studentList = new ArrayList<>();
studentList.add(new Student("花花", 10, 589));
studentList.add(new Student("豆豆", 54, 356));
studentList.add(new Student("秋雅", 23, 439));
studentList.add(new Student("皮皮", 2, 665));
studentList.add(new Student("蛋蛋", 19, 502));
}

public static List<Student> getStudentList() {
return studentList;
}

public String getName() {
return name;
}

public String getClazz() {
return clazz;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* 测试
*/
public class ApiTest {

private Logger logger = LoggerFactory.getLogger(ApiTest.class);

@Test
public void test_Principal() {
Principal principal = new Principal();
Map<String, Object> map = principal.queryClazzInfo("3年1班");
logger.info("查询结果:{}", JSON.toJSONString(map));
}

}

重构,越少人知道越好

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
38
39
40
41
42
/**
* 学生
*/
public class Student {

private String name; // 学生姓名
private int rank; // 考试排名(总排名)
private double grade; // 考试分数(总分)

public Student() {
}

public Student(String name, int rank, double grade) {
this.name = name;
this.rank = rank;
this.grade = grade;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getRank() {
return rank;
}

public void setRank(int rank) {
this.rank = rank;
}

public double getGrade() {
return grade;
}

public void setGrade(double grade) {
this.grade = grade;
}
}
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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
/**
* 老师
*/
public class Teacher {

private String name; // 老师名称
private String clazz; // 班级
private static List<Student> studentList; // 学生

public Teacher() {
}

public Teacher(String name, String clazz) {
this.name = name;
this.clazz = clazz;
}

static {
studentList = new ArrayList<>();
studentList.add(new Student("花花", 10, 589));
studentList.add(new Student("豆豆", 54, 356));
studentList.add(new Student("秋雅", 23, 439));
studentList.add(new Student("皮皮", 2, 665));
studentList.add(new Student("蛋蛋", 19, 502));
}

// 总分
public double clazzTotalScore() {
double totalScore = 0;
for (Student stu : studentList) {
totalScore += stu.getGrade();
}
return totalScore;
}

// 平均分
public double clazzAverageScore(){
double totalScore = 0;
for (Student stu : studentList) {
totalScore += stu.getGrade();
}
return totalScore / studentList.size();
}

// 班级人数
public int clazzStudentCount(){
return studentList.size();
}

public static List<Student> getStudentList() {
return studentList;
}

public String getName() {
return name;
}

public String getClazz() {
return clazz;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
* 校长
*/
public class Principal {

private Teacher teacher = new Teacher("丽华", "3年1班");

// 查询班级信息,总分数、学生人数、平均值
public Map<String, Object> queryClazzInfo(String clazzId) {
// 获取班级信息;学生总人数、总分、平均分
int stuCount = teacher.clazzStudentCount();
double totalScore = teacher.clazzTotalScore();
double averageScore = teacher.clazzAverageScore();

// 组装对象,实际业务开发会有对应的类
Map<String, Object> mapObj = new HashMap<>();
mapObj.put("班级", teacher.getClazz());
mapObj.put("老师", teacher.getName());
mapObj.put("学生人数", stuCount);
mapObj.put("班级总分数", totalScore);
mapObj.put("班级平均分", averageScore);
return mapObj;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* 测试
*/
public class ApiTest {

private Logger logger = LoggerFactory.getLogger(ApiTest.class);

@Test
public void test_Principal() {
Principal principal = new Principal();
Map<String, Object> map = principal.queryClazzInfo("3年1班");
logger.info("查询结果:{}", JSON.toJSONString(map));
}
}

接口隔离原则

要求程序员尽量将臃肿庞大的接口拆分为更小的和更具体的接口,让接口中只包含客户感兴趣的方法

  • 接口隔离原则:更下的接口,更具体的接口
  • 王者荣耀:射箭,隐袭,沉默,眩晕
  • 编码实现:高内聚,低耦合
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* 王者英雄,英雄技能接口
*/
public interface ISkill {

// 射箭
void doArchery();

// 隐袭
void doInvisible();

// 沉默
void doSilent();

// 眩晕
void doVertigo();

}
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
/**
* 王者英雄,廉颇
*/
public class HeroLianPo implements ISkill{

@Override
public void doArchery() {
// 无此技能的实现
}

@Override
public void doInvisible() {
System.out.println("廉颇的隐身技能");
}

@Override
public void doSilent() {
System.out.println("廉颇的沉默技能");
}

@Override
public void doVertigo() {
System.out.println("廉颇的眩晕技能");
}
}
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
/**
* 王者英雄,后裔
*/
public class HeroHouYi implements ISkill{

@Override
public void doArchery() {
System.out.println("后裔的灼日之矢");
}

@Override
public void doInvisible() {
System.out.println("后裔的隐身技能");
}

@Override
public void doSilent() {
System.out.println("后裔的沉默技能");
}

@Override
public void doVertigo() {
// 无此技能的实现
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* 单元测试
*/
public class ApiTest {

@Test
public void test_ISkill(){
// 后裔
HeroHouYi heroHouYi = new HeroHouYi();
heroHouYi.doArchery();

// 廉颇
HeroLianPo heroLianPo = new HeroLianPo();
heroLianPo.doInvisible();
}
}

接口单独抽取

1
2
3
4
5
6
7
8
9
/**
* 射箭接口
*/
public interface ISkillArchery {

// 射箭
void doArchery();

}
1
2
3
4
5
6
7
8
9
/**
* 隐袭
*/
public interface ISkillInvisible {

// 隐袭
void doInvisible();

}
1
2
3
4
5
6
7
8
9
/**
* 沉默
*/
public interface ISkillSilent {

// 沉默
void doSilent();

}
1
2
3
4
5
6
7
8
9
/**
* 眩晕
*/
public interface ISkillVertigo {

// 眩晕
void doVertigo();

}

实现各个接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* 王者英雄,后裔
*/
public class HeroHouYi implements ISkillArchery, ISkillInvisible, ISkillSilent {

@Override
public void doArchery() {
System.out.println("后裔的灼日之矢");
}

@Override
public void doInvisible() {
System.out.println("后裔的隐身技能");
}

@Override
public void doSilent() {
System.out.println("后裔的沉默技能");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* 王者英雄,廉颇
*/
public class HeroLianPo implements ISkillInvisible, ISkillSilent, ISkillVertigo {

@Override
public void doInvisible() {
System.out.println("廉颇的隐身技能");
}

@Override
public void doSilent() {
System.out.println("廉颇的沉默技能");
}

@Override
public void doVertigo() {
System.out.println("廉颇的眩晕技能");
}
}

依赖倒置原则

程序要依赖于抽象接口,不要依赖于具体实现。简单的说就是要求对抽象进行编程,不要对实现进行编程,这样就降低了客户与实现模块间的耦合

  • 依赖倒置原则:依赖接口,降低耦合
  • 用户抽奖:随机抽奖,权重抽奖
  • 编码实现:高内聚,低耦合
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
/**
* 投注用户
*/
public class BetUser {

private String userName; // 用户姓名
private int userWeight; // 用户权重

public BetUser() {
}

public BetUser(String userName, int userWeight) {
this.userName = userName;
this.userWeight = userWeight;
}

public String getUserName() {
return userName;
}

public void setUserName(String userName) {
this.userName = userName;
}

public int getUserWeight() {
return userWeight;
}

public void setUserWeight(int userWeight) {
this.userWeight = userWeight;
}
}
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
/**
* 抽奖控制
*/
public class DrawControl {

// 随机抽取指定数量的用户,作为中奖用户
public List<BetUser> doDrawRandom(List<BetUser> list, int count) {
// 集合数量很小直接返回
if (list.size() <= count) return list;
// 乱序集合
Collections.shuffle(list);
// 取出指定数量的中奖用户
List<BetUser> prizeList = new ArrayList<>(count);
for (int i = 0; i < count; i++) {
prizeList.add(list.get(i));
}
return prizeList;
}

// 权重排名获取指定数量的用户,作为中奖用户
public List<BetUser> doDrawWeight(List<BetUser> list, int count) {
// 按照权重排序
list.sort((o1, o2) -> {
int e = o2.getUserWeight() - o1.getUserWeight();
if (0 == e) return 0;
return e > 0 ? 1 : -1;
});
// 取出指定数量的中奖用户
List<BetUser> prizeList = new ArrayList<>(count);
for (int i = 0; i < count; i++) {
prizeList.add(list.get(i));
}
return prizeList;
}
}
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
/**
* 单元测试
*/
public class ApiTest {

private Logger logger = LoggerFactory.getLogger(ApiTest.class);

@Test
public void test_DrawControl(){
List<BetUser> betUserList = new ArrayList<>();
betUserList.add(new BetUser("花花", 65));
betUserList.add(new BetUser("豆豆", 43));
betUserList.add(new BetUser("小白", 72));
betUserList.add(new BetUser("笨笨", 89));
betUserList.add(new BetUser("丑蛋", 10));

DrawControl drawControl = new DrawControl();
List<BetUser> prizeRandomUserList = drawControl.doDrawRandom(betUserList, 3);
logger.info("随机抽奖,中奖用户名单:{}", JSON.toJSON(prizeRandomUserList));

List<BetUser> prizeWeightUserList = drawControl.doDrawWeight(betUserList, 3);
logger.info("权重抽奖,中奖用户名单:{}", JSON.toJSON(prizeWeightUserList));
}

}

代码重构

1
2
3
4
5
6
7
8
/**
* 抽奖接口
*/
public interface IDraw {

List<BetUser> prize(List<BetUser> list, int count);

}
1
2
3
4
5
6
7
8
9
10
11
12
/**
* 抽奖控制
*/
public class DrawControl {

private IDraw draw;

public List<BetUser> doDraw(IDraw draw, List<BetUser> betUserList, int count) {
return draw.prize(betUserList, count);
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* 随机抽奖
*/
public class DrawRandom implements IDraw {

@Override
public List<BetUser> prize(List<BetUser> list, int count) {
// 集合数量很小直接返回
if (list.size() <= count) return list;
// 乱序集合
Collections.shuffle(list);
// 取出指定数量的中奖用户
List<BetUser> prizeList = new ArrayList<>(count);
for (int i = 0; i < count; i++) {
prizeList.add(list.get(i));
}
return prizeList;
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* 权重抽奖
*/
public class DrawWeightRank implements IDraw {

@Override
public List<BetUser> prize(List<BetUser> list, int count) {
// 按照权重排序
list.sort((o1, o2) -> {
int e = o2.getUserWeight() - o1.getUserWeight();
if (0 == e) return 0;
return e > 0 ? 1 : -1;
});
// 取出指定数量的中奖用户
List<BetUser> prizeList = new ArrayList<>(count);
for (int i = 0; i < count; i++) {
prizeList.add(list.get(i));
}
return prizeList;
}
}
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
/**
* 单元测试
*/
public class ApiTest {

private Logger logger = LoggerFactory.getLogger(ApiTest.class);

@Test
public void test_DrawControl() {
List<BetUser> betUserList = new ArrayList<>();
betUserList.add(new BetUser("花花", 65));
betUserList.add(new BetUser("豆豆", 43));
betUserList.add(new BetUser("小白", 72));
betUserList.add(new BetUser("笨笨", 89));
betUserList.add(new BetUser("丑蛋", 10));

DrawControl drawControl = new DrawControl();
List<BetUser> prizeRandomUserList = drawControl.doDraw(new DrawRandom(), betUserList, 3);
logger.info("随机抽奖,中奖用户名单:{}", JSON.toJSON(prizeRandomUserList));

List<BetUser> prizeWeightUserList = drawControl.doDraw(new DrawWeightRank(), betUserList, 3);
logger.info("权重抽奖,中奖用户名单:{}", JSON.toJSON(prizeWeightUserList));
}

}

工厂方法模式

工厂模式又称工厂方法模式,是一种创建型设计模式,其在父类中提供一个创建对象的方法,允许子类决定实例化对象的类型

  • 多种类型商品不同接口,统一发奖服务搭建场景
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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
/**
* 发奖请求对象
*/
public class AwardReq {

private String uId; // 用户唯一ID
private Integer awardType; // 奖品类型(可以用枚举定义);1优惠券、2实物商品、3第三方兑换卡(爱奇艺)
private String awardNumber; // 奖品编号;sku、couponNumber、cardId
private String bizId; // 业务ID,防重复
private Map<String, String> extMap; // 扩展信息

public String getuId() {
return uId;
}

public void setuId(String uId) {
this.uId = uId;
}

public Integer getAwardType() {
return awardType;
}

public void setAwardType(Integer awardType) {
this.awardType = awardType;
}

public String getAwardNumber() {
return awardNumber;
}

public void setAwardNumber(String awardNumber) {
this.awardNumber = awardNumber;
}

public String getBizId() {
return bizId;
}

public void setBizId(String bizId) {
this.bizId = bizId;
}

public Map<String, String> getExtMap() {
return extMap;
}

public void setExtMap(Map<String, String> extMap) {
this.extMap = extMap;
}
}
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
/**
* 发奖结果反馈对象
*/
public class AwardRes {

private String code; // 编码
private String info; // 描述

public AwardRes(String code, String info) {
this.code = code;
this.info = info;
}

public String getCode() {
return code;
}

public void setCode(String code) {
this.code = code;
}

public String getInfo() {
return info;
}

public void setInfo(String info) {
this.info = info;
}
}
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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
/**
* 模拟发奖服务
*/
public class PrizeController {

private Logger logger = LoggerFactory.getLogger(PrizeController.class);

public AwardRes awardToUser(AwardReq req) {
String reqJson = JSON.toJSONString(req);
AwardRes awardRes = null;
try {
logger.info("奖品发放开始{}。req:{}", req.getuId(), reqJson);
// 按照不同类型方法商品[1优惠券、2实物商品、3第三方兑换卡(爱奇艺)]
if (req.getAwardType() == 1) {
CouponService couponService = new CouponService();
CouponResult couponResult = couponService.sendCoupon(req.getuId(), req.getAwardNumber(), req.getBizId());
if ("0000".equals(couponResult.getCode())) {
awardRes = new AwardRes("0000", "发放成功");
} else {
awardRes = new AwardRes("0001", couponResult.getInfo());
}
} else if (req.getAwardType() == 2) {
GoodsService goodsService = new GoodsService();
DeliverReq deliverReq = new DeliverReq();
deliverReq.setUserName(queryUserName(req.getuId()));
deliverReq.setUserPhone(queryUserPhoneNumber(req.getuId()));
deliverReq.setSku(req.getAwardNumber());
deliverReq.setOrderId(req.getBizId());
deliverReq.setConsigneeUserName(req.getExtMap().get("consigneeUserName"));
deliverReq.setConsigneeUserPhone(req.getExtMap().get("consigneeUserPhone"));
deliverReq.setConsigneeUserAddress(req.getExtMap().get("consigneeUserAddress"));
Boolean isSuccess = goodsService.deliverGoods(deliverReq);
if (isSuccess) {
awardRes = new AwardRes("0000", "发放成功");
} else {
awardRes = new AwardRes("0001", "发放失败");
}
} else if (req.getAwardType() == 3) {
String bindMobileNumber = queryUserPhoneNumber(req.getuId());
IQiYiCardService iQiYiCardService = new IQiYiCardService();
iQiYiCardService.grantToken(bindMobileNumber, req.getAwardNumber());
awardRes = new AwardRes("0000", "发放成功");
}
logger.info("奖品发放完成{}。", req.getuId());
} catch (Exception e) {
logger.error("奖品发放失败{}。req:{}", req.getuId(), reqJson, e);
awardRes = new AwardRes("0001", e.getMessage());
}

return awardRes;
}

private String queryUserName(String uId) {
return "花花";
}

private String queryUserPhoneNumber(String uId) {
return "15200101232";
}
}
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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
/**
* 单元测试
*/
public class ApiTest {

private Logger logger = LoggerFactory.getLogger(ApiTest.class);

@Test
public void test_awardToUser() {

PrizeController prizeController = new PrizeController();

System.out.println("\r\n模拟发放优惠券测试\r\n");
// 模拟发放优惠券测试
AwardReq req01 = new AwardReq();
req01.setuId("10001");
req01.setAwardType(1);
req01.setAwardNumber("EGM1023938910232121323432");
req01.setBizId("791098764902132");
AwardRes awardRes01 = prizeController.awardToUser(req01);

logger.info("请求参数:{}", JSON.toJSON(req01));
logger.info("测试结果:{}", JSON.toJSON(awardRes01));

System.out.println("\r\n模拟方法实物商品\r\n");
// 模拟方法实物商品
AwardReq req02 = new AwardReq();
req02.setuId("10001");
req02.setAwardType(2);
req02.setAwardNumber("9820198721311");
req02.setBizId("1023000020112221113");
req02.setExtMap(new HashMap<String, String>() {{
put("consigneeUserName", "谢飞机");
put("consigneeUserPhone", "15200292123");
put("consigneeUserAddress", "吉林省.长春市.双阳区.XX街道.檀溪苑小区.#18-2109");
}});

AwardRes awardRes02 = prizeController.awardToUser(req02);
logger.info("请求参数:{}", JSON.toJSON(req02));
logger.info("测试结果:{}", JSON.toJSON(awardRes02));

System.out.println("\r\n第三方兑换卡(爱奇艺)\r\n");
AwardReq req03 = new AwardReq();
req03.setuId("10001");
req03.setAwardType(3);
req03.setAwardNumber("AQY1xjkUodl8LO975GdfrYUio");

AwardRes awardRes03 = prizeController.awardToUser(req03);
logger.info("请求参数:{}", JSON.toJSON(req03));
logger.info("测试结果:{}", JSON.toJSON(awardRes03));

}
}

接口实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* 爱奇艺兑换卡
*/
public class CardCommodityService implements ICommodity {

private Logger logger = LoggerFactory.getLogger(CardCommodityService.class);

// 模拟注入
private IQiYiCardService iQiYiCardService = new IQiYiCardService();

public void sendCommodity(String uId, String commodityId, String bizId, Map<String, String> extMap) throws Exception {
String mobile = queryUserMobile(uId);
iQiYiCardService.grantToken(mobile, bizId);
logger.info("请求参数[爱奇艺兑换卡] => uId:{} commodityId:{} bizId:{} extMap:{}", uId, commodityId, bizId, JSON.toJSON(extMap));
logger.info("测试结果[爱奇艺兑换卡]:success");
}

private String queryUserMobile(String uId) {
return "15200101232";
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* 优惠券
*/
public class CouponCommodityService implements ICommodity {

private Logger logger = LoggerFactory.getLogger(CouponCommodityService.class);

private CouponService couponService = new CouponService();

public void sendCommodity(String uId, String commodityId, String bizId, Map<String, String> extMap) throws Exception {
CouponResult couponResult = couponService.sendCoupon(uId, commodityId, bizId);
logger.info("请求参数[优惠券] => uId:{} commodityId:{} bizId:{} extMap:{}", uId, commodityId, bizId, JSON.toJSON(extMap));
logger.info("测试结果[优惠券]:{}", JSON.toJSON(couponResult));
if (!"0000".equals(couponResult.getCode())) throw new RuntimeException(couponResult.getInfo());
}
}
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
/**
* 实物商品
*/
public class GoodsCommodityService implements ICommodity {

private Logger logger = LoggerFactory.getLogger(GoodsCommodityService.class);

private GoodsService goodsService = new GoodsService();

public void sendCommodity(String uId, String commodityId, String bizId, Map<String, String> extMap) throws Exception {
DeliverReq deliverReq = new DeliverReq();
deliverReq.setUserName(queryUserName(uId));
deliverReq.setUserPhone(queryUserPhoneNumber(uId));
deliverReq.setSku(commodityId);
deliverReq.setOrderId(bizId);
deliverReq.setConsigneeUserName(extMap.get("consigneeUserName"));
deliverReq.setConsigneeUserPhone(extMap.get("consigneeUserPhone"));
deliverReq.setConsigneeUserAddress(extMap.get("consigneeUserAddress"));

Boolean isSuccess = goodsService.deliverGoods(deliverReq);

logger.info("请求参数[实物商品] => uId:{} commodityId:{} bizId:{} extMap:{}", uId, commodityId, bizId, JSON.toJSON(extMap));
logger.info("测试结果[实物商品]:{}", isSuccess);

if (!isSuccess) throw new RuntimeException("实物商品发放失败");
}

private String queryUserName(String uId) {
return "花花";
}

private String queryUserPhoneNumber(String uId) {
return "15200101232";
}
}
1
2
3
4
5
6
7
8
/**
* 发放商品接口
*/
public interface ICommodity {

void sendCommodity(String uId, String commodityId, String bizId, Map<String, String> extMap) throws Exception;

}

引入工厂模式

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
/**
* 工厂
*/
public class StoreFactory {

/**
* 奖品类型方式实例化
* @param commodityType 奖品类型
* @return 实例化对象
*/
public ICommodity getCommodityService(Integer commodityType) {
if (null == commodityType) return null;
if (1 == commodityType) return new CouponCommodityService();
if (2 == commodityType) return new GoodsCommodityService();
if (3 == commodityType) return new CardCommodityService();
throw new RuntimeException("不存在的奖品服务类型");
}

/**
* 奖品类信息方式实例化
* @param clazz 奖品类
* @return 实例化对象
*/
public ICommodity getCommodityService(Class<? extends ICommodity> clazz) throws IllegalAccessException, InstantiationException {
if (null == clazz) return null;
return clazz.newInstance();
}
}
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
/**
* 单元测试
*/
public class ApiTest {

@Test
public void test_StoreFactory_01() throws Exception {
StoreFactory storeFactory = new StoreFactory();

// 1. 优惠券
ICommodity commodityService_1 = storeFactory.getCommodityService(1);
commodityService_1.sendCommodity("10001", "EGM1023938910232121323432", "791098764902132", null);

// 2. 实物商品
ICommodity commodityService_2 = storeFactory.getCommodityService(2);
commodityService_2.sendCommodity("10001", "9820198721311", "1023000020112221113", new HashMap<String, String>() {{
put("consigneeUserName", "谢飞机");
put("consigneeUserPhone", "15200292123");
put("consigneeUserAddress", "吉林省.长春市.双阳区.XX街道.檀溪苑小区.#18-2109");
}});

// 3. 第三方兑换卡(模拟爱奇艺)
ICommodity commodityService_3 = storeFactory.getCommodityService(3);
commodityService_3.sendCommodity("10001", "AQY1xjkUodl8LO975GdfrYUio", null, null);

}

@Test
public void test_StoreFactory_02() throws Exception {
StoreFactory storeFactory = new StoreFactory();
// 1. 优惠券
ICommodity commodityService = storeFactory.getCommodityService(CouponCommodityService.class);
commodityService.sendCommodity("10001", "EGM1023938910232121323432", "791098764902132", null);
}
}

抽象工厂模式

围绕一个超级工厂创建其他工厂。该超级工厂又称为其他工厂的工厂。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式

  • 替换Redis双集群升级,代理类抽象场景
1
2
3
4
5
6
7
8
9
10
11
public interface CacheService {

String get(final String key);

void set(String key, String value);

void set(String key, String value, long timeout, TimeUnit timeUnit);

void del(String key);

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class CacheServiceImpl implements CacheService {

private RedisUtils redisUtils = new RedisUtils();

public String get(String key) {
return redisUtils.get(key);
}

public void set(String key, String value) {
redisUtils.set(key, value);
}

public void set(String key, String value, long timeout, TimeUnit timeUnit) {
redisUtils.set(key, value, timeout, timeUnit);
}

public void del(String key) {
redisUtils.del(key);
}
}

提供集群

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
/**
* 模拟Redis缓存服务,EGM
*/
public class EGM {

private Logger logger = LoggerFactory.getLogger(EGM.class);

private Map<String, String> dataMap = new ConcurrentHashMap<String, String>();

public String gain(String key) {
logger.info("EGM获取数据 key:{}", key);
return dataMap.get(key);
}

public void set(String key, String value) {
logger.info("EGM写入数据 key:{} val:{}", key, value);
dataMap.put(key, value);
}

public void setEx(String key, String value, long timeout, TimeUnit timeUnit) {
logger.info("EGM写入数据 key:{} val:{} timeout:{} timeUnit:{}", key, value, timeout, timeUnit.toString());
dataMap.put(key, value);
}

public void delete(String key) {
logger.info("EGM删除数据 key:{}", key);
dataMap.remove(key);
}
}
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
/**
* 模拟Redis缓存服务,IIR
*/
public class IIR {

private Logger logger = LoggerFactory.getLogger(IIR.class);

private Map<String, String> dataMap = new ConcurrentHashMap<String, String>();

public String get(String key) {
logger.info("IIR获取数据 key:{}", key);
return dataMap.get(key);
}

public void set(String key, String value) {
logger.info("IIR写入数据 key:{} val:{}", key, value);
dataMap.put(key, value);
}

public void setExpire(String key, String value, long timeout, TimeUnit timeUnit) {
logger.info("IIR写入数据 key:{} val:{} timeout:{} timeUnit:{}", key, value, timeout, timeUnit.toString());
dataMap.put(key, value);
}

public void del(String key) {
logger.info("IIR删除数据 key:{}", key);
dataMap.remove(key);
}
}
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
/**
* 模拟最开始使用的Redis服务,单机的。
*/
public class RedisUtils {

private Logger logger = LoggerFactory.getLogger(RedisUtils.class);

private Map<String, String> dataMap = new ConcurrentHashMap<>();

public String get(String key) {
logger.info("Redis获取数据 key:{}", key);
return dataMap.get(key);
}

public void set(String key, String value) {
logger.info("Redis写入数据 key:{} val:{}", key, value);
dataMap.put(key, value);
}

public void set(String key, String value, long timeout, TimeUnit timeUnit) {
logger.info("Redis写入数据 key:{} val:{} timeout:{} timeUnit:{}", key, value, timeout, timeUnit.toString());
dataMap.put(key, value);
}

public void del(String key) {
logger.info("Redis删除数据 key:{}", key);
dataMap.remove(key);
}
}

升级

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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
/**
* 升级后,使用多套Redis集群服务,同时兼容以前单体Redis服务
*/
public class CacheClusterServiceImpl implements CacheService {

private RedisUtils redisUtils = new RedisUtils();

private EGM egm = new EGM();

private IIR iir = new IIR();

public String get(String key, int redisType) {

if (1 == redisType) {
return egm.gain(key);
}

if (2 == redisType) {
return iir.get(key);
}

return redisUtils.get(key);
}

public void set(String key, String value, int redisType) {

if (1 == redisType) {
egm.set(key, value);
return;
}

if (2 == redisType) {
iir.set(key, value);
return;
}

redisUtils.set(key, value);
}

public void set(String key, String value, long timeout, TimeUnit timeUnit, int redisType) {

if (1 == redisType) {
egm.setEx(key, value, timeout, timeUnit);
return;
}

if (2 == redisType) {
iir.setExpire(key, value, timeout, timeUnit);
return;
}

redisUtils.set(key, value, timeout, timeUnit);
}

public void del(String key, int redisType) {

if (1 == redisType) {
egm.delete(key);
return;
}

if (2 == redisType) {
iir.del(key);
return;
}

redisUtils.del(key);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/*
* 测试
*/
public class ApiTest {

private Logger logger = LoggerFactory.getLogger(ApiTest.class);

@Test
public void test_CacheServiceAfterImpl() {
CacheService cacheService = new CacheClusterServiceImpl();

cacheService.set("user_name_01", "小明", 1);
String val01 = cacheService.get("user_name_01", 1);
logger.info("缓存集群升级,测试结果:{}", val01);
}
}

提供适配类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* 车间适配器
*/
public interface ICacheAdapter {

String get(final String key);

void set(String key, String value);

void set(String key, String value, long timeout, TimeUnit timeUnit);

void del(String key);

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class EGMCacheAdapter implements ICacheAdapter {

private EGM egm = new EGM();

public String get(String key) {
return egm.gain(key);
}

public void set(String key, String value) {
egm.set(key, value);
}

public void set(String key, String value, long timeout, TimeUnit timeUnit) {
egm.setEx(key, value, timeout, timeUnit);
}

public void del(String key) {
egm.delete(key);
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class IIRCacheAdapter implements ICacheAdapter {

private IIR iir = new IIR();

@Override
public String get(String key) {
return iir.get(key);
}

@Override
public void set(String key, String value) {
iir.set(key, value);
}

@Override
public void set(String key, String value, long timeout, TimeUnit timeUnit) {
iir.setExpire(key, value, timeout, timeUnit);
}

@Override
public void del(String key) {
iir.del(key);
}
}
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 class ClassLoaderUtils {

public static Class<?>[] getClazzByArgs(Object[] args) {
Class<?>[] parameterTypes = new Class[args.length];
for (int i = 0; i < args.length; i++) {
if (args[i] instanceof ArrayList) {
parameterTypes[i] = List.class;
continue;
}
if (args[i] instanceof LinkedList) {
parameterTypes[i] = List.class;
continue;
}
if (args[i] instanceof HashMap) {
parameterTypes[i] = Map.class;
continue;
}
if (args[i] instanceof Long){
parameterTypes[i] = long.class;
continue;
}
if (args[i] instanceof Double){
parameterTypes[i] = double.class;
continue;
}
if (args[i] instanceof TimeUnit){
parameterTypes[i] = TimeUnit.class;
continue;
}
parameterTypes[i] = args[i].getClass();
}
return parameterTypes;
}
}

抽象工厂实现

1
2
3
4
5
6
7
8
9
10
11
/**
* 代理对象
*/
public class JDKProxyFactory {

public static <T> T getProxy(Class<T> cacheClazz, Class<? extends ICacheAdapter> cacheAdapter) throws Exception {
InvocationHandler handler = new JDKInvocationHandler(cacheAdapter.newInstance());
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
return (T) Proxy.newProxyInstance(classLoader, new Class[]{cacheClazz}, handler);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* 抽象工厂
*/
public class JDKInvocationHandler implements InvocationHandler {

private ICacheAdapter cacheAdapter;

public JDKInvocationHandler(ICacheAdapter cacheAdapter) {
this.cacheAdapter = cacheAdapter;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return ICacheAdapter.class.getMethod(method.getName(), ClassLoaderUtils.getClazzByArgs(args)).invoke(cacheAdapter, args);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* 单元测试
*/
public class ApiTest {

private Logger logger = LoggerFactory.getLogger(ApiTest.class);

@Test
public void test_CacheService() throws Exception {
CacheService proxy_EGM = JDKProxyFactory.getProxy(CacheService.class, EGMCacheAdapter.class);
proxy_EGM.set("user_name_01", "小明");
String val01 = proxy_EGM.get("user_name_01");
logger.info("缓存服务 EGM 测试,proxy_EGM.get 测试结果:{}", val01);

CacheService proxy_IIR = JDKProxyFactory.getProxy(CacheService.class, IIRCacheAdapter.class);
proxy_IIR.set("user_name_01", "小明");
String val02 = proxy_IIR.get("user_name_01");
logger.info("缓存服务 IIR 测试,proxy_IIR.get 测试结果:{}", val02);
}
}

建造者模式

指将一个复杂对象的构造与它的表示分离,是同样的构造过程可以创建不同的表示。它是将一个复杂的对象分解为多个简单的对象,然后一步一步构建而成

  • 各项装修物料组合套餐选配场景
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
public interface IMenu {

/**
* 吊顶
*/
IMenu appendCeiling(Matter matter);

/**
* 涂料
*/
IMenu appendCoat(Matter matter);

/**
* 地板
*/
IMenu appendFloor(Matter matter);

/**
* 地砖
*/
IMenu appendTile(Matter matter);

/**
* 明细
*/
String getDetail();

}
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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
/**
* 装修包
*/
public class DecorationPackageMenu implements IMenu {

private List<Matter> list = new ArrayList<Matter>(); // 装修清单
private BigDecimal price = BigDecimal.ZERO; // 装修价格

private BigDecimal area; // 面积
private String grade; // 装修等级;豪华欧式、轻奢田园、现代简约

private DecorationPackageMenu() {
}

public DecorationPackageMenu(Double area, String grade) {
this.area = new BigDecimal(area);
this.grade = grade;
}

public IMenu appendCeiling(Matter matter) {
list.add(matter);
price = price.add(area.multiply(new BigDecimal("0.2")).multiply(matter.price()));
return this;
}

public IMenu appendCoat(Matter matter) {
list.add(matter);
price = price.add(area.multiply(new BigDecimal("1.4")).multiply(matter.price()));
return this;
}

public IMenu appendFloor(Matter matter) {
list.add(matter);
price = price.add(area.multiply(matter.price()));
return this;
}

public IMenu appendTile(Matter matter) {
list.add(matter);
price = price.add(area.multiply(matter.price()));
return this;
}

public String getDetail() {

StringBuilder detail = new StringBuilder("\r\n-------------------------------------------------------\r\n" +
"装修清单" + "\r\n" +
"套餐等级:" + grade + "\r\n" +
"套餐价格:" + price.setScale(2, BigDecimal.ROUND_HALF_UP) + " 元\r\n" +
"房屋面积:" + area.doubleValue() + " 平米\r\n" +
"材料清单:\r\n");

for (Matter matter: list) {
detail.append(matter.scene()).append(":").append(matter.brand()).append("、").append(matter.model()).append("、平米价格:").append(matter.price()).append(" 元。\n");
}

return detail.toString();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class Builder {

public IMenu levelOne(Double area) {
return new DecorationPackageMenu(area, "豪华欧式")
.appendCeiling(new LevelTwoCeiling()) // 吊顶,二级顶
.appendCoat(new DuluxCoat()) // 涂料,多乐士
.appendFloor(new ShengXiangFloor()); // 地板,圣象
}

public IMenu levelTwo(Double area){
return new DecorationPackageMenu(area, "轻奢田园")
.appendCeiling(new LevelTwoCeiling()) // 吊顶,二级顶
.appendCoat(new LiBangCoat()) // 涂料,立邦
.appendTile(new MarcoPoloTile()); // 地砖,马可波罗
}

public IMenu levelThree(Double area){
return new DecorationPackageMenu(area, "现代简约")
.appendCeiling(new LevelOneCeiling()) // 吊顶,一级顶
.appendCoat(new LiBangCoat()) // 涂料,立邦
.appendTile(new DongPengTile()); // 地砖,东鹏
}
}

原型模式

用于创建重复的对象,同时又能保证性能。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式

  • 上机考试多套试,每人题目和答案乱序排列场景
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
public class Topic {

private Map<String, String> option; // 选项;A、B、C、D
private String key; // 答案;B

public Topic() {
}

public Topic(Map<String, String> option, String key) {
this.option = option;
this.key = key;
}

public Map<String, String> getOption() {
return option;
}

public void setOption(Map<String, String> option) {
this.option = option;
}

public String getKey() {
return key;
}

public void setKey(String key) {
this.key = key;
}
}
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
public class TopicRandomUtil {

/**
* 乱序Map元素,记录对应答案key
* @param option 题目
* @param key 答案
* @return Topic 乱序后 {A=c., B=d., C=a., D=b.}
*/
static public Topic random(Map<String, String> option, String key) {
Set<String> keySet = option.keySet();
ArrayList<String> keyList = new ArrayList<String>(keySet);
Collections.shuffle(keyList);
HashMap<String, String> optionNew = new HashMap<String, String>();
int idx = 0;
String keyNew = "";
for (String next : keySet) {
String randomKey = keyList.get(idx++);
if (key.equals(next)) {
keyNew = randomKey;
}
optionNew.put(randomKey, option.get(next));
}
return new Topic(optionNew, keyNew);
}
}
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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
/**
* 题库
*/
public class QuestionBank implements Cloneable{

private String candidate; // 考生
private String number; // 考号

private ArrayList<ChoiceQuestion> choiceQuestionList = new ArrayList<>();
private ArrayList<AnswerQuestion> answerQuestionList = new ArrayList<>();

public QuestionBank append(ChoiceQuestion choiceQuestion) {
choiceQuestionList.add(choiceQuestion);
return this;
}

public QuestionBank append(AnswerQuestion answerQuestion) {
answerQuestionList.add(answerQuestion);
return this;
}

@Override
public Object clone() throws CloneNotSupportedException {
QuestionBank questionBank = (QuestionBank) super.clone();
questionBank.choiceQuestionList = (ArrayList<ChoiceQuestion>) choiceQuestionList.clone();
questionBank.answerQuestionList = (ArrayList<AnswerQuestion>) answerQuestionList.clone();

// 题目乱序
Collections.shuffle(questionBank.choiceQuestionList);
Collections.shuffle(questionBank.answerQuestionList);

// 答案乱序
ArrayList<ChoiceQuestion> choiceQuestionList = questionBank.choiceQuestionList;
for (ChoiceQuestion question : choiceQuestionList) {
Topic random = TopicRandomUtil.random(question.getOption(), question.getKey());
question.setOption(random.getOption());
question.setKey(random.getKey());
}
return questionBank;
}

public void setCandidate(String candidate) {
this.candidate = candidate;
}

public void setNumber(String number) {
this.number = number;
}

@Override
public String toString() {

StringBuilder detail = new StringBuilder("考生:" + candidate + "\r\n" +
"考号:" + number + "\r\n" +
"--------------------------------------------\r\n" +
"一、选择题" + "\r\n\n");

for (int idx = 0; idx < choiceQuestionList.size(); idx++) {
detail.append("第").append(idx + 1).append("题:").append(choiceQuestionList.get(idx).getName()).append("\r\n");
Map<String, String> option = choiceQuestionList.get(idx).getOption();
for (String key : option.keySet()) {
detail.append(key).append(":").append(option.get(key)).append("\r\n");;
}
detail.append("答案:").append(choiceQuestionList.get(idx).getKey()).append("\r\n\n");
}

detail.append("二、问答题" + "\r\n\n");

for (int idx = 0; idx < answerQuestionList.size(); idx++) {
detail.append("第").append(idx + 1).append("题:").append(answerQuestionList.get(idx).getName()).append("\r\n");
detail.append("答案:").append(answerQuestionList.get(idx).getKey()).append("\r\n\n");
}

return detail.toString();
}
}
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
38
39
40
41
42
43
44
45
46
47
48
/**
* 题库管理
*/
public class QuestionBankController {

private QuestionBank questionBank = new QuestionBank();

public QuestionBankController() {
questionBank.append(new ChoiceQuestion("JAVA所定义的版本中不包括", new HashMap<String, String>() {{
put("A", "JAVA2 EE");
put("B", "JAVA2 Card");
put("C", "JAVA2 ME");
put("D", "JAVA2 HE");
put("E", "JAVA2 SE");
}}, "D")).append(new ChoiceQuestion("下列说法正确的是", new HashMap<String, String>() {{
put("A", "JAVA程序的main方法必须写在类里面");
put("B", "JAVA程序中可以有多个main方法");
put("C", "JAVA程序中类名必须与文件名一样");
put("D", "JAVA程序的main方法中如果只有一条语句,可以不用{}(大括号)括起来");
}}, "A")).append(new ChoiceQuestion("变量命名规范说法正确的是", new HashMap<String, String>() {{
put("A", "变量由字母、下划线、数字、$符号随意组成;");
put("B", "变量不能以数字作为开头;");
put("C", "A和a在java中是同一个变量;");
put("D", "不同类型的变量,可以起相同的名字;");
}}, "B")).append(new ChoiceQuestion("以下()不是合法的标识符", new HashMap<String, String>() {{
put("A", "STRING");
put("B", "x3x;");
put("C", "void");
put("D", "de$f");
}}, "C")).append(new ChoiceQuestion("表达式(11+3*8)/4%3的值是", new HashMap<String, String>() {{
put("A", "31");
put("B", "0");
put("C", "1");
put("D", "2");
}}, "D"))
.append(new AnswerQuestion("小红马和小黑马生的小马几条腿", "4条腿"))
.append(new AnswerQuestion("铁棒打头疼还是木棒打头疼", "头最疼"))
.append(new AnswerQuestion("什么床不能睡觉", "牙床"))
.append(new AnswerQuestion("为什么好马不吃回头草", "后面的草没了"));
}

public String createPaper(String candidate, String number) throws CloneNotSupportedException {
QuestionBank questionBankClone = (QuestionBank) questionBank.clone();
questionBankClone.setCandidate(candidate);
questionBankClone.setNumber(number);
return questionBankClone.toString();
}
}
1
2
3
4
5
6
7
8
9
10
public class ApiTest {

@Test
public void test_QuestionBank() throws CloneNotSupportedException {
QuestionBankController questionBankController = new QuestionBankController();
System.out.println(questionBankController.createPaper("花花", "1000001921032"));
System.out.println(questionBankController.createPaper("豆豆", "1000001921051"));
System.out.println(questionBankController.createPaper("大宝", "1000001921987"));
}
}

单例模式

最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式

  • 7种单例模式案例,推荐枚举单例模式

原则

  • 私有构造(阻止类被通过常规方法实例化)
  • 以静态方法或者枚举返回实例(保证实例的唯一性)
  • 确保实例只有一个,尤其是多线程环境(保证在创建实例时的线程安全)
  • 确保反序列化时不会重新构建对象(在有序列化反序列化的场景下防止单例被莫名破坏,造成未考虑到的后果)

手段

  • 主动处理
    • synchronized
    • volatitle
    • cas
  • JVM机制
    • 由静态初始化器(在静态字段上或static{}块中的初始化器)初始化数据时
    • 访问final字段时
    • 在创建线程之前创建对象时
    • 线程可以看见它将要处理的对象时
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/*
* 饿汉模式(线程安全)
*/
public class Singleton_03 {

private static Singleton_03 instance = new Singleton_03();

private Singleton_03() {
}

public static Singleton_03 getInstance() {
return instance;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/*
* 懒汉模式(线程不安全)
*/
public class Singleton_01 {

private static Singleton_01 instance;

private Singleton_01() {
}

public static Singleton_01 getInstance(){
if (null != instance) return instance;
instance = new Singleton_01();
return instance;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/*
* 双重锁校验
*/
public class Singleton_05 {

private static volatile Singleton_05 instance;

private Singleton_05() {
}

public static Singleton_05 getInstance(){
if(null != instance) return instance;
synchronized (Singleton_05.class){
if (null == instance){
instance = new Singleton_05();
}
}
return instance;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/*
* 类的内部类
*/
public class Singleton_04 {

private static class SingletonHolder {
private static Singleton_04 instance = new Singleton_04();
}

private Singleton_04() {
}

public static Singleton_04 getInstance() {
return SingletonHolder.instance;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/*
* CAS
*/
public class Singleton_06 {

private static final AtomicReference<Singleton_06> INSTANCE = new AtomicReference<Singleton_06>();

private static Singleton_06 instance;

private Singleton_06() {
}

public static final Singleton_06 getInstance() {
for (; ; ) {
Singleton_06 instance = INSTANCE.get();
if (null != instance) return instance;
INSTANCE.compareAndSet(null, new Singleton_06());
return INSTANCE.get();
}
}
}
1
2
3
4
5
6
7
8
9
10
/*
* 枚举单例
*/
public enum Singleton_07 {

INSTANCE;
public void test(){
System.out.println("hi~");
}
}

适配器模式

将一个类的接口适配成用户所期待的。一个适配允许通常因为接口不兼容而不能在一起工作的类工作在一起,做法是将类自己的接口包裹在一个已存在的类中

  • 从多个MQ消息体中,抽取指定字段值场景
1
2
3
4
5
6
7
8
/**
* 适配接口
*/
public interface OrderAdapterService {

boolean isFirst(String uId);

}
1
2
3
4
5
6
7
8
9
10
11
/**
* 内部订单,判断首单逻辑
*/
public class InsideOrderServiceImpl implements OrderAdapterService {

private OrderService orderService = new OrderService();

public boolean isFirst(String uId) {
return orderService.queryUserOrderCount(uId) <= 1;
}
}
1
2
3
4
5
6
7
8
9
10
11
/**
* 第三方订单,判断首单逻辑
*/
public class POPOrderAdapterServiceImpl implements OrderAdapterService {

private POPOrderService popOrderService = new POPOrderService();

public boolean isFirst(String uId) {
return popOrderService.isFirstOrder(uId);
}
}
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
38
39
40
41
42
/**
* 字段
*/
public class RebateInfo {

private String userId;
private String bizId;
private String bizTime;
private String desc;

public String getUserId() {
return userId;
}

public void setUserId(String userId) {
this.userId = userId;
}

public String getBizId() {
return bizId;
}

public void setBizId(String bizId) {
this.bizId = bizId;
}

public String getBizTime() {
return bizTime;
}

public void setBizTime(String bizTime) {
this.bizTime = bizTime;
}

public String getDesc() {
return desc;
}

public void setDesc(String desc) {
this.desc = desc;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* 适配器
*/
public class MQAdapter {

public static RebateInfo filter(String strJson, Map<String, String> link) throws Exception {
return filter(JSON.parseObject(strJson, Map.class), link);
}

public static RebateInfo filter(Map obj, Map<String, String> link) throws Exception {
RebateInfo rebateInfo = new RebateInfo();
for (String key : link.keySet()) {
Object val = obj.get(link.get(key));
RebateInfo.class.getMethod("set" + key.substring(0, 1).toUpperCase() + key.substring(1), String.class).invoke(rebateInfo, val.toString());
}
return rebateInfo;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class ApiTest {

@Test
public void test_MQAdapter() throws Exception {
SimpleDateFormat s = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date parse = s.parse("2020-06-01 23:20:16");

create_account create_account = new create_account();
create_account.setNumber("100001");
create_account.setAddress("江苏省.连云港市.海州区.大学里职业技术学院");
create_account.setAccountDate(parse);
create_account.setDesc("在校开户");

HashMap<String, String> link01 = new HashMap<String, String>();
link01.put("userId", "number");
link01.put("bizId", "number");
link01.put("bizTime", "accountDate");
link01.put("desc", "desc");

RebateInfo rebateInfo01 = MQAdapter.filter(create_account.toString(), link01);
System.out.println("mq.create_account(适配前)" + create_account.toString());
System.out.println("mq.create_account(适配后)" + JSON.toJSONString(rebateInfo01));
}
}

桥接模式

是一种结构型设计模式,可将一个大类或一系列紧密相关的类拆分为抽象和实现两个独立的层次结构,从而能在开发时分别使用

  • 多支付渠道(微信、支付宝)与多支付模式(刷脸、指纹)场景

支付模式

1
2
3
4
5
public interface IPayMode {

boolean security(String uId);

}
1
2
3
4
5
6
7
8
9
public class PayCypher implements IPayMode{

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

public boolean security(String uId) {
logger.info("密码支付,风控校验环境安全");
return true;
}
}
1
2
3
4
5
6
7
8
9
public class PayFaceMode implements IPayMode{

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

public boolean security(String uId) {
logger.info("人脸支付,风控校验脸部识别");
return true;
}
}
1
2
3
4
5
6
7
8
9
public class PayFingerprintMode implements IPayMode{

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

public boolean security(String uId) {
logger.info("指纹支付,风控校验指纹信息");
return true;
}
}

支付渠道

1
2
3
4
5
6
7
8
9
10
11
12
public abstract class Pay {

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

protected IPayMode payMode;

public Pay(IPayMode payMode) {
this.payMode = payMode;
}

public abstract String transfer(String uId, String tradeId, BigDecimal amount);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class WxPay extends Pay {

public WxPay(IPayMode payMode) {
super(payMode);
}

public String transfer(String uId, String tradeId, BigDecimal amount) {
logger.info("模拟微信渠道支付划账开始。uId:{} tradeId:{} amount:{}", uId, tradeId, amount);
boolean security = payMode.security(uId);
logger.info("模拟微信渠道支付风控校验。uId:{} tradeId:{} security:{}", uId, tradeId, security);
if (!security) {
logger.info("模拟微信渠道支付划账拦截。uId:{} tradeId:{} amount:{}", uId, tradeId, amount);
return "0001";
}
logger.info("模拟微信渠道支付划账成功。uId:{} tradeId:{} amount:{}", uId, tradeId, amount);
return "0000";
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class ZfbPay extends Pay {

public ZfbPay(IPayMode payMode) {
super(payMode);
}

public String transfer(String uId, String tradeId, BigDecimal amount) {
logger.info("模拟支付宝渠道支付划账开始。uId:{} tradeId:{} amount:{}", uId, tradeId, amount);
boolean security = payMode.security(uId);
logger.info("模拟支付宝渠道支付风控校验。uId:{} tradeId:{} security:{}", uId, tradeId, security);
if (!security) {
logger.info("模拟支付宝渠道支付划账拦截。uId:{} tradeId:{} amount:{}", uId, tradeId, amount);
return "0001";
}
logger.info("模拟支付宝渠道支付划账成功。uId:{} tradeId:{} amount:{}", uId, tradeId, amount);
return "0000";
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/*
* 测试验证
*/
public class ApiTest {

@Test
public void test_pay() {

System.out.println("\r\n模拟测试场景;微信支付、人脸方式。");
Pay wxPay = new WxPay(new PayFaceMode());
wxPay.transfer("weixin_1092033111", "100000109893", new BigDecimal(100));

System.out.println("\r\n模拟测试场景;支付宝支付、指纹方式。");
Pay zfbPay = new ZfbPay(new PayFingerprintMode());
zfbPay.transfer("jlu19dlxo111", "100000109894", new BigDecimal(100));

}
}

组合模式

也成为整体-部分模式,它的宗旨是通过将单个对象(叶子节点)和组合对象(树枝节点)用相同的接口进行表示,使得客户对单个对象和组合对象的使用具有一致性

  • 营销差异化人群发券,决策树引擎搭建场景

充血模型

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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
/**
* 决策结果
*/
public class EngineResult {

private boolean isSuccess; //执行结果
private String userId; //用户ID
private Long treeId; //规则树ID
private Long nodeId; //果实节点ID
private String nodeValue;//果实节点值

public EngineResult() {
}

public EngineResult(boolean isSuccess) {
this.isSuccess = isSuccess;
}

public EngineResult(String userId, Long treeId, Long nodeId, String nodeValue) {
this.isSuccess = true;
this.userId = userId;
this.treeId = treeId;
this.nodeId = nodeId;
this.nodeValue = nodeValue;
}

public boolean isSuccess() {
return isSuccess;
}

public void setSuccess(boolean success) {
isSuccess = success;
}

public String getUserId() {
return userId;
}

public void setUserId(String userId) {
this.userId = userId;
}

public Long getTreeId() {
return treeId;
}

public void setTreeId(Long treeId) {
this.treeId = treeId;
}

public Long getNodeId() {
return nodeId;
}

public void setNodeId(Long nodeId) {
this.nodeId = nodeId;
}

public String getNodeValue() {
return nodeValue;
}

public void setNodeValue(String nodeValue) {
this.nodeValue = nodeValue;
}
}
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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
/**
* 规则树节点信息
*/
public class TreeNode {

private Long treeId; //规则树ID
private Long treeNodeId; //规则树节点ID
private Integer nodeType; //节点类型;1子叶、2果实
private String nodeValue; //节点值[nodeType=2];果实值
private String ruleKey; //规则Key
private String ruleDesc; //规则描述
private List<TreeNodeLink> treeNodeLinkList; //节点链路

public Long getTreeId() {
return treeId;
}

public void setTreeId(Long treeId) {
this.treeId = treeId;
}

public Long getTreeNodeId() {
return treeNodeId;
}

public void setTreeNodeId(Long treeNodeId) {
this.treeNodeId = treeNodeId;
}

public Integer getNodeType() {
return nodeType;
}

public void setNodeType(Integer nodeType) {
this.nodeType = nodeType;
}

public String getNodeValue() {
return nodeValue;
}

public void setNodeValue(String nodeValue) {
this.nodeValue = nodeValue;
}

public String getRuleKey() {
return ruleKey;
}

public void setRuleKey(String ruleKey) {
this.ruleKey = ruleKey;
}

public String getRuleDesc() {
return ruleDesc;
}

public void setRuleDesc(String ruleDesc) {
this.ruleDesc = ruleDesc;
}

public List<TreeNodeLink> getTreeNodeLinkList() {
return treeNodeLinkList;
}

public void setTreeNodeLinkList(List<TreeNodeLink> treeNodeLinkList) {
this.treeNodeLinkList = treeNodeLinkList;
}
}
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
38
39
40
41
42
/**
* 规则树线信息
*/
public class TreeNodeLink {

private Long nodeIdFrom; //节点From
private Long nodeIdTo; //节点To
private Integer ruleLimitType; //限定类型;1:=;2:>;3:<;4:>=;5<=;6:enum[枚举范围]
private String ruleLimitValue; //限定值

public Long getNodeIdFrom() {
return nodeIdFrom;
}

public void setNodeIdFrom(Long nodeIdFrom) {
this.nodeIdFrom = nodeIdFrom;
}

public Long getNodeIdTo() {
return nodeIdTo;
}

public void setNodeIdTo(Long nodeIdTo) {
this.nodeIdTo = nodeIdTo;
}

public Integer getRuleLimitType() {
return ruleLimitType;
}

public void setRuleLimitType(Integer ruleLimitType) {
this.ruleLimitType = ruleLimitType;
}

public String getRuleLimitValue() {
return ruleLimitValue;
}

public void setRuleLimitValue(String ruleLimitValue) {
this.ruleLimitValue = ruleLimitValue;
}
}
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
/**
* 树根信息
*/
public class TreeRoot {

private Long treeId; //规则树ID
private Long treeRootNodeId; //规则树根ID
private String treeName; //规则树名称

public Long getTreeId() {
return treeId;
}

public void setTreeId(Long treeId) {
this.treeId = treeId;
}

public Long getTreeRootNodeId() {
return treeRootNodeId;
}

public void setTreeRootNodeId(Long treeRootNodeId) {
this.treeRootNodeId = treeRootNodeId;
}

public String getTreeName() {
return treeName;
}

public void setTreeName(String treeName) {
this.treeName = treeName;
}
}

聚合对象

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
/**
* 规则树聚合
*/
public class TreeRich {

private TreeRoot treeRoot; //树根信息
private Map<Long, TreeNode> treeNodeMap; //树节点ID -> 子节点

public TreeRich(TreeRoot treeRoot, Map<Long, TreeNode> treeNodeMap) {
this.treeRoot = treeRoot;
this.treeNodeMap = treeNodeMap;
}

public TreeRoot getTreeRoot() {
return treeRoot;
}

public void setTreeRoot(TreeRoot treeRoot) {
this.treeRoot = treeRoot;
}

public Map<Long, TreeNode> getTreeNodeMap() {
return treeNodeMap;
}

public void setTreeNodeMap(Map<Long, TreeNode> treeNodeMap) {
this.treeNodeMap = treeNodeMap;
}
}

逻辑处理类

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 abstract class BaseLogic implements LogicFilter{

@Override
public Long filter(String matterValue, List<TreeNodeLink> treeNodeLinkList) {
for (TreeNodeLink nodeLine : treeNodeLinkList) {
if (decisionLogic(matterValue, nodeLine)) return nodeLine.getNodeIdTo();
}
return 0L;
}

@Override
public abstract String matterValue(Long treeId, String userId, Map<String, String> decisionMatter);

private boolean decisionLogic(String matterValue, TreeNodeLink nodeLink) {
switch (nodeLink.getRuleLimitType()) {
case 1:
return matterValue.equals(nodeLink.getRuleLimitValue());
case 2:
return Double.parseDouble(matterValue) > Double.parseDouble(nodeLink.getRuleLimitValue());
case 3:
return Double.parseDouble(matterValue) < Double.parseDouble(nodeLink.getRuleLimitValue());
case 4:
return Double.parseDouble(matterValue) <= Double.parseDouble(nodeLink.getRuleLimitValue());
case 5:
return Double.parseDouble(matterValue) >= Double.parseDouble(nodeLink.getRuleLimitValue());
default:
return false;
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public interface LogicFilter {

/**
* 逻辑决策器
*
* @param matterValue 决策值
* @param treeNodeLineInfoList 决策节点
* @return 下一个节点Id
*/
Long filter(String matterValue, List<TreeNodeLink> treeNodeLineInfoList);

/**
* 获取决策值
*
* @param decisionMatter 决策物料
* @return 决策值
*/
String matterValue(Long treeId, String userId, Map<String, String> decisionMatter);

}
1
2
3
4
5
6
7
public class UserAgeFilter extends BaseLogic {

@Override
public String matterValue(Long treeId, String userId, Map<String, String> decisionMatter) {
return decisionMatter.get("age");
}
}
1
2
3
4
5
6
7
public class UserGenderFilter extends BaseLogic {

@Override
public String matterValue(Long treeId, String userId, Map<String, String> decisionMatter) {
return decisionMatter.get("gender");
}
}
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
public abstract class EngineBase extends EngineConfig implements IEngine{

private Logger logger = LoggerFactory.getLogger(EngineBase.class);

@Override
public abstract EngineResult process(Long treeId, String userId, TreeRich treeRich, Map<String, String> decisionMatter);

protected TreeNode engineDecisionMaker(TreeRich treeRich, Long treeId, String userId, Map<String, String> decisionMatter) {
TreeRoot treeRoot = treeRich.getTreeRoot();
Map<Long, TreeNode> treeNodeMap = treeRich.getTreeNodeMap();
// 规则树根ID
Long rootNodeId = treeRoot.getTreeRootNodeId();
TreeNode treeNodeInfo = treeNodeMap.get(rootNodeId);
//节点类型[NodeType];1子叶、2果实
while (treeNodeInfo.getNodeType().equals(1)) {
String ruleKey = treeNodeInfo.getRuleKey();
LogicFilter logicFilter = logicFilterMap.get(ruleKey);
String matterValue = logicFilter.matterValue(treeId, userId, decisionMatter);
Long nextNode = logicFilter.filter(matterValue, treeNodeInfo.getTreeNodeLinkList());
treeNodeInfo = treeNodeMap.get(nextNode);
logger.info("决策树引擎=>{} userId:{} treeId:{} treeNode:{} ruleKey:{} matterValue:{}", treeRoot.getTreeName(), userId, treeId, treeNodeInfo.getTreeNodeId(), ruleKey, matterValue);
}
return treeNodeInfo;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class EngineConfig {

static Map<String, LogicFilter> logicFilterMap;

static {
logicFilterMap = new ConcurrentHashMap<>();
logicFilterMap.put("userAge", new UserAgeFilter());
logicFilterMap.put("userGender", new UserGenderFilter());
}

public Map<String, LogicFilter> getLogicFilterMap() {
return logicFilterMap;
}

public void setLogicFilterMap(Map<String, LogicFilter> logicFilterMap) {
this.logicFilterMap = logicFilterMap;
}
}
1
2
3
4
5
public interface IEngine {

EngineResult process(final Long treeId, final String userId, TreeRich treeRich, final Map<String, String> decisionMatter);

}
1
2
3
4
5
6
7
8
9
10
public class TreeEngineHandle extends EngineBase {

@Override
public EngineResult process(Long treeId, String userId, TreeRich treeRich, Map<String, String> decisionMatter) {
// 决策流程
TreeNode treeNode = engineDecisionMaker(treeRich, treeId, userId, decisionMatter);
// 决策结果
return new EngineResult(userId, treeId, treeNode.getTreeNodeId(), treeNode.getNodeValue());
}
}
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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
/*
* 测试
*/
public class ApiTest {

private Logger logger = LoggerFactory.getLogger(ApiTest.class);

private TreeRich treeRich;

@Before
public void init(){
// 节点:1
TreeNode treeNode_01 = new TreeNode();
treeNode_01.setTreeId(10001L);
treeNode_01.setTreeNodeId(1L);
treeNode_01.setNodeType(1);
treeNode_01.setNodeValue(null);
treeNode_01.setRuleKey("userGender");
treeNode_01.setRuleDesc("用户性别[男/女]");

// 链接:1->11
TreeNodeLink treeNodeLink_11 = new TreeNodeLink();
treeNodeLink_11.setNodeIdFrom(1L);
treeNodeLink_11.setNodeIdTo(11L);
treeNodeLink_11.setRuleLimitType(1);
treeNodeLink_11.setRuleLimitValue("man");

// 链接:1->12
TreeNodeLink treeNodeLink_12 = new TreeNodeLink();
treeNodeLink_12.setNodeIdFrom(1L);
treeNodeLink_12.setNodeIdTo(12L);
treeNodeLink_12.setRuleLimitType(1);
treeNodeLink_12.setRuleLimitValue("woman");

List<TreeNodeLink> treeNodeLinkList_1 = new ArrayList<>();
treeNodeLinkList_1.add(treeNodeLink_11);
treeNodeLinkList_1.add(treeNodeLink_12);

treeNode_01.setTreeNodeLinkList(treeNodeLinkList_1);

// 节点:11
TreeNode treeNode_11 = new TreeNode();
treeNode_11.setTreeId(10001L);
treeNode_11.setTreeNodeId(11L);
treeNode_11.setNodeType(1);
treeNode_11.setNodeValue(null);
treeNode_11.setRuleKey("userAge");
treeNode_11.setRuleDesc("用户年龄");

// 链接:11->111
TreeNodeLink treeNodeLink_111 = new TreeNodeLink();
treeNodeLink_111.setNodeIdFrom(11L);
treeNodeLink_111.setNodeIdTo(111L);
treeNodeLink_111.setRuleLimitType(3);
treeNodeLink_111.setRuleLimitValue("25");

// 链接:11->112
TreeNodeLink treeNodeLink_112 = new TreeNodeLink();
treeNodeLink_112.setNodeIdFrom(11L);
treeNodeLink_112.setNodeIdTo(112L);
treeNodeLink_112.setRuleLimitType(5);
treeNodeLink_112.setRuleLimitValue("25");

List<TreeNodeLink> treeNodeLinkList_11 = new ArrayList<>();
treeNodeLinkList_11.add(treeNodeLink_111);
treeNodeLinkList_11.add(treeNodeLink_112);

treeNode_11.setTreeNodeLinkList(treeNodeLinkList_11);

// 节点:12
TreeNode treeNode_12 = new TreeNode();
treeNode_12.setTreeId(10001L);
treeNode_12.setTreeNodeId(12L);
treeNode_12.setNodeType(1);
treeNode_12.setNodeValue(null);
treeNode_12.setRuleKey("userAge");
treeNode_12.setRuleDesc("用户年龄");

// 链接:12->121
TreeNodeLink treeNodeLink_121 = new TreeNodeLink();
treeNodeLink_121.setNodeIdFrom(12L);
treeNodeLink_121.setNodeIdTo(121L);
treeNodeLink_121.setRuleLimitType(3);
treeNodeLink_121.setRuleLimitValue("25");

// 链接:12->122
TreeNodeLink treeNodeLink_122 = new TreeNodeLink();
treeNodeLink_122.setNodeIdFrom(12L);
treeNodeLink_122.setNodeIdTo(122L);
treeNodeLink_122.setRuleLimitType(5);
treeNodeLink_122.setRuleLimitValue("25");

List<TreeNodeLink> treeNodeLinkList_12 = new ArrayList<>();
treeNodeLinkList_12.add(treeNodeLink_121);
treeNodeLinkList_12.add(treeNodeLink_122);

treeNode_12.setTreeNodeLinkList(treeNodeLinkList_12);

// 节点:111
TreeNode treeNode_111 = new TreeNode();
treeNode_111.setTreeId(10001L);
treeNode_111.setTreeNodeId(111L);
treeNode_111.setNodeType(2);
treeNode_111.setNodeValue("果实A");

// 节点:112
TreeNode treeNode_112 = new TreeNode();
treeNode_112.setTreeId(10001L);
treeNode_112.setTreeNodeId(112L);
treeNode_112.setNodeType(2);
treeNode_112.setNodeValue("果实B");

// 节点:121
TreeNode treeNode_121 = new TreeNode();
treeNode_121.setTreeId(10001L);
treeNode_121.setTreeNodeId(121L);
treeNode_121.setNodeType(2);
treeNode_121.setNodeValue("果实C");

// 节点:122
TreeNode treeNode_122 = new TreeNode();
treeNode_122.setTreeId(10001L);
treeNode_122.setTreeNodeId(122L);
treeNode_122.setNodeType(2);
treeNode_122.setNodeValue("果实D");

// 树根
TreeRoot treeRoot = new TreeRoot();
treeRoot.setTreeId(10001L);
treeRoot.setTreeRootNodeId(1L);
treeRoot.setTreeName("规则决策树");

Map<Long, TreeNode> treeNodeMap = new HashMap<>();
treeNodeMap.put(1L, treeNode_01);
treeNodeMap.put(11L, treeNode_11);
treeNodeMap.put(12L, treeNode_12);
treeNodeMap.put(111L, treeNode_111);
treeNodeMap.put(112L, treeNode_112);
treeNodeMap.put(121L, treeNode_121);
treeNodeMap.put(122L, treeNode_122);

treeRich = new TreeRich(treeRoot, treeNodeMap);
}

@Test
public void test_tree() {
logger.info("决策树组合结构信息:\r\n" + JSON.toJSONString(treeRich));

IEngine treeEngineHandle = new TreeEngineHandle();

/**
* 测试数据
* 果实A:gender=man、age=22
* 果实B:gender=man、age=29
* 果实C:gender=woman、age=22
* 果实D:gender=woman、age=29
*/
Map<String, String> decisionMatter = new HashMap<>();
decisionMatter.put("gender", "man");
decisionMatter.put("age", "29");

EngineResult result = treeEngineHandle.process(10001L, "Oli09pLkdjh", treeRich, decisionMatter);
logger.info("测试结果:{}", JSON.toJSONString(result));

}
}

装饰器模式

在不改变现有对象结构的情况下,动态地给该对象增加一些职责(即增加其额外功能)的模式,它属于对象结构型模式

  • SSO单点登录功能扩展,增加拦截用户访问方法范围场景
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public abstract class SsoDecorator implements HandlerInterceptor{

private HandlerInterceptor handlerInterceptor;

private SsoDecorator(){}

public SsoDecorator(HandlerInterceptor handlerInterceptor) {
this.handlerInterceptor = handlerInterceptor;
}

public boolean preHandle(String request, String response, Object handler) {
return handlerInterceptor.preHandle(request, response, handler);
}
}
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
public class LoginSsoDecorator extends SsoDecorator {

private Logger logger = LoggerFactory.getLogger(LoginSsoDecorator.class);

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

static {
authMap.put("huahua", "queryUserInfo");
authMap.put("doudou", "queryUserInfo");
}

public LoginSsoDecorator(HandlerInterceptor handlerInterceptor) {
super(handlerInterceptor);
}

@Override
public boolean preHandle(String request, String response, Object handler) {
boolean success = super.preHandle(request, response, handler);
if (!success) return false;
String userId = request.substring(8);
String method = authMap.get(userId);
logger.info("模拟单点登录方法访问拦截校验:{} {}", userId, method);
// 模拟方法校验
return "queryUserInfo".equals(method);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
/*
* 测试
*/
public class ApiTest {

@Test
public void test_LoginSsoDecorator() {
LoginSsoDecorator ssoDecorator = new LoginSsoDecorator(new SsoInterceptor());
String request = "1successhuahua";
boolean success = ssoDecorator.preHandle(request, "ewcdqwt40liuiu", "t");
System.out.println("登录校验:" + request + (success ? " 放行" : " 拦截"));
}
}

外观模式

隐藏系统的复杂性,并向客户端提供一个客户端可以访问系统的接口。这种类型的设计模式属于结构型模式,它向现有的系统添加一个接口,来隐藏系统的复杂性

  • 基于SpringBoot开发门面模式中间件,统一控制接口白名单场景

切面处理白名单

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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
/**
* 切面定义
*/
@Component
public class DoJoinPoint {

private Logger logger = LoggerFactory.getLogger(DoJoinPoint.class);

@Autowired
private StarterService starterService;

@Pointcut("@annotation(cn.bugstack.design.door.annotation.DoDoor)")
public void aopPoint() {

}

@Around("aopPoint()")
public Object doRouter(ProceedingJoinPoint jp) throws Throwable {
Method method = getMethod(jp);
DoDoor door = method.getAnnotation(DoDoor.class);

String keyValue = getFiledValue(door.key(), jp.getArgs());
logger.info("itstack door handler method:{} value:{}", method.getName(), keyValue);
if (null == keyValue || "".equals(keyValue)) return jp.proceed();

String[] split = starterService.split(",");

// 白名单过滤
for (String str : split) {
if (keyValue.equals(str)) {
return jp.proceed();
}
}
// 拦截
return returnObject(door, method);
}

private Method getMethod(JoinPoint jp) throws NoSuchMethodException {
Signature sig = jp.getSignature();
MethodSignature methodSignature = (MethodSignature) sig;
return getClass(jp).getMethod(methodSignature.getName(), methodSignature.getParameterTypes());
}

private Class<? extends Object> getClass(JoinPoint jp) throws NoSuchMethodException {
return jp.getTarget().getClass();
}

//返回对象
private Object returnObject(DoDoor doGate, Method method) throws IllegalAccessException, InstantiationException {
Class<?> returnType = method.getReturnType();
String returnJson = doGate.returnJson();
if ("".equals(returnJson)) {
return returnType.newInstance();
}
return JSON.parseObject(returnJson, returnType);
}

//获取属性值
private String getFiledValue(String filed, Object[] args) {
String filedValue = null;
for (Object arg : args) {
try {
if (null == filedValue || "".equals(filedValue)) {
filedValue = BeanUtils.getProperty(arg, filed);
} else {
break;
}
} catch (Exception e) {
if (args.length == 1) {
return args[0].toString();
}
}
}
return filedValue;
}
}

门面自定义注解

1
2
3
4
5
6
7
8
9
10
11
12
/**
* 注解
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface DoDoor {

String key() default "";

String returnJson() default "";

}

获取白名单配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* 切面定义
*/
@Configuration
@ConditionalOnClass(StarterService.class)
@EnableConfigurationProperties(StarterServiceProperties.class)
public class StarterAutoConfigure {

@Autowired
private StarterServiceProperties properties;

@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(prefix = "itstack.door", value = "enabled", havingValue = "true")
StarterService starterService() {
return new StarterService(properties.getUserStr());
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* 切面定义
*
* itstack:
* door:
* enabled: true
* userStr: 1001,aaaa,ccc #白名单用户ID,多个逗号隔开
*/
public class StarterService {

private String userStr;

public StarterService(String userStr) {
this.userStr = userStr;
}

public String[] split(String separatorChar) {
return StringUtils.split(this.userStr, separatorChar);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* 切面定义
*/
@ConfigurationProperties("itstack.door")
public class StarterServiceProperties {

private String userStr;

public String getUserStr() {
return userStr;
}

public void setUserStr(String userStr) {
this.userStr = userStr;
}
}

享元模式

主要用于减少创建对象的数量,以减少内存占用和提高性能。这种类型的设计模式属于结构型模式,它提供了减少对象数量从而改善应用所需的对象结构的方式

  • 基于Redis秒杀,提供活动与库存信息查询场景

定义基本信息

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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
public class Activity {

/** 活动ID */
private Long id;
/** 活动名称 */
private String name;
/** 活动描述 */
private String desc;
/** 开始时间 */
private Date startTime;
/** 结束时间 */
private Date stopTime;
/** 活动库存 */
private Stock stock;

public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getDesc() {
return desc;
}

public void setDesc(String desc) {
this.desc = desc;
}

public Date getStartTime() {
return startTime;
}

public void setStartTime(Date startTime) {
this.startTime = startTime;
}

public Date getStopTime() {
return stopTime;
}

public void setStopTime(Date stopTime) {
this.stopTime = stopTime;
}

public Stock getStock() {
return stock;
}

public void setStock(Stock stock) {
this.stock = stock;
}
}
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
public class Stock {

/** 库存总量 */
private int total;
/** 库存已用 */
private int used;

public Stock(int total, int used) {
this.total = total;
this.used = used;
}

public int getTotal() {
return total;
}

public void setTotal(int total) {
this.total = total;
}

public int getUsed() {
return used;
}

public void setUsed(int used) {
this.used = used;
}
}

模拟Redis

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class RedisUtils {

private ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);

private AtomicInteger stock = new AtomicInteger(0);

public RedisUtils() {
scheduledExecutorService.scheduleAtFixedRate(() -> {
// 模拟库存消耗
stock.addAndGet(1);
}, 0, 100000, TimeUnit.MICROSECONDS);

}

public int getStockUsed() {
return stock.get();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class ActivityFactory {

static Map<Long, Activity> activityMap = new HashMap<>();

public static Activity getActivity(Long id){
Activity activity = activityMap.get(id);
if (null == activity) {
// 模拟从实际业务应用从接口中获取活动信息
activity = new Activity();
activity.setId(10001L);
activity.setName("图书嗨乐");
activity.setDesc("图书优惠券分享激励分享活动第二期");
activity.setStartTime(new Date());
activity.setStopTime(new Date());
activityMap.put(id, activity);
}
return activity;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
public class ActivityController {

private RedisUtils redisUtils = new RedisUtils();

public Activity queryActivityInfo(Long id) {
Activity activity = ActivityFactory.getActivity(id);
// 模拟从Redis中获取库存变化信息
Stock stock = new Stock(1000, redisUtils.getStockUsed());
activity.setStock(stock);
return activity;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/*
* 测试
*/
public class ApiTest {

private Logger logger = LoggerFactory.getLogger(ApiTest.class);

private ActivityController activityController = new ActivityController();

@Test
public void test_queryActivityInfo() throws InterruptedException {
for (int idx = 0; idx < 10; idx++) {
Long req = 10001L;
Activity activity = activityController.queryActivityInfo(req);
logger.info("测试结果:{} {}", req, JSON.toJSONString(activity));
Thread.sleep(1200);
}
}
}

代理模式

一个类代表另一个类的功能。这种类型的设计模式属于结构型模式。在代理模式中,创建具有现有对象的对象,以便向外界提供功能接口

  • 模拟mybatis-spring中定义的DAO接口,使用代理类方式操作数据库原理实现场景
1
2
3
4
5
6
public interface IUserDao {

@Select("select userName from user where id = #{uId}")
String queryUserInfo(String uId);

}
1
2
3
4
5
6
7
8
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Select {

String value() default "";

}
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 class MapperFactoryBean<T> implements FactoryBean<T> {

private Logger logger = LoggerFactory.getLogger(MapperFactoryBean.class);

private Class<T> mapperInterface;

public MapperFactoryBean(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
}

@Override
public T getObject() throws Exception {
InvocationHandler handler = (proxy, method, args) -> {
Select select = method.getAnnotation(Select.class);
logger.info("SQL:{}", select.value().replace("#{uId}", args[0].toString()));
return args[0] + " 沉淀、分享、成长,让自己和他人都能有所收获!";
};
return (T) Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class[]{mapperInterface}, handler);
}

@Override
public Class<?> getObjectType() {
return mapperInterface;
}

@Override
public boolean isSingleton() {
return false;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class RegisterBeanFactory implements BeanDefinitionRegistryPostProcessor {

@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClass(MapperFactoryBean.class);
beanDefinition.setScope("singleton");
beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(IUserDao.class);

BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(beanDefinition, "userDao");
BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, beanDefinitionRegistry);
}

@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
// left
}
}

责任链模式

为了避免请求发送者与多个请求处理者耦合在一起,于是将所有请求的处理者通过前一对象记住其下一个对象的引用而连成一条链;当有请求发生时,可将请求沿着这条链传递,直到有对象处理它为止

  • 模拟618电商大促期间,项目上线流程多级负责人审批场景
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
/**
* 责任链审核抽象类
*/
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;
protected String levelUserName;
protected AuthLink next;

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

public AuthLink getNext() {
return next;
}

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

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

}
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
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);
}
}

public String getCode() {
return code;
}

public void setCode(String code) {
this.code = code;
}

public String getInfo() {
return info;
}

public void setInfo(String info) {
this.info = info;
}
}

实现类

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
/**
* 一级负责人
*/
public class Level1AuthLink 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 Level1AuthLink(String levelUserId, String levelUserName) throws ParseException {
super(levelUserId, levelUserName);
}

@Override
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.getNext();
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);
}
}
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
/**
* 二级负责人
*/
public class Level2AuthLink 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 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.getNext();
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);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* 三级负责人
*/
public class Level3AuthLink extends AuthLink {

public Level3AuthLink(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.getNext();
if (null == next) {
return new AuthInfo("0000", "单号:", orderId, " 状态:一级审批完成负责人", " 时间:", f.format(date), " 审批人:", levelUserName);
}

return next.doAuth(uId, orderId, authDate);
}
}
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
/**
* 测试
*/
public class ApiTest {

private Logger logger = LoggerFactory.getLogger(ApiTest.class);

@org.junit.Test
public void test_AuthLink() throws ParseException {

AuthLink authLink = new Level3AuthLink("1000013", "王工")
.appendNext(new Level2AuthLink("1000012", "张经理")
.appendNext(new Level1AuthLink("1000011", "段总")));

SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date currentDate = f.parse("2020-06-18 23:49:46");

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

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

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

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

命令模式

将一个请求封装为一个对象,使发出请求的责任和执行请求过程分隔开。这样两者之间通过命令对象进行沟通,便于将命令对象进行储存、传递、调用、增加与管理

  • 模拟高档餐厅八大菜系,小二点单厨师烹饪场景
1
2
3
4
5
6
7
8
/**
* 烹饪接口
*/
public interface ICook {

void doCooking();

}
1
2
3
4
5
6
7
8
9
public class GuangDongCook implements ICook {

private Logger logger = LoggerFactory.getLogger(ICook.class);

@Override
public void doCooking() {
logger.info("广东厨师,烹饪鲁菜,宫廷菜系,以孔府风味为龙头");
}
}
1
2
3
4
5
6
7
8
public class JiangSuCook implements ICook {

private Logger logger = LoggerFactory.getLogger(ICook.class);

public void doCooking() {
logger.info("江苏厨师,烹饪苏菜,宫廷菜系,古今国宴上最受人欢迎的菜系。");
}
}
1
2
3
4
5
6
7
8
public class ShanDongCook implements ICook {

private Logger logger = LoggerFactory.getLogger(ICook.class);

public void doCooking() {
logger.info("山东厨师,烹饪鲁菜,宫廷菜系,以孔府风味为龙头");
}
}
1
2
3
4
5
6
7
8
public class SiChuanCook implements ICook {

private Logger logger = LoggerFactory.getLogger(ICook.class);

public void doCooking() {
logger.info("四川厨师,烹饪川菜,中国最有特色的菜系,也是民间喜欢的菜系。");
}
}
1
2
3
4
5
6
7
8
9
10
11
/**
* 菜系,菜系:山东(鲁菜) 四川(川菜) 江苏(苏菜) 广东(粤菜) 福建(闽菜) 浙江(浙菜) 湖南(湘菜) 安徽(徽菜)
*/
public interface ICuisine {

/**
* 烹调、制作
*/
void cook();

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* 广东(粤菜)
*/
public class GuangDoneCuisine implements ICuisine {

private ICook cook;

private GuangDoneCuisine() {
}

public GuangDoneCuisine(ICook cook) {
this.cook = cook;
}

@Override
public void cook() {
cook.doCooking();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* 江苏(苏菜)
*/
public class JiangSuCuisine implements ICuisine {

private ICook cook;

public JiangSuCuisine(ICook cook) {
this.cook = cook;
}

public void cook() {
cook.doCooking();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* 山东(鲁菜)
*/
public class ShanDongCuisine implements ICuisine {

private ICook cook;

public ShanDongCuisine(ICook cook) {
this.cook = cook;
}

public void cook() {
cook.doCooking();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* 四川(川菜)
*/
public class SiChuanCuisine implements ICuisine {

private ICook cook;

public SiChuanCuisine(ICook cook) {
this.cook = cook;
}

public void cook() {
cook.doCooking();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* 负责点餐
*/
public class XiaoEr {

private Logger logger = LoggerFactory.getLogger(XiaoEr.class);

private List<ICuisine> cuisineList = new ArrayList<ICuisine>();

public void order(ICuisine cuisine) {
cuisineList.add(cuisine);
}

public synchronized void placeOrder() {
for (ICuisine cuisine : cuisineList) {
cuisine.cook();
}
cuisineList.clear();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class ApiTest {

@Test
public void test_xiaoEr(){
// 菜系 + 厨师;广东(粤菜)、江苏(苏菜)、山东(鲁菜)、四川(川菜)
ICuisine guangDoneCuisine = new GuangDoneCuisine(new GuangDongCook());
JiangSuCuisine jiangSuCuisine = new JiangSuCuisine(new JiangSuCook());
ShanDongCuisine shanDongCuisine = new ShanDongCuisine(new ShanDongCook());
SiChuanCuisine siChuanCuisine = new SiChuanCuisine(new SiChuanCook());

// 点单
XiaoEr xiaoEr = new XiaoEr();
xiaoEr.order(guangDoneCuisine);
xiaoEr.order(jiangSuCuisine);
xiaoEr.order(shanDongCuisine);
xiaoEr.order(siChuanCuisine);

xiaoEr.placeOrder();
}
}

迭代器模式

提供一种方法顺序访问一个聚合对象中的各种元素,而又不暴露该对象的内部表示

  • 模拟公司组织架构树结构关系,深度迭代遍历人员信息输出场景
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
38
39
40
41
42
43
44
45
46
47
/**
* 雇员
*/
public class Employee {

// ID
private String uId;
// 姓名
private String name;
// 备注
private String desc;

public Employee(String uId, String name) {
this.uId = uId;
this.name = name;
}

public Employee(String uId, String name, String desc) {
this.uId = uId;
this.name = name;
this.desc = desc;
}

public String getuId() {
return uId;
}

public void setuId(String uId) {
this.uId = uId;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getDesc() {
return desc;
}

public void setDesc(String desc) {
this.desc = desc;
}
}

定义接口

1
2
3
4
5
6
7
public interface Iterator<E> {

boolean hasNext();

E next();

}
1
2
3
4
5
public interface Iterable<E> {

Iterator<E> iterator();

}
1
2
3
4
5
6
7
8
9
10
11
12
13
public interface Collection<E, T> extends Iterable {

boolean add(E e);

boolean remove(E e);

boolean addLink(String key, T t);

boolean removeLink(String key);

Iterator<E> iterator();

}

链路

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
public class Link {

private String fromId;
private String toId;

public Link(String fromId, String toId) {
this.fromId = fromId;
this.toId = toId;
}

public String getFromId() {
return fromId;
}

public void setFromId(String fromId) {
this.fromId = fromId;
}

public String getToId() {
return toId;
}

public void setToId(String toId) {
this.toId = toId;
}
}
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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
/**
* 遍历操作
*/
public class GroupStructure implements Collection<Employee, Link> {

private String groupId;
private String groupName;

private Map<String, Employee> employeeMap = new ConcurrentHashMap<>();
private Map<String, List<Link>> linkMap = new ConcurrentHashMap<>();
private Map<String, String> invertedMap = new ConcurrentHashMap<>();

public GroupStructure(String groupId, String groupName) {
this.groupId = groupId;
this.groupName = groupName;
}

@Override
public boolean add(Employee employee) {
return null != employeeMap.put(employee.getuId(), employee);
}

@Override
public boolean remove(Employee employee) {
return null != employeeMap.remove(employee.getuId());
}

@Override
public boolean addLink(String key, Link link) {
invertedMap.put(link.getToId(), link.getFromId());
if (linkMap.containsKey(key)){
return linkMap.get(key).add(link);
} else {
LinkedList<Link> links = new LinkedList<>();
links.add(link);
linkMap.put(key, links);
return true;
}
}

@Override
public boolean removeLink(String key) {
return null != linkMap.remove(key);
}

@Override
public Iterator<Employee> iterator() {

return new Iterator<Employee>() {

HashMap<String, Integer> keyMap = new HashMap<>();

int totalIdx = 0;
private String fromId = groupId;
private String toId = groupId;

@Override
public boolean hasNext() {
return totalIdx < employeeMap.size();
}

@Override
public Employee next() {

List<Link> links = linkMap.get(toId);
int cursorIdx = getCursorIdx(toId);

// 同级节点扫描
if (null == links){
cursorIdx = getCursorIdx(fromId);
links = linkMap.get(fromId);
}

// 上级节点扫描
while (cursorIdx > links.size() - 1){
fromId = invertedMap.get(fromId);
cursorIdx = getCursorIdx(fromId);
links = linkMap.get(fromId);
}

// 获取节点
Link link = links.get(cursorIdx);
toId = link.getToId();
fromId = link.getFromId();
totalIdx++;

return employeeMap.get(link.getToId());
}

public int getCursorIdx(String key) {
int idx = 0;
if (keyMap.containsKey(key)) {
idx = keyMap.get(key);
keyMap.put(key, ++idx);
} else {
keyMap.put(key, idx);
}
return idx;
}
};
}
}
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
/**
* 遍历测试
*/
public class ApiTest {

private Logger logger = LoggerFactory.getLogger(ApiTest.class);

@Test
public void test_iterator() {
GroupStructure groupStructure = new GroupStructure("1", "小傅哥");
groupStructure.add(new Employee("2", "花花", "二级部门"));
groupStructure.add(new Employee("3", "豆包", "二级部门"));
groupStructure.add(new Employee("4", "蹦蹦", "三级部门"));
groupStructure.add(new Employee("5", "大烧", "三级部门"));
groupStructure.add(new Employee("6", "虎哥", "四级部门"));
groupStructure.add(new Employee("7", "玲姐", "四级部门"));
groupStructure.add(new Employee("8", "秋雅", "四级部门"));

groupStructure.addLink("1", new Link("1", "2"));
groupStructure.addLink("1", new Link("1", "3"));

groupStructure.addLink("2", new Link("2", "4"));
groupStructure.addLink("2", new Link("2", "5"));

groupStructure.addLink("5", new Link("5", "6"));
groupStructure.addLink("5", new Link("5", "7"));
groupStructure.addLink("5", new Link("5", "8"));

Iterator<Employee> iterator = groupStructure.iterator();
while (iterator.hasNext()) {
Employee employee = iterator.next();
logger.info("{} 雇员 ID:{} Name:{}", employee.getDesc(), employee.getuId(), employee.getName());
}
}
}

中介者模式

定义一个中介对象来封装一系列对象之间的交互,使原有对象之间的耦合松散,且可以独立地改变它们之间的交互。中介者模式又叫调停模式,它是迪米特法则的典型应用

  • 按照Mybatis原理手写ORM框架,给JDBC方式操作数据库增加中介者场景

po

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
38
39
40
41
42
43
44
45
46
47
48
public class School {

private Long id;
private String name;
private String address;
private Date createTime;
private Date updateTime;

public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getAddress() {
return address;
}

public void setAddress(String address) {
this.address = address;
}

public Date getCreateTime() {
return createTime;
}

public void setCreateTime(Date createTime) {
this.createTime = createTime;
}

public Date getUpdateTime() {
return updateTime;
}

public void setUpdateTime(Date updateTime) {
this.updateTime = updateTime;
}
}
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
38
39
40
41
42
43
44
45
46
47
48
public class User {

private Long id;
private String name;
private Integer age;
private Date createTime;
private Date updateTime;

public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public Integer getAge() {
return age;
}

public void setAge(Integer age) {
this.age = age;
}

public Date getCreateTime() {
return createTime;
}

public void setCreateTime(Date createTime) {
this.createTime = createTime;
}

public Date getUpdateTime() {
return updateTime;
}

public void setUpdateTime(Date updateTime) {
this.updateTime = updateTime;
}
}

dao

1
2
3
4
5
public interface ISchoolDao {

School querySchoolInfoById(Long treeId);

}
1
2
3
4
5
public interface IUserDao {

User queryUserInfoById(Long id);

}

ORM框架

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* 配置类
*/
public class Configuration {

protected Connection connection;
protected Map<String, String> dataSource;
protected Map<String, XNode> mapperElement;

public void setConnection(Connection connection) {
this.connection = connection;
}

public void setDataSource(Map<String, String> dataSource) {
this.dataSource = dataSource;
}

public void setMapperElement(Map<String, XNode> mapperElement) {
this.mapperElement = mapperElement;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
public interface SqlSession {

<T> T selectOne(String statement);

<T> T selectOne(String statement, Object parameter);

<T> List<T> selectList(String statement);

<T> List<T> selectList(String statement, Object parameter);

void close();
}
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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
public class DefaultSqlSession implements SqlSession {

private Connection connection;
private Map<String, XNode> mapperElement;

public DefaultSqlSession(Connection connection, Map<String, XNode> mapperElement) {
this.connection = connection;
this.mapperElement = mapperElement;
}

@Override
public <T> T selectOne(String statement) {
try {
XNode xNode = mapperElement.get(statement);
PreparedStatement preparedStatement = connection.prepareStatement(xNode.getSql());
ResultSet resultSet = preparedStatement.executeQuery();
List<T> objects = resultSet2Obj(resultSet, Class.forName(xNode.getResultType()));
return objects.get(0);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}

@Override
public <T> T selectOne(String statement, Object parameter) {
XNode xNode = mapperElement.get(statement);
Map<Integer, String> parameterMap = xNode.getParameter();
try {
PreparedStatement preparedStatement = connection.prepareStatement(xNode.getSql());
buildParameter(preparedStatement, parameter, parameterMap);
ResultSet resultSet = preparedStatement.executeQuery();
List<T> objects = resultSet2Obj(resultSet, Class.forName(xNode.getResultType()));
return objects.get(0);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}

@Override
public <T> List<T> selectList(String statement) {
XNode xNode = mapperElement.get(statement);
try {
PreparedStatement preparedStatement = connection.prepareStatement(xNode.getSql());
ResultSet resultSet = preparedStatement.executeQuery();
return resultSet2Obj(resultSet, Class.forName(xNode.getResultType()));
} catch (Exception e) {
e.printStackTrace();
}
return null;
}

@Override
public <T> List<T> selectList(String statement, Object parameter) {
XNode xNode = mapperElement.get(statement);
Map<Integer, String> parameterMap = xNode.getParameter();
try {
PreparedStatement preparedStatement = connection.prepareStatement(xNode.getSql());
buildParameter(preparedStatement, parameter, parameterMap);
ResultSet resultSet = preparedStatement.executeQuery();
return resultSet2Obj(resultSet, Class.forName(xNode.getResultType()));
} catch (Exception e) {
e.printStackTrace();
}
return null;
}

private void buildParameter(PreparedStatement preparedStatement, Object parameter, Map<Integer, String> parameterMap) throws SQLException, IllegalAccessException {

int size = parameterMap.size();
// 单个参数
if (parameter instanceof Long) {
for (int i = 1; i <= size; i++) {
preparedStatement.setLong(i, Long.parseLong(parameter.toString()));
}
return;
}

if (parameter instanceof Integer) {
for (int i = 1; i <= size; i++) {
preparedStatement.setInt(i, Integer.parseInt(parameter.toString()));
}
return;
}

if (parameter instanceof String) {
for (int i = 1; i <= size; i++) {
preparedStatement.setString(i, parameter.toString());
}
return;
}

Map<String, Object> fieldMap = new HashMap<>();
// 对象参数
Field[] declaredFields = parameter.getClass().getDeclaredFields();
for (Field field : declaredFields) {
String name = field.getName();
field.setAccessible(true);
Object obj = field.get(parameter);
field.setAccessible(false);
fieldMap.put(name, obj);
}

for (int i = 1; i <= size; i++) {
String parameterDefine = parameterMap.get(i);
Object obj = fieldMap.get(parameterDefine);

if (obj instanceof Short) {
preparedStatement.setShort(i, Short.parseShort(obj.toString()));
continue;
}

if (obj instanceof Integer) {
preparedStatement.setInt(i, Integer.parseInt(obj.toString()));
continue;
}

if (obj instanceof Long) {
preparedStatement.setLong(i, Long.parseLong(obj.toString()));
continue;
}

if (obj instanceof String) {
preparedStatement.setString(i, obj.toString());
continue;
}

if (obj instanceof Date) {
preparedStatement.setDate(i, (java.sql.Date) obj);
}

}

}

private <T> List<T> resultSet2Obj(ResultSet resultSet, Class<?> clazz) {
List<T> list = new ArrayList<>();
try {
ResultSetMetaData metaData = resultSet.getMetaData();
int columnCount = metaData.getColumnCount();
// 每次遍历行值
while (resultSet.next()) {
T obj = (T) clazz.newInstance();
for (int i = 1; i <= columnCount; i++) {
Object value = resultSet.getObject(i);
String columnName = metaData.getColumnName(i);
String setMethod = "set" + columnName.substring(0, 1).toUpperCase() + columnName.substring(1);
Method method;
if (value instanceof Timestamp) {
method = clazz.getMethod(setMethod, Date.class);
} else {
method = clazz.getMethod(setMethod, value.getClass());
}
method.invoke(obj, value);
}
list.add(obj);
}
} catch (Exception e) {
e.printStackTrace();
}
return list;
}

@Override
public void close() {
if (null == connection) return;
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
1
2
3
4
5
public interface SqlSessionFactory {

SqlSession openSession();

}
1
2
3
4
5
6
7
8
9
10
11
12
13
public class DefaultSqlSessionFactory implements SqlSessionFactory {

private final Configuration configuration;

public DefaultSqlSessionFactory(Configuration configuration) {
this.configuration = configuration;
}

@Override
public SqlSession openSession() {
return new DefaultSqlSession(configuration.connection, configuration.mapperElement);
}
}

构建者

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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
public class SqlSessionFactoryBuilder {

public DefaultSqlSessionFactory build(Reader reader) {
SAXReader saxReader = new SAXReader();
try {
saxReader.setEntityResolver(new XMLMapperEntityResolver());
Document document = saxReader.read(new InputSource(reader));
Configuration configuration = parseConfiguration(document.getRootElement());
return new DefaultSqlSessionFactory(configuration);
} catch (DocumentException e) {
e.printStackTrace();
}
return null;
}

private Configuration parseConfiguration(Element root) {
Configuration configuration = new Configuration();
configuration.setDataSource(dataSource(root.selectNodes("//dataSource")));
configuration.setConnection(connection(configuration.dataSource));
configuration.setMapperElement(mapperElement(root.selectNodes("mappers")));
return configuration;
}

// 获取数据源配置信息
private Map<String, String> dataSource(List<Element> list) {
Map<String, String> dataSource = new HashMap<>(4);
Element element = list.get(0);
List content = element.content();
for (Object o : content) {
Element e = (Element) o;
String name = e.attributeValue("name");
String value = e.attributeValue("value");
dataSource.put(name, value);
}
return dataSource;
}

private Connection connection(Map<String, String> dataSource) {
try {
Class.forName(dataSource.get("driver"));
return DriverManager.getConnection(dataSource.get("url"), dataSource.get("username"), dataSource.get("password"));
} catch (ClassNotFoundException | SQLException e) {
e.printStackTrace();
}
return null;
}

// 获取SQL语句信息
private Map<String, XNode> mapperElement(List<Element> list) {
Map<String, XNode> map = new HashMap<>();

Element element = list.get(0);
List content = element.content();
for (Object o : content) {
Element e = (Element) o;
String resource = e.attributeValue("resource");

try {
Reader reader = Resources.getResourceAsReader(resource);
SAXReader saxReader = new SAXReader();
Document document = saxReader.read(new InputSource(reader));
Element root = document.getRootElement();
//命名空间
String namespace = root.attributeValue("namespace");

// SELECT
List<Element> selectNodes = root.selectNodes("select");
for (Element node : selectNodes) {
String id = node.attributeValue("id");
String parameterType = node.attributeValue("parameterType");
String resultType = node.attributeValue("resultType");
String sql = node.getText();

// ? 匹配
Map<Integer, String> parameter = new HashMap<>();
Pattern pattern = Pattern.compile("(#\\{(.*?)})");
Matcher matcher = pattern.matcher(sql);
for (int i = 1; matcher.find(); i++) {
String g1 = matcher.group(1);
String g2 = matcher.group(2);
parameter.put(i, g2);
sql = sql.replace(g1, "?");
}

XNode xNode = new XNode();
xNode.setNamespace(namespace);
xNode.setId(id);
xNode.setParameterType(parameterType);
xNode.setResultType(resultType);
xNode.setSql(sql);
xNode.setParameter(parameter);

map.put(namespace + "." + id, xNode);
}
} catch (Exception ex) {
ex.printStackTrace();
}

}
return map;
}
}
1
2
3
4
5
6
7
8
9
10
11
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.bugstack.design.dao.ISchoolDao">

<select id="querySchoolInfoById" resultType="cn.bugstack.design.po.School">
SELECT id, name, address, createTime, updateTime
FROM school
where id = #{id}
</select>

</mapper>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.bugstack.design.dao.IUserDao">

<select id="queryUserInfoById" parameterType="java.lang.Long" resultType="cn.bugstack.design.po.User">
SELECT id, name, age, createTime, updateTime
FROM user
where id = #{id}
</select>

<select id="queryUserList" parameterType="cn.bugstack.design.po.User" resultType="cn.bugstack.design.po.User">
SELECT id, name, age, createTime, updateTime
FROM user
where age = #{age}
</select>

</mapper>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class Resources {

public static Reader getResourceAsReader(String resource) throws IOException {
return new InputStreamReader(getResourceAsStream(resource));
}

private static InputStream getResourceAsStream(String resource) throws IOException {
ClassLoader[] classLoaders = getClassLoaders();
for (ClassLoader classLoader : classLoaders) {
InputStream inputStream = classLoader.getResourceAsStream(resource);
if (null != inputStream) {
return inputStream;
}
}
throw new IOException("Could not find resource " + resource);
}

private static ClassLoader[] getClassLoaders() {
return new ClassLoader[]{
ClassLoader.getSystemClassLoader(),
Thread.currentThread().getContextClassLoader()};
}
}
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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
public class XNode {

private String namespace;
private String id;
private String parameterType;
private String resultType;
private String sql;
private Map<Integer, String> parameter;

public String getNamespace() {
return namespace;
}

public void setNamespace(String namespace) {
this.namespace = namespace;
}

public String getId() {
return id;
}

public void setId(String id) {
this.id = id;
}

public String getParameterType() {
return parameterType;
}

public void setParameterType(String parameterType) {
this.parameterType = parameterType;
}

public String getResultType() {
return resultType;
}

public void setResultType(String resultType) {
this.resultType = resultType;
}

public String getSql() {
return sql;
}

public void setSql(String sql) {
this.sql = sql;
}

public Map<Integer, String> getParameter() {
return parameter;
}

public void setParameter(Map<Integer, String> parameter) {
this.parameter = parameter;
}
}

测试

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
38
39
40
41
42
43
44
45
46
47
48
49
public class ApiTest {

private Logger logger = LoggerFactory.getLogger(ApiTest.class);

@Test
public void test_queryUserInfoById() {
String resource = "mybatis-config-datasource.xml";
Reader reader;
try {
reader = Resources.getResourceAsReader(resource);
SqlSessionFactory sqlMapper = new SqlSessionFactoryBuilder().build(reader);

SqlSession session = sqlMapper.openSession();
try {
User user = session.selectOne("cn.bugstack.design.dao.IUserDao.queryUserInfoById", 1L);
logger.info("测试结果:{}", JSON.toJSONString(user));
} finally {
session.close();
reader.close();
}
} catch (Exception e) {
e.printStackTrace();
}

}

@Test
public void test_queryUserList() {
String resource = "mybatis-config-datasource.xml";
Reader reader;
try {
reader = Resources.getResourceAsReader(resource);
SqlSessionFactory sqlMapper = new SqlSessionFactoryBuilder().build(reader);

SqlSession session = sqlMapper.openSession();
try {
User req = new User();
req.setAge(18);
List<User> userList = session.selectList("cn.bugstack.design.dao.IUserDao.queryUserList", req);
logger.info("测试结果:{}", JSON.toJSONString(userList));
} finally {
session.close();
reader.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}

备忘录模式

在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便以后当需要时能够将该对象恢复到原先保存的状态

  • 模拟互联网系统上线过程中,配置文件回滚场景
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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
/**
* 配置文件
*/
public class ConfigFile {

// 版本号
private String versionNo;
// 内容
private String content;
// 时间
private Date dateTime;
// 操作人
private String operator;

public ConfigFile(String versionNo, String content, Date dateTime, String operator) {
this.versionNo = versionNo;
this.content = content;
this.dateTime = dateTime;
this.operator = operator;
}

public String getVersionNo() {
return versionNo;
}

public void setVersionNo(String versionNo) {
this.versionNo = versionNo;
}

public String getContent() {
return content;
}

public void setContent(String content) {
this.content = content;
}

public Date getDateTime() {
return dateTime;
}

public void setDateTime(Date dateTime) {
this.dateTime = dateTime;
}

public String getOperator() {
return operator;
}

public void setOperator(String operator) {
this.operator = operator;
}
}
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
/**
* 管理员
*/
public class Admin {

private int cursorIdx = 0;
private List<ConfigMemento> mementoList = new ArrayList<>();
private Map<String, ConfigMemento> mementoMap = new ConcurrentHashMap<>();

public void append(ConfigMemento memento) {
mementoList.add(memento);
mementoMap.put(memento.getConfigFile().getVersionNo(), memento);
cursorIdx++;
}

public ConfigMemento undo() {
if (--cursorIdx <= 0) return mementoList.get(0);
return mementoList.get(cursorIdx);
}

public ConfigMemento redo() {
if (++cursorIdx > mementoList.size()) return mementoList.get(mementoList.size() - 1);
return mementoList.get(cursorIdx);
}

public ConfigMemento get(String versionNo) {
return mementoMap.get(versionNo);
}
}

创建备忘录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* 备忘录
*/
public class ConfigMemento {

private ConfigFile configFile;

public ConfigMemento(ConfigFile configFile) {
this.configFile = configFile;
}

public ConfigFile getConfigFile() {
return configFile;
}

public void setConfigFile(ConfigFile configFile) {
this.configFile = configFile;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
* 记录者
*/
public class ConfigOriginator {

private ConfigFile configFile;

public ConfigFile getConfigFile() {
return configFile;
}

public void setConfigFile(ConfigFile configFile) {
this.configFile = configFile;
}

public ConfigMemento saveMemento(){
return new ConfigMemento(this.configFile);
}

public void getMemento(ConfigMemento configMemento){
this.configFile = configMemento.getConfigFile();
}
}
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
38
39
40
41
42
43
/**
* 单元测试
*/
public class ApiTest {

private Logger logger = LoggerFactory.getLogger(ApiTest.class);

@Test
public void test() {
Admin admin = new Admin();

ConfigOriginator configOriginator = new ConfigOriginator();

configOriginator.setConfigFile(new ConfigFile("1000001", "配置内容A=哈哈", new Date(), "小傅哥"));
admin.append(configOriginator.saveMemento()); // 保存配置

configOriginator.setConfigFile(new ConfigFile("1000002", "配置内容A=嘻嘻", new Date(), "小傅哥"));
admin.append(configOriginator.saveMemento()); // 保存配置

configOriginator.setConfigFile(new ConfigFile("1000003", "配置内容A=么么", new Date(), "小傅哥"));
admin.append(configOriginator.saveMemento()); // 保存配置

configOriginator.setConfigFile(new ConfigFile("1000004", "配置内容A=嘿嘿", new Date(), "小傅哥"));
admin.append(configOriginator.saveMemento()); // 保存配置

// 历史配置(回滚)
configOriginator.getMemento(admin.undo());
logger.info("历史配置(回滚)undo:{}", JSON.toJSONString(configOriginator.getConfigFile()));

// 历史配置(回滚)
configOriginator.getMemento(admin.undo());
logger.info("历史配置(回滚)undo:{}", JSON.toJSONString(configOriginator.getConfigFile()));

// 历史配置(前进)
configOriginator.getMemento(admin.redo());
logger.info("历史配置(前进)redo:{}", JSON.toJSONString(configOriginator.getConfigFile()));

// 历史配置(获取)
configOriginator.getMemento(admin.get("1000002"));
logger.info("历史配置(获取)get:{}", JSON.toJSONString(configOriginator.getConfigFile()));

}
}

观察者模式

指多个对象间存在一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。这种模式有时又称作发布-订阅模式、模型-视图模式,它是对象行为型模式

  • 模型类似小客车指标摇号过程,监听消息通知用户中签场景
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
public class LotteryResult {

private String uId; // 用户ID
private String msg; // 摇号信息
private Date dateTime; // 业务时间

public LotteryResult(String uId, String msg, Date dateTime) {
this.uId = uId;
this.msg = msg;
this.dateTime = dateTime;
}

public String getuId() {
return uId;
}

public void setuId(String uId) {
this.uId = uId;
}

public String getMsg() {
return msg;
}

public void setMsg(String msg) {
this.msg = msg;
}

public Date getDateTime() {
return dateTime;
}

public void setDateTime(Date dateTime) {
this.dateTime = dateTime;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
* 摇号服务抽象类,也可以创建接口,在用抽象类实现
*/
public abstract class LotteryService {

private EventManager eventManager;

public LotteryService() {
eventManager = new EventManager(EventManager.EventType.MQ, EventManager.EventType.Message);
eventManager.subscribe(EventManager.EventType.MQ, new MQEventListener());
eventManager.subscribe(EventManager.EventType.Message, new MessageEventListener());
}

public LotteryResult draw(String uId) {
LotteryResult lotteryResult = doDraw(uId);
eventManager.notify(EventManager.EventType.MQ, lotteryResult);
eventManager.notify(EventManager.EventType.Message, lotteryResult);
return lotteryResult;
}

protected abstract LotteryResult doDraw(String uId);

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* 摇号服务实现
*/
public class LotteryServiceImpl extends LotteryService{

private MinibusTargetService minibusTargetService = new MinibusTargetService();

@Override
protected LotteryResult doDraw(String uId) {
// 摇号
String lottery = minibusTargetService.lottery(uId);
// 结果
return new LotteryResult(uId, lottery, new Date());
}
}

事件

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
38
39
40
41
42
43
44
45
46
47
48
49
/**
* @description 事件管理者
*/
public class EventManager {

Map<Enum<EventType>, List<EventListener>> listeners = new HashMap<>();

public EventManager(Enum<EventType>... operations) {
for (Enum<EventType> operation : operations) {
this.listeners.put(operation, new ArrayList<>());
}
}

public enum EventType {
MQ, Message
}

/**
* 订阅
* @param eventType 事件类型
* @param listener 监听
*/
public void subscribe(Enum<EventType> eventType, EventListener listener) {
List<EventListener> users = listeners.get(eventType);
users.add(listener);
}

/**
* 取消订阅
* @param eventType 事件类型
* @param listener 监听
*/
public void unsubscribe(Enum<EventType> eventType, EventListener listener) {
List<EventListener> users = listeners.get(eventType);
users.remove(listener);
}

/**
* 通知
* @param eventType 事件类型
* @param result 结果
*/
public void notify(Enum<EventType> eventType, LotteryResult result) {
List<EventListener> users = listeners.get(eventType);
for (EventListener listener : users) {
listener.doEvent(result);
}
}
}

监听

1
2
3
4
5
6
7
8
/**
* 监听接口
*/
public interface EventListener {

void doEvent(LotteryResult result);

}
1
2
3
4
5
6
7
8
9
10
11
12
/**
* 短信通知
*/
public class MessageEventListener implements EventListener {

private Logger logger = LoggerFactory.getLogger(MessageEventListener.class);

@Override
public void doEvent(LotteryResult result) {
logger.info("给用户 {} 发送短信通知(短信):{}", result.getuId(), result.getMsg());
}
}
1
2
3
4
5
6
7
8
9
public class MQEventListener implements EventListener {

private Logger logger = LoggerFactory.getLogger(MQEventListener.class);

@Override
public void doEvent(LotteryResult result) {
logger.info("记录用户 {} 摇号结果(MQ):{}", result.getuId(), result.getMsg());
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* 测试
*/
public class ApiTest {

private Logger logger = LoggerFactory.getLogger(ApiTest.class);

@Test
public void test_draw() {
LotteryService lotteryService = new LotteryServiceImpl();
LotteryResult result = lotteryService.draw("1000000101010019");
logger.info("Result:{}", JSON.toJSONString(result));
}
}

状态模式

对有状态的对象,把复杂的 “判断逻辑” 提取到不同的状态对象中,允许状态对象在其内部状态发生改变时改变其行为

  • 模拟系统营销活动,状态流程审核发布上线场景
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
public class Result {

private String code; // 编码
private String info; // 描述

public Result(String code, String info) {
this.code = code;
this.info = info;
}

public String getCode() {
return code;
}

public void setCode(String code) {
this.code = code;
}

public String getInfo() {
return info;
}

public void setInfo(String info) {
this.info = info;
}
}
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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
public abstract class State {

/**
* 活动提审
*
* @param activityId 活动ID
* @param currentStatus 当前状态
* @return 执行结果
*/
public abstract Result arraignment(String activityId, Enum<Status> currentStatus);

/**
* 审核通过
*
* @param activityId 活动ID
* @param currentStatus 当前状态
* @return 执行结果
*/
public abstract Result checkPass(String activityId, Enum<Status> currentStatus);

/**
* 审核拒绝
*
* @param activityId 活动ID
* @param currentStatus 当前状态
* @return 执行结果
*/
public abstract Result checkRefuse(String activityId, Enum<Status> currentStatus);

/**
* 撤审撤销
*
* @param activityId 活动ID
* @param currentStatus 当前状态
* @return 执行结果
*/
public abstract Result checkRevoke(String activityId, Enum<Status> currentStatus);

/**
* 活动关闭
*
* @param activityId 活动ID
* @param currentStatus 当前状态
* @return 执行结果
*/
public abstract Result close(String activityId, Enum<Status> currentStatus);

/**
* 活动开启
*
* @param activityId 活动ID
* @param currentStatus 当前状态
* @return 执行结果
*/
public abstract Result open(String activityId, Enum<Status> currentStatus);

/**
* 活动执行
*
* @param activityId 活动ID
* @param currentStatus 当前状态
* @return 执行结果
*/
public abstract Result doing(String activityId, Enum<Status> currentStatus);

}

状态

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
38
39
40
/**
* 待审核
*/
public class CheckState extends State {

@Override
public Result arraignment(String activityId, Enum<Status> currentStatus) {
return new Result("0001", "待审核状态不可重复提审");
}

@Override
public Result checkPass(String activityId, Enum<Status> currentStatus) {
ActivityService.execStatus(activityId, currentStatus, Status.Pass);
return new Result("0000", "活动审核通过完成");
}

public Result checkRefuse(String activityId, Enum<Status> currentStatus) {
ActivityService.execStatus(activityId, currentStatus, Status.Refuse);
return new Result("0000", "活动审核拒绝完成");
}

@Override
public Result checkRevoke(String activityId, Enum<Status> currentStatus) {
ActivityService.execStatus(activityId, currentStatus, Status.Editing);
return new Result("0000", "活动审核撤销回到编辑中");
}

public Result close(String activityId, Enum<Status> currentStatus) {
ActivityService.execStatus(activityId, currentStatus, Status.Close);
return new Result("0000", "活动审核关闭完成");
}

public Result open(String activityId, Enum<Status> currentStatus) {
return new Result("0001", "非关闭活动不可开启");
}

public Result doing(String activityId, Enum<Status> currentStatus) {
return new Result("0001", "待审核活动不可执行活动中变更");
}
}
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
/**
* 活动状态;活动关闭
*/
public class CloseState extends State {

public Result arraignment(String activityId, Enum<Status> currentStatus) {
return new Result("0001", "活动关闭不可提审");
}

public Result checkPass(String activityId, Enum<Status> currentStatus) {
return new Result("0001", "活动关闭不可审核通过");
}

public Result checkRefuse(String activityId, Enum<Status> currentStatus) {
return new Result("0001", "活动关闭不可审核拒绝");
}

@Override
public Result checkRevoke(String activityId, Enum<Status> currentStatus) {
return new Result("0001", "活动关闭不可撤销审核");
}

public Result close(String activityId, Enum<Status> currentStatus) {
return new Result("0001", "活动关闭不可重复关闭");
}

public Result open(String activityId, Enum<Status> currentStatus) {
ActivityService.execStatus(activityId, currentStatus, Status.Open);
return new Result("0000", "活动开启完成");
}

public Result doing(String activityId, Enum<Status> currentStatus) {
return new Result("0001", "活动关闭不可变更活动中");
}
}
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
/**
* 活动状态;活动中
*/
public class DoingState extends State {

public Result arraignment(String activityId, Enum<Status> currentStatus) {
return new Result("0001", "活动中不可提审");
}

public Result checkPass(String activityId, Enum<Status> currentStatus) {
return new Result("0001", "活动中不可审核通过");
}

public Result checkRefuse(String activityId, Enum<Status> currentStatus) {
return new Result("0001", "活动中不可审核拒绝");
}

@Override
public Result checkRevoke(String activityId, Enum<Status> currentStatus) {
return new Result("0001", "活动中不可撤销审核");
}

public Result close(String activityId, Enum<Status> currentStatus) {
ActivityService.execStatus(activityId, currentStatus, Status.Close);
return new Result("0000", "活动关闭成功");
}

public Result open(String activityId, Enum<Status> currentStatus) {
return new Result("0001", "活动中不可开启");
}

public Result doing(String activityId, Enum<Status> currentStatus) {
return new Result("0001", "活动中不可重复执行");
}
}
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
/**
* 活动状态;编辑中
*/
public class EditingState extends State {

public Result arraignment(String activityId, Enum<Status> currentStatus) {
ActivityService.execStatus(activityId, currentStatus, Status.Check);
return new Result("0000", "活动提审成功");
}

public Result checkPass(String activityId, Enum<Status> currentStatus) {
return new Result("0001", "编辑中不可审核通过");
}

public Result checkRefuse(String activityId, Enum<Status> currentStatus) {
return new Result("0001", "编辑中不可审核拒绝");
}

@Override
public Result checkRevoke(String activityId, Enum<Status> currentStatus) {
return new Result("0001", "编辑中不可撤销审核");
}

public Result close(String activityId, Enum<Status> currentStatus) {
ActivityService.execStatus(activityId, currentStatus, Status.Close);
return new Result("0000", "活动关闭成功");
}

public Result open(String activityId, Enum<Status> currentStatus) {
return new Result("0001", "非关闭活动不可开启");
}

public Result doing(String activityId, Enum<Status> currentStatus) {
return new Result("0001", "编辑中活动不可执行活动中变更");
}
}
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
/**
* 活动状态;活动开启
*/
public class OpenState extends State {

public Result arraignment(String activityId, Enum<Status> currentStatus) {
return new Result("0001", "活动开启不可提审");
}

public Result checkPass(String activityId, Enum<Status> currentStatus) {
return new Result("0001", "活动开启不可审核通过");
}

public Result checkRefuse(String activityId, Enum<Status> currentStatus) {
return new Result("0001", "活动开启不可审核拒绝");
}

@Override
public Result checkRevoke(String activityId, Enum<Status> currentStatus) {
return new Result("0001", "活动开启不可撤销审核");
}

public Result close(String activityId, Enum<Status> currentStatus) {
ActivityService.execStatus(activityId, currentStatus, Status.Close);
return new Result("0000", "活动关闭完成");
}

public Result open(String activityId, Enum<Status> currentStatus) {
return new Result("0001", "活动不可重复开启");
}

public Result doing(String activityId, Enum<Status> currentStatus) {
ActivityService.execStatus(activityId, currentStatus, Status.Doing);
return new Result("0000", "活动变更活动中完成");
}
}
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 class PassState extends State {

public Result arraignment(String activityId, Enum<Status> currentStatus) {
return new Result("0001", "已审核状态不可重复提审");
}

public Result checkPass(String activityId, Enum<Status> currentStatus) {
return new Result("0001", "已审核状态不可重复审核");
}

public Result checkRefuse(String activityId, Enum<Status> currentStatus) {
ActivityService.execStatus(activityId, currentStatus, Status.Refuse);
return new Result("0000", "活动审核拒绝完成");
}

@Override
public Result checkRevoke(String activityId, Enum<Status> currentStatus) {
return new Result("0001", "审核通过不可撤销(可先拒绝审核)");
}

public Result close(String activityId, Enum<Status> currentStatus) {
ActivityService.execStatus(activityId, currentStatus, Status.Close);
return new Result("0000", "活动审核关闭完成");
}

public Result open(String activityId, Enum<Status> currentStatus) {
return new Result("0001", "非关闭活动不可开启");
}

public Result doing(String activityId, Enum<Status> currentStatus) {
ActivityService.execStatus(activityId, currentStatus, Status.Doing);
return new Result("0000", "活动变更活动中完成");
}
}
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 class RefuseState extends State {

public Result arraignment(String activityId, Enum<Status> currentStatus) {
return new Result("0001", "已审核状态不可重复提审");
}

public Result checkPass(String activityId, Enum<Status> currentStatus) {
return new Result("0001", "已审核状态不可重复审核");
}

public Result checkRefuse(String activityId, Enum<Status> currentStatus) {
ActivityService.execStatus(activityId, currentStatus, Status.Refuse);
return new Result("0000", "活动审核拒绝不可重复审核");
}

@Override
public Result checkRevoke(String activityId, Enum<Status> currentStatus) {
ActivityService.execStatus(activityId, currentStatus, Status.Editing);
return new Result("0000", "撤销审核完成");
}

public Result close(String activityId, Enum<Status> currentStatus) {
ActivityService.execStatus(activityId, currentStatus, Status.Close);
return new Result("0000", "活动审核关闭完成");
}

public Result open(String activityId, Enum<Status> currentStatus) {
return new Result("0001", "非关闭活动不可开启");
}

public Result doing(String activityId, Enum<Status> currentStatus) {
return new Result("0001", "审核拒绝不可执行活动为进行中");
}
}

包装处理

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
38
39
40
41
42
public class StateHandler {

private Map<Enum<Status>, State> stateMap = new ConcurrentHashMap<>();

public StateHandler() {
stateMap.put(Status.Check, new CheckState()); // 待审核
stateMap.put(Status.Close, new CloseState()); // 已关闭
stateMap.put(Status.Doing, new DoingState()); // 活动中
stateMap.put(Status.Editing, new EditingState()); // 编辑中
stateMap.put(Status.Open, new OpenState()); // 已开启
stateMap.put(Status.Pass, new PassState()); // 审核通过
stateMap.put(Status.Refuse, new RefuseState()); // 审核拒绝
}

public Result arraignment(String activityId, Enum<Status> currentStatus) {
return stateMap.get(currentStatus).arraignment(activityId, currentStatus);
}

public Result checkPass(String activityId, Enum<Status> currentStatus) {
return stateMap.get(currentStatus).checkPass(activityId, currentStatus);
}

public Result checkRefuse(String activityId, Enum<Status> currentStatus) {
return stateMap.get(currentStatus).checkRefuse(activityId, currentStatus);
}

public Result checkRevoke(String activityId, Enum<Status> currentStatus) {
return stateMap.get(currentStatus).checkRevoke(activityId, currentStatus);
}

public Result close(String activityId, Enum<Status> currentStatus) {
return stateMap.get(currentStatus).close(activityId, currentStatus);
}

public Result open(String activityId, Enum<Status> currentStatus) {
return stateMap.get(currentStatus).open(activityId, currentStatus);
}

public Result doing(String activityId, Enum<Status> currentStatus) {
return stateMap.get(currentStatus).doing(activityId, currentStatus);
}
}
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 class ApiTest {

private Logger logger = LoggerFactory.getLogger(ApiTest.class);

@Test
public void test_Editing2Arraignment() {
String activityId = "100001";
ActivityService.init(activityId, Status.Editing);

StateHandler stateHandler = new StateHandler();
Result result = stateHandler.arraignment(activityId, Status.Editing);

logger.info("测试结果(编辑中To提审活动):{}", JSON.toJSONString(result));
logger.info("活动信息:{} 状态:{}", JSON.toJSONString(ActivityService.queryActivityInfo(activityId)), JSON.toJSONString(ActivityService.queryActivityInfo(activityId).getStatus()));
}

@Test
public void test_Editing2Open() {
String activityId = "100001";
ActivityService.init(activityId, Status.Editing);

StateHandler stateHandler = new StateHandler();
Result result = stateHandler.open(activityId, Status.Editing);

logger.info("测试结果(编辑中To开启活动):{}", JSON.toJSONString(result));
logger.info("活动信息:{} 状态:{}", JSON.toJSONString(ActivityService.queryActivityInfo(activityId)), JSON.toJSONString(ActivityService.queryActivityInfo(activityId).getStatus()));
}
}

策略模式

指定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的变化不会影响使用算法的客户。策略模式属于对象行为模式,它通过对算法进行封装,把使用算法的责任和算法的实现分割开来,并委派给不同的对象对这些算法进行管理

  • 模拟多种营销类型优惠券,折扣金额计算策略场景
1
2
3
4
5
6
7
8
/**
* 优惠券折扣计算接口
*/
public interface ICouponDiscount<T> {

BigDecimal discountAmount(T couponInfo, BigDecimal skuPrice);

}

实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class MJCouponDiscount implements ICouponDiscount<Map<String,String>> {

/**
* 满减计算
* 1. 判断满足x元后-n元,否则不减
* 2. 最低支付金额1元
*/
@Override
public BigDecimal discountAmount(Map<String, String> couponInfo, BigDecimal skuPrice) {
String x = couponInfo.get("x");
String o = couponInfo.get("n");

// 小于商品金额条件的,直接返回商品原价
if (skuPrice.compareTo(new BigDecimal(x)) < 0) return skuPrice;
// 减去优惠金额判断
BigDecimal discountAmount = skuPrice.subtract(new BigDecimal(o));
if (discountAmount.compareTo(BigDecimal.ZERO) < 1) return BigDecimal.ONE;

return discountAmount;
}
}
1
2
3
4
5
6
7
8
9
10
public class NYGCouponDiscount implements ICouponDiscount<Double> {

/**
* n元购购买
* 1. 无论原价多少钱都固定金额购买
*/
public BigDecimal discountAmount(Double couponInfo, BigDecimal skuPrice) {
return new BigDecimal(couponInfo);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
public class ZJCouponDiscount implements ICouponDiscount<Double> {

/**
* 直减计算
* 1. 使用商品价格减去优惠价格
* 2. 最低支付金额1元
*/
public BigDecimal discountAmount(Double couponInfo, BigDecimal skuPrice) {
BigDecimal discountAmount = skuPrice.subtract(new BigDecimal(couponInfo));
if (discountAmount.compareTo(BigDecimal.ZERO) < 1) return BigDecimal.ONE;
return discountAmount;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class ZKCouponDiscount implements ICouponDiscount<Double> {

/**
* 折扣计算
* 1. 使用商品价格乘以折扣比例,为最后支付金额
* 2. 保留两位小数
* 3. 最低支付金额1元
*/
public BigDecimal discountAmount(Double couponInfo, BigDecimal skuPrice) {
BigDecimal discountAmount = skuPrice.multiply(new BigDecimal(couponInfo)).setScale(2, BigDecimal.ROUND_HALF_UP);
if (discountAmount.compareTo(BigDecimal.ZERO) < 1) return BigDecimal.ONE;
return discountAmount;
}
}

管理类

1
2
3
4
5
6
7
8
9
10
11
12
public class Context<T> {

private ICouponDiscount<T> couponDiscount;

public Context(ICouponDiscount<T> couponDiscount) {
this.couponDiscount = couponDiscount;
}

public BigDecimal discountAmount(T couponInfo, BigDecimal skuPrice) {
return couponDiscount.discountAmount(couponInfo, skuPrice);
}
}
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
38
39
40
41
/**
* 测试
*/
public class ApiTest {

private Logger logger = LoggerFactory.getLogger(ApiTest.class);

@Test
public void test_zj() {
Context<Double> context = new Context<>(new ZJCouponDiscount());
BigDecimal discountAmount = context.discountAmount(10D, new BigDecimal(100));
logger.info("测试结果:直减优惠后金额 {}", discountAmount);
}

@Test
public void test_mj() {
// 满100减10,商品100元
Context<Map<String,String>> context = new Context<Map<String,String>>(new MJCouponDiscount());
Map<String,String> mapReq = new HashMap<String, String>();
mapReq.put("x","100");
mapReq.put("n","10");
BigDecimal discountAmount = context.discountAmount(mapReq, new BigDecimal(100));
logger.info("测试结果:满减优惠后金额 {}", discountAmount);
}

@Test
public void test_zk() {
// 折扣9折,商品100元
Context<Double> context = new Context<Double>(new ZKCouponDiscount());
BigDecimal discountAmount = context.discountAmount(0.9D, new BigDecimal(100));
logger.info("测试结果:折扣9折后金额 {}", discountAmount);
}

@Test
public void test_nyg() {
// n元购;100-10,商品100元
Context<Double> context = new Context<Double>(new NYGCouponDiscount());
BigDecimal discountAmount = context.discountAmount(90D, new BigDecimal(100));
logger.info("测试结果:n元购优惠后金额 {}", discountAmount);
}
}

模板模式

指定义一个操作中的算法骨架,而将算法的一些步骤延迟到子类中,使得子类可以不改变该算法结构的情况下重定义该算法的某些特定步骤。它是一种类行为型模式

  • 模拟爬虫各类电商商品,生成营销推广海报场景
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
38
39
/**
* 抽象模板
*/
public abstract class NetMall {

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

String uId; // 用户ID
String uPwd; // 用户密码

public NetMall(String uId, String uPwd) {
this.uId = uId;
this.uPwd = uPwd;
}

/**
* 生成商品推广海报
*
* @param skuUrl 商品地址(京东、淘宝、当当)
* @return 海报图片base64位信息
*/
public String generateGoodsPoster(String skuUrl) {
if (!login(uId, uPwd)) return null; // 1. 验证登录

Map<String, String> reptile = reptile(skuUrl); // 2. 爬虫商品

return createBase64(reptile); // 3. 组装海报
}

// 模拟登录
protected abstract Boolean login(String uId, String uPwd);

// 爬虫提取商品信息(登录后的优惠价格)
protected abstract Map<String, String> reptile(String skuUrl);

// 生成商品海报信息
protected abstract String createBase64(Map<String, String> goodsInfo);

}
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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
public class HttpClient {

public static String doGet(String httpurl) {
HttpURLConnection connection = null;
InputStream is = null;
BufferedReader br = null;
String result = null;// 返回结果字符串
try {
// 创建远程url连接对象
URL url = new URL(httpurl);
// 通过远程url连接对象打开一个连接,强转成httpURLConnection类
connection = (HttpURLConnection) url.openConnection();
// 设置连接方式:get
connection.setRequestMethod("GET");
// 设置连接主机服务器的超时时间:15000毫秒
connection.setConnectTimeout(15000);
// 设置读取远程返回的数据时间:60000毫秒
connection.setReadTimeout(60000);
// 发送请求
connection.connect();
// 通过connection连接,获取输入流
if (connection.getResponseCode() == 200) {
is = connection.getInputStream();
// 封装输入流is,并指定字符集
br = new BufferedReader(new InputStreamReader(is, "UTF-8"));
// 存放数据
StringBuilder sbf = new StringBuilder();
String temp = null;
while ((temp = br.readLine()) != null) {
sbf.append(temp);
sbf.append("\r\n");
}
result = sbf.toString();
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
// 关闭资源
if (null != br) {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}

if (null != is) {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}

assert connection != null;
connection.disconnect();// 关闭远程连接
}

return result;
}
}

接口实现

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
public class DangDangNetMall extends NetMall {

public DangDangNetMall(String uId, String uPwd) {
super(uId, uPwd);
}

@Override
public Boolean login(String uId, String uPwd) {
logger.info("模拟当当用户登录 uId:{} uPwd:{}", uId, uPwd);
return true;
}

@Override
public Map<String, String> reptile(String skuUrl) {
String str = HttpClient.doGet(skuUrl);
Pattern p9 = Pattern.compile("(?<=title\\>).*(?=</title)");
Matcher m9 = p9.matcher(str);
Map<String, String> map = new ConcurrentHashMap<String, String>();
if (m9.find()) {
map.put("name", m9.group());
}
map.put("price", "4548.00");
logger.info("模拟当当商品爬虫解析:{} | {} 元 {}", map.get("name"), map.get("price"), skuUrl);
return map;
}

@Override
public String createBase64(Map<String, String> goodsInfo) {
BASE64Encoder encoder = new BASE64Encoder();
logger.info("模拟生成当当商品base64海报");
return encoder.encode(JSON.toJSONString(goodsInfo).getBytes());
}
}
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
/**
* 模拟JD商城
*/
public class JDNetMall extends NetMall {

public JDNetMall(String uId, String uPwd) {
super(uId, uPwd);
}

public Boolean login(String uId, String uPwd) {
logger.info("模拟京东用户登录 uId:{} uPwd:{}", uId, uPwd);
return true;
}

public Map<String, String> reptile(String skuUrl) {
String str = HttpClient.doGet(skuUrl);
Pattern p9 = Pattern.compile("(?<=title\\>).*(?=</title)");
Matcher m9 = p9.matcher(str);
Map<String, String> map = new ConcurrentHashMap<String, String>();
if (m9.find()) {
map.put("name", m9.group());
}
map.put("price", "5999.00");
logger.info("模拟京东商品爬虫解析:{} | {} 元 {}", map.get("name"), map.get("price"), skuUrl);
return map;
}

public String createBase64(Map<String, String> goodsInfo) {
BASE64Encoder encoder = new BASE64Encoder();
logger.info("模拟生成京东商品base64海报");
return encoder.encode(JSON.toJSONString(goodsInfo).getBytes());
}
}
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
public class TaoBaoNetMall extends NetMall {

public TaoBaoNetMall(String uId, String uPwd) {
super(uId, uPwd);
}

@Override
public Boolean login(String uId, String uPwd) {
logger.info("模拟淘宝用户登录 uId:{} uPwd:{}", uId, uPwd);
return true;
}

@Override
public Map<String, String> reptile(String skuUrl) {
String str = HttpClient.doGet(skuUrl);
Pattern p9 = Pattern.compile("(?<=title\\>).*(?=</title)");
Matcher m9 = p9.matcher(str);
Map<String, String> map = new ConcurrentHashMap<String, String>();
if (m9.find()) {
map.put("name", m9.group());
}
map.put("price", "4799.00");
logger.info("模拟淘宝商品爬虫解析:{} | {} 元 {}", map.get("name"), map.get("price"), skuUrl);
return map;
}

@Override
public String createBase64(Map<String, String> goodsInfo) {
BASE64Encoder encoder = new BASE64Encoder();
logger.info("模拟生成淘宝商品base64海报");
return encoder.encode(JSON.toJSONString(goodsInfo).getBytes());
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* 测试类
*/
public class ApiTest {

public Logger logger = LoggerFactory.getLogger(ApiTest.class);

/**
* 测试链接
* 京东;https://item.jd.com/100008348542.html
* 淘宝;https://detail.tmall.com/item.htm
* 当当;http://product.dangdang.com/1509704171.html
*/
@Test
public void test_NetMall() {
NetMall netMall = new JDNetMall("1000001","*******");
String base64 = netMall.generateGoodsPoster("https://item.jd.com/100008348542.html");
logger.info("测试结果:{}", base64);
}
}

访问者模式

指将作用于某种数据结构中的各元素的操作分离出来封装成独立的类,使其在不改变数据结构的前提下可以添加作用于这些元素的新的操作,为数据结构中的每个元素提供多种访问方式

  • 模拟家长与校长,对学生和老师的不同视角信息的访问场景
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* 用户类
*/
public abstract class User {

public String name; // 姓名
public String identity; // 身份;重点班、普通班 | 特级教师、普通教师、实习教师
public String clazz; // 班级

public User(String name, String identity, String clazz) {
this.name = name;
this.identity = identity;
this.clazz = clazz;
}

// 核心访问方法
public abstract void accept(Visitor visitor);

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 学生
public class Student extends User {

public Student(String name, String identity, String clazz) {
super(name, identity, clazz);
}

public void accept(Visitor visitor) {
visitor.visit(this);
}

public int ranking() {
return (int) (Math.random() * 100);
}

public int count() {
return 105 - new Random().nextInt(10);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 老师
public class Teacher extends User {

public Teacher(String name, String identity, String clazz) {
super(name, identity, clazz);
}

public void accept(Visitor visitor) {
visitor.visit(this);
}

// 升本率
public double entranceRatio() {
return BigDecimal.valueOf(Math.random() * 100).setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
}
}

访问者接口

1
2
3
4
5
6
7
8
9
10
11
12
/**
* 访问者接口
*/
public interface Visitor {

// 访问学生信息
void visit(Student student);

// 访问老师信息
void visit(Teacher teacher);

}

接口实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* 家长
*/
public class Parent implements Visitor {

private Logger logger = LoggerFactory.getLogger(Parent.class);

@Override
public void visit(Student student) {
logger.info("学生信息 姓名:{} 班级:{} 排名:{}", student.name, student.clazz, student.ranking());
}

@Override
public void visit(Teacher teacher) {
logger.info("老师信息 姓名:{} 班级:{} 级别:{}", teacher.name, teacher.clazz, teacher.identity);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* 校长
*/
public class Principal implements Visitor {

private Logger logger = LoggerFactory.getLogger(Principal.class);

@Override
public void visit(Student student) {
logger.info("学生信息 班级:{} 人数:{}", student.clazz, student.count());
}

@Override
public void visit(Teacher teacher) {
logger.info("学生信息 姓名:{} 班级:{} 升学率:{}", teacher.name, teacher.clazz, teacher.entranceRatio());
}
}

数据视图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
* 数据看板
*/
public class DataView {

List<User> userList = new ArrayList<User>();

public DataView() {
userList.add(new Student("谢飞机", "重点班", "一年一班"));
userList.add(new Student("windy", "重点班", "一年一班"));
userList.add(new Student("大毛", "普通班", "二年三班"));
userList.add(new Student("Shing", "普通班", "三年四班"));
userList.add(new Teacher("BK", "特级教师", "一年一班"));
userList.add(new Teacher("娜娜Goddess", "特级教师", "一年一班"));
userList.add(new Teacher("dangdang", "普通教师", "二年三班"));
userList.add(new Teacher("泽东", "实习教师", "三年四班"));
}

public void show(Visitor visitor) {
for (User user : userList) {
user.accept(visitor);
}
}
}

测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class ApiTest {

private Logger logger = LoggerFactory.getLogger(ApiTest.class);

@Test
public void test_show(){
DataView dataView = new DataView();

logger.info("\r\n家长视角访问:");
dataView.show(new Parent());

logger.info("\r\n校长视角访问:");
dataView.show(new Principal());
}
}

Relearning Design Pattern
https://www.renkelin.vip/2024/07/29/Relearning-design-patterns/
Author
Kolin
Posted on
July 29, 2024
Licensed under