设计模式

设计模式

一份好的代码,离不开好的设计模式。设计模式总共有七大准则,二十多种设计模式都是根据七大准则来编写的。

  • 单一职责

    我们尽可能的让一个模块(类)只实现一个功能,在一些复杂的项目中如果一个类实现多个功能的话,那么整体逻辑会狼狈不堪,如果是小demo的话,那么就无所谓了。

  • 开闭原则

    软件实体(类、模块或方法)应该对扩展开放,对修改封闭。这意味着在添加新功能时,应该尽量通过扩展现有代码来实现,而不是修改现有代码。

  • 里氏替换原则

    基类使用的地方子类一定可以使用,反之不行。

  • 接口隔离原则

    接口的粒度应该细化,使得客户端只用到它自己需要的接口

  • 依赖倒置原则

    高层模块不应该依赖于低层模块,它们都应该依赖于抽象。抽象不应该依赖于细节,细节应该依赖于抽象。

  • 合成复用原则

    尽量使用组合和聚合,而不是继承,来实现代码复用。

  • 迪米特原则

    一个对象应该对另一个对象有最少的了解。换句话说,一个对象应该只与直接相关的其他对象进行通信,而避免与无关的对象进行交互。

设计模式总体分为三类:创建型模式,结构型模式,行为模式。

创建型模式:这类模式提供创建对象的机制, 能够提升已有代码的灵活性和可复⽤性。主要有:工厂、抽象、建造、原型、单例。

结构型模式:这类模式介绍如何将对象和类组装成较⼤的结构, 并同时保持结构的灵活和⾼效。主要有:适配器、桥接、组合、装饰、外观、亨元、代理。

行为型模式:这类模式负责对象间的⾼效沟通和职责委派。主要有:责任链、命令、迭代器、中介者、备忘录、观察者、状态、策略、模板、访问者。

所有案例代码: https://github.com/ykexc/Design

工厂(方法)模式

场景:在发放奖品的场景下,有着不同类别的奖品(实物,优惠券,其他…),入参和出参均不同,通过工厂方法模式统一入参,提高代码扩展性,减少if else

文件目录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
├─awards
│ Coupon.java
│ Goods.java
│ IQIYICard.java

├─req
│ GoodsReq.java

├─resp
│ GoodsResp.java

├─service
│ CouponService.java
│ GoodsService.java
│ IQIYICardService.java

└─store
│ ICommodity.java
│ StoreFactory.java

└─impl
CouponCommodityImpl.java
GoodsCommodityImpl.java
OtherCommodityImpl.java

coupon,goods,iqiyi分别代表着三种不同类别的奖品,service中是三种不同奖品发放的逻辑,入参出参不同,通过定义ICommodity接口统一入参,并实现三种不同方式的发放逻辑,通过StoreFactory进行最路由处理,使代码变得比较整洁干净,易于扩展。

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 CouponService {

public Coupon sendCoupon(String uid, String couponId, String uuid) {
System.out.println("发送优惠券成功: uid: " + uid + " couponId: " + couponId + " uid: " + uid);
return new Coupon("0x3f3f3f3f", "普通优惠券");
}

}


public class GoodsService {


public GoodsResp sendGoods(
GoodsReq req
) {
System.out.println("实物奖品发放成功: " + "uid: " + req.uid() + " address: " + req.address());
return new GoodsResp(req.uid(), req.address(), req.phone(), new Goods("abc", "一本书"));
}

}


public class IQIYICardService {


public void grantToken(String uid, String bindMobileNumber) {
System.out.println("模拟发放爱奇艺会员卡一张:" + bindMobileNumber + "," + new IQIYICard("0x3f3f3f3f"));
}

}
1
2
3
4
5
public interface ICommodity {

void sendCommodity(Map<String, String> param);

}
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 CouponCommodityImpl implements ICommodity {

CouponService couponService = new CouponService();

Logger logger = LoggerFactory.getLogger(CouponCommodityImpl.class);

@Override
public void sendCommodity(Map<String, String> param) {
Coupon coupon = couponService.sendCoupon(param.get("uid"), param.get("couponId"), param.get("uuid"));
logger.info("优惠券发放成功: {}: {}", coupon.cardId(), coupon.info());
}
}



public class GoodsCommodityImpl implements ICommodity {

Logger logger = LoggerFactory.getLogger(GoodsCommodityImpl.class);

GoodsService goodsService = new GoodsService();
@Override
public void sendCommodity(Map<String, String> param) {
GoodsResp goodsResp = goodsService.sendGoods(JSON.parseObject(param.get("goodsReq"), GoodsReq.class));
logger.info("实物发放成功, {}: {} :{}", goodsResp.uid(), goodsResp.goods(), goodsResp.address());
}
}



public class OtherCommodityImpl implements ICommodity {


IQIYICardService iqiyiCardService = new IQIYICardService();

Logger logger = LoggerFactory.getLogger(OtherCommodityImpl.class);

@Override
public void sendCommodity(Map<String, String> param) {
iqiyiCardService.grantToken(param.get("uid"), param.get("bindMobileNumber"));
logger.info("发送爱奇艺月卡成功");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class StoreFactory {

GoodsCommodityImpl goodsCommodity;
CouponCommodityImpl couponCommodity;

OtherCommodityImpl otherCommodity;

public StoreFactory() {
goodsCommodity = new GoodsCommodityImpl();
couponCommodity = new CouponCommodityImpl();
otherCommodity = new OtherCommodityImpl();
}

public ICommodity getCommodity(Integer commodityType) throws IllegalAccessException {
if (null == commodityType) return null;
if (commodityType.equals(1)) return goodsCommodity;
if (commodityType.equals(2)) return couponCommodity;
if (commodityType.equals(3)) return otherCommodity;
throw new IllegalAccessException("错误的类型");
}

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


StoreFactory storeFactory = new StoreFactory();

@Test
public void test() throws IllegalAccessException {
ICommodity commodity1 = storeFactory.getCommodity(1);

commodity1.sendCommodity(Map.of("goodsReq", JSON.toJSONString(new GoodsReq("012", "陕西省咸阳市", "15229602304"))));

ICommodity commodity2 = storeFactory.getCommodity(2);

commodity2.sendCommodity(Map.of("uid", "124", "couponId", "2344", "uuid", "sdfd23453"));

ICommodity commodity3 = storeFactory.getCommodity(3);

commodity3.sendCommodity(Map.of("bindMobileNumber", "sdssfdff233", "uid", "4554"));
}

}

总结: 在工厂(方法)模式中,避免了创建者和产品逻辑的相互耦合,使用指定的工厂进行分配指定的实现,统一的入参,满足了单一职责,开闭原则。但也存在缺点,如果种类特别多的话就得需要很多类 。

抽象工厂模式

场景: 对于系统的主题有多种,但都遵循统一的实现,基于抽象工厂模式,动态的采用不同的工厂生产出不同的对象

文件目录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
├─Theme
│ │ I.java
│ │ IAdapter.java
│ │ package-info.java
│ │ T1.java
│ │ T2.java
│ │
│ ├─impl
│ │ Impl.java
│ │ T1Impl.java
│ │ T2Impl.java
│ │
│ └─proxy
│ ├─cglib
│ │ CGLIBMethodIntercept.java
│ │ CGLIBProxy.java
│ │
│ └─jdk
│ JDKInvocationHandler.java
│ JDKProxy.java

└─util
ClassLoaderUtils.java

IImpl作为系统的默认主题,T1T2分别为两个自定义主题,其中的核心方法都是一样的,方法签名都是一样的,共有两个抽象工厂jdk_proxycglib_proxy都可以实现对象的创建,通过动态指定不同工厂生产同种类型的对象。

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 interface I {

void f1();

void f2();

void f3();

void f4();

}

public class Impl implements I {


@Override
public void f1() {
System.out.println("ST f1");
}

@Override
public void f2() {
System.out.println("ST f2");
}

@Override
public void f3() {
System.out.println("ST f3");
}

@Override
public void f4() {
System.out.println("ST f4");
}

}
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
//两中的自定义主题
public class T1 {


public void f1T1() {
System.out.println("这是T1调色功能");
}

public void f2T1() {
System.out.println("这是T1组件功能");
}


public void f3T1() {
System.out.println("这是T1放大功能");
}

public void f4T1() {
System.out.println("这是T1缩小功能");
}


}

public class T2 {


public void f1T2() {
System.out.println("这是T2调色功能");
}

public void f2T2() {
System.out.println("这是T2组件功能");
}


public void f3T2() {
System.out.println("这是T2放大功能");
}

public void f4T2() {
System.out.println("这是T2缩小功能");
}
}
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
//通过适配器来适配默认的方法
public interface IAdapter {


void f1();

void f2();

void f3();

void f4();

}

public class T1Impl implements IAdapter {

T1 t1 = new T1();

@Override
public void f1() {
t1.f1T1();
}

@Override
public void f2() {
t1.f2T1();
}

@Override
public void f3() {
t1.f3T1();
}

@Override
public void f4() {
t1.f4T1();
}
}

public class T2Impl implements IAdapter {


T2 t2 = new T2();

@Override
public void f1() {
t2.f1T2();
}

@Override
public void f2() {
t2.f2T2();
}

@Override
public void f3() {
t2.f3T2();
}

@Override
public void f4() {
t2.f4T2();
}
}
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
//JDK_Proxy实现
public class JDKInvocationHandler implements InvocationHandler {

private final IAdapter iAdapter;

public JDKInvocationHandler(IAdapter iAdapter) {
this.iAdapter = iAdapter;
}

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return IAdapter.class.getMethod(method.getName(), ClassLoaderUtils.getClazzByArgs(args)).invoke(iAdapter, args);
}
}

@SuppressWarnings("unchecked")
public class JDKProxy {


public static<T> T getProxy(Class<T> clazz, IAdapter adapter) {
JDKInvocationHandler jdkInvocationHandler = new JDKInvocationHandler(adapter);
ClassLoader classLoader = clazz.getClassLoader();
return (T) Proxy.newProxyInstance(classLoader, clazz.getInterfaces(), jdkInvocationHandler);
}

}
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
//cglib_proxy实现
public class CGLIBMethodIntercept implements MethodInterceptor {

private final IAdapter adapter;

public CGLIBMethodIntercept(IAdapter adapter) {
this.adapter = adapter;
}


@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
return IAdapter.class.getMethod(method.getName(), ClassLoaderUtils.getClazzByArgs(objects)).invoke(adapter, objects);
}
}

@SuppressWarnings("unchecked")
public class CGLIBProxy {


public static <T> T getProxy(Class<T> clazz, IAdapter adapter) {
return (T) Enhancer.create(clazz, new CGLIBMethodIntercept(adapter));
}


}
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
175
176
177
178
179
public class ClassLoaderUtils {

private static Set<Class> primitiveSet = new HashSet<Class>();

static {
primitiveSet.add(Integer.class);
primitiveSet.add(Long.class);
primitiveSet.add(Float.class);
primitiveSet.add(Byte.class);
primitiveSet.add(Short.class);
primitiveSet.add(Double.class);
primitiveSet.add(Character.class);
primitiveSet.add(Boolean.class);
}

/**
* 得到当前ClassLoader
*
* @return ClassLoader
*/
public static ClassLoader getCurrentClassLoader() {
ClassLoader cl = Thread.currentThread().getContextClassLoader();
if (cl == null) {
cl = ClassLoaderUtils.class.getClassLoader();
}
return cl == null ? ClassLoader.getSystemClassLoader() : cl;
}

/**
* 得到当前ClassLoader
*
* @param clazz 某个类
* @return ClassLoader
*/
public static ClassLoader getClassLoader(Class<?> clazz) {
ClassLoader loader = Thread.currentThread().getContextClassLoader();
if (loader != null) {
return loader;
}
if (clazz != null) {
loader = clazz.getClassLoader();
if (loader != null) {
return loader;
}
return clazz.getClassLoader();
}
return ClassLoader.getSystemClassLoader();
}

/**
* 根据类名加载Class
*
* @param className 类名
* @return Class
* @throws ClassNotFoundException 找不到类
*/
public static Class forName(String className)
throws ClassNotFoundException {
return forName(className, true);
}

/**
* 根据类名加载Class
*
* @param className 类名
* @param initialize 是否初始化
* @return Class
* @throws ClassNotFoundException 找不到类
*/
public static Class forName(String className, boolean initialize)
throws ClassNotFoundException {
return Class.forName(className, initialize, getCurrentClassLoader());
}

/**
* 根据类名加载Class
*
* @param className 类名
* @param cl Classloader
* @return Class
* @throws ClassNotFoundException 找不到类
*/
public static Class forName(String className, ClassLoader cl)
throws ClassNotFoundException {
return Class.forName(className, true, cl);
}

/**
* 实例化一个对象(只检测默认构造函数,其它不管)
*
* @param clazz 对象类
* @param <T> 对象具体类
* @return 对象实例
* @throws Exception 没有找到方法,或者无法处理,或者初始化方法异常等
*/
public static <T> T newInstance(Class<T> clazz) throws Exception {
if (primitiveSet.contains(clazz)) {
return null;
}
if (clazz.isMemberClass() && !Modifier.isStatic(clazz.getModifiers())) {
Constructor constructorList[] = clazz.getDeclaredConstructors();
Constructor defaultConstructor = null;
for (Constructor con : constructorList) {
if (con.getParameterTypes().length == 1) {
defaultConstructor = con;
break;
}
}
if (defaultConstructor != null) {
if (defaultConstructor.isAccessible()) {
return (T) defaultConstructor.newInstance(new Object[]{null});
} else {
try {
defaultConstructor.setAccessible(true);
return (T) defaultConstructor.newInstance(new Object[]{null});
} finally {
defaultConstructor.setAccessible(false);
}
}
} else {
throw new Exception("The " + clazz.getCanonicalName() + " has no default constructor!");
}
}
try {
return clazz.newInstance();
} catch (Exception e) {
Constructor<T> constructor = clazz.getDeclaredConstructor();
if (constructor.isAccessible()) {
throw new Exception("The " + clazz.getCanonicalName() + " has no default constructor!", e);
} else {
try {
constructor.setAccessible(true);
return constructor.newInstance();
} finally {
constructor.setAccessible(false);
}
}
}
}

public static Class<?>[] getClazzByArgs(Object[] args) {
if (args == null) return new Class[0];
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;
}

public Method getMethod(Class<?> classType, String methodName, Class<?>... parameterTypes) throws NoSuchMethodException {
return classType.getMethod(methodName, parameterTypes);
}

}

总结: 抽象工厂和工厂方法的本质都是为了创建对象,两者本质上的区别在于一个是直接创建对象,一个是先创建工厂再创建对象,抽象工厂的更灵活一点,科以动态指定不同的工厂,两者还有一点不同的就是工厂方法可以创建类型完全不一样的对象,而抽象工厂更适合创建同类型的对象。

建造者模式

场景: 在点餐时通过不同的菜品组成不同的套餐,可以使用建造者模式使得代码更加整洁,对于新增套餐省去if else

文件目录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
food
│ Builder.java
│ IMenu.java
│ Matter.java
│ Pack.java

├─beverage
│ Noodles.java
│ Rice.java

├─dishes
│ ColdDish.java
│ HotDish.java

└─staplefood
CocaCola.java
PepsiCola.java

食品总共有beveragedishesstaplefood三种类型,都是基于食品的规范Matter来实现的,基于不同的组合可以实现不同级别的套餐,定义IMenu接口采取返回原类这种格式,通过Pack来实现,在Builder里面定义不同的级别的组合方式,最后只需要在Builder里面选取所需要的级别即可。

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
public interface Matter {


/**
* 类型
* @return String
*/
String scene();


/**
* 品牌
* @return String
*/
String brand();


/**
* 价格
* @return BigDecimal
*/
BigDecimal price();

/**
* 描述
* @return String
*/
String desc();

}

public class Noodles implements Matter {
@Override
public String scene() {
return "主食";
}

@Override
public String brand() {
return "香雪";
}

@Override
public BigDecimal price() {
return BigDecimal.valueOf(54.9);
}

@Override
public String desc() {
return "普通面条";
}
}


public class Rice implements Matter {
@Override
public String scene() {
return "主食";
}

@Override
public String brand() {
return "金龙鱼";
}

@Override
public BigDecimal price() {
return BigDecimal.valueOf(55.3);
}

@Override
public String desc() {
return "普通的金龙鱼大米";
}
}


public class ColdDish implements Matter {
@Override
public String scene() {
return "菜品";
}

@Override
public String brand() {
return "一级";
}

@Override
public BigDecimal price() {
return BigDecimal.valueOf(15.8);
}

@Override
public String desc() {
return "一级凉菜,挺好吃";
}
}


public class HotDish implements Matter {
@Override
public String scene() {
return "菜品";
}

@Override
public String brand() {
return "一级";
}

@Override
public BigDecimal price() {
return BigDecimal.valueOf(9.6);
}

@Override
public String desc() {
return "普通热菜";
}
}


public class CocaCola implements Matter {
@Override
public String scene() {
return "饮料";
}

@Override
public String brand() {
return "可口可乐";
}

@Override
public BigDecimal price() {
return BigDecimal.valueOf(3);
}

@Override
public String desc() {
return "可口可乐中瓶";
}
}


public class PepsiCola implements Matter {
@Override
public String scene() {
return "饮料";
}

@Override
public String brand() {
return "百事可乐";
}

@Override
public BigDecimal price() {
return BigDecimal.valueOf(3);
}

@Override
public String desc() {
return "百事可乐中瓶";
}
}

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
public interface IMenu {

IMenu appendBeverage(Matter beverage);



IMenu appendDishes(Matter dishes);


IMenu appendStapleFood(Matter stapleFood);


String ofDetail();

}

public class Pack implements IMenu {


private List<Matter> lists = new ArrayList<>();

private BigDecimal price = BigDecimal.ZERO;


private int copies = 1;

public Pack(int copies) {
this.copies = copies;
}

public Pack() {
}

@Override
public IMenu appendBeverage(Matter beverage) {
lists.add(beverage);
price = price.add(beverage.price());
return this;
}

@Override
public IMenu appendDishes(Matter dishes) {
lists.add(dishes);
price = price.add(dishes.price());
return this;
}

@Override
public IMenu appendStapleFood(Matter stapleFood) {
lists.add(stapleFood);
price = price.add(stapleFood.price());
return this;
}

@Override
public String ofDetail() {
StringJoiner sj = new StringJoiner("[", " ", "]");
for (var e : lists) sj.add(e.desc());
sj.add("price").add(String.valueOf(price.doubleValue() * copies));
return sj.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
public class Builder {






public IMenu levelOne(int copies) {
return new Pack(copies)
.appendBeverage(new Rice())
.appendDishes(new HotDish())
.appendStapleFood(new CocaCola());
}

public IMenu levelOne() {
return new Pack()
.appendBeverage(new Rice())
.appendDishes(new HotDish())
.appendStapleFood(new CocaCola());
}

public IMenu levelTwo(int copies) {
return new Pack(copies)
.appendBeverage(new Noodles())
.appendDishes(new ColdDish())
.appendStapleFood(new PepsiCola());
}

public IMenu levelTwo() {
return new Pack()
.appendBeverage(new Noodles())
.appendDishes(new ColdDish())
.appendStapleFood(new PepsiCola());
}

}

总结: 建造者模式适合于通过一些不变的小对象组合成不同种类的大对象的需求,建造者模式的最大优点就是易于扩展,在需要扩展时只需要添加少量代码,而不需要修改原先的代码。

原型模式

原型模式用于(重复)对象的创建,往往基于Java的clone实现,在此谈谈Java的克隆

目录

1
2
3
4
5
6
─normal
E1.java
E2.java
E3.java
Immutable.java
R.java

原型模式主要是用于解决对象的克隆,通常不常用,在此介绍Java如何克隆一个对象。

浅拷贝:对于基本类型而言,复制了一个新的值,对于引用类型而言,复制了地址。

深拷贝:对于任意类型而言都实现了完全复制。

Java实现深拷贝的方式有两种,一种是利用JDK自带的Clone实现,另一种是让对象经过两次IO。

基于Clone实现,首先确保要实现Cloneable接口,重写clone方法,值得注意的是clone方法是Object类中的本地方法,对于包含引用类型的对象而言需要层层克隆,并且这些引用类都需要实现Cloneable接口显然对于一个对象内层层包含多个引用对象时这种方式是不可取的,但是它的效率并不低。

1
2
3
4
5
6
7
8
9
10
//  引用类型R类
public class R implements Cloneable, Serializable {


@Override
protected R clone() throws CloneNotSupportedException {
return (R) super.clone();
}
}

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
//  基于JDK的clone实现深拷贝
public class E1 implements Cloneable{

public String name;
R obj;

int v;

public E1(R _obj, String _name, int _v) {
this.name = _name;
this.v = _v;
this.obj = _obj;
}


@Override
public E1 clone() throws CloneNotSupportedException {
E1 ret = null;
ret = (E1) super.clone();
ret.obj = ret.obj.clone();
return ret;
}


@Override
public String toString() {
return "E{" +
"name='" + name + '\'' +
", obj=" + obj +
", v=" + v +
'}';
}
}

另一种方式就是基于先读后写的方式,这种只需要保证所有的实现Serializable接口即可,然后将对象进行两次序列化即可。这样复制出来的对象比上面复制出的更’干净’。

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 E2 implements Serializable {

private final R obj;

public final String name;


private final int k;


public E2(R _obj, String _name, int _k) {
this.obj = _obj;
this.name = _name;
this.k = _k;
}


@Override
public String toString() {
return "E2{" +
"obj=" + obj +
", name='" + name + '\'' +
", k=" + k +
'}';
}


public E2 cloneBySerializable() throws IOException, ClassNotFoundException {
try (
var buf = new ByteArrayOutputStream();
var oos = new ObjectOutputStream(buf)
) {
oos.writeObject(this);
try (
var in = new ObjectInputStream(new ByteArrayInputStream(buf.toByteArray()))
) {
return (E2) in.readObject();
}
}
}
}

至于我为什么说更’干净’呢?这就得提起E1E2中都有的String了,String作为JDK自带的类,并没有实现Cloneable接口,那么它在深拷贝的时候,原对象和复制后的对象的String对象应该是同一个,事实证明确实如此,但这不就违背了深拷贝的定义了吗?但是String它是一个特殊的类,被成为不可变类,不可变类就是一个类创建对象后这个对象中的属性值不在发生改变,当需要改变时都需要返回新的对象,这样想的话,不可变类的特性似乎刚好满足我们对对象复制的要求。而且还省事,所以有必要时可以考虑将类设为不可变类,但是不可变类会反复创建对象,也是有偿有失。

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 Test {


@org.junit.Test
public void test0() throws CloneNotSupportedException {
E1 e = new E1(new R(), "huya", 123);
long t1 = System.currentTimeMillis();
E1 clone = e.clone();
long t2 = System.currentTimeMillis();
System.out.println(t2 - t1); //时间性能优越
System.out.println(e);
System.out.println(clone);
System.out.println(e.name == clone.name); //输出true

}

@org.junit.Test
public void test1() throws IOException, ClassNotFoundException {
E2 e = new E2(new R(), "huya", 123);
long t1 = System.currentTimeMillis();
E2 clone = e.cloneBySerializable();
long t2 = System.currentTimeMillis();
System.out.println(t2 - t1);
System.out.println(e);
System.out.println(clone);
System.out.println(e.name == clone.name); //输出false
}
}

单例模式

单例模式主要是用于解决对象的复用性,有饿汉和懒汉模式,主要就是一下7种

目录

1
2
3
4
5
6
7
8
9
10
11
12
13
type
├─hungry
package-info.java
│ S1.java

└─lazy
package-info.java
S2.java
S3.java
S4.java
S5.java
S6.java
S7.java
1
2
3
4
5
6
7
8
9
10
11
12
/**
* 饿汉,线程安全, 此种类型也可以不要获取实例方法
*/
public class S1 {

public static final S1 INSTANCE = new S1();

public static S1 getInstance() {
return INSTANCE;
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* 懒汉式线程不安全
*/
public class S2 {
public static volatile S2 s2;


public static S2 getInstance() {
if (s2 == null) s2 = new S2();
return s2;
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* 线程安全,但效率太低
*/
public class S3 {

public static S3 s3;

public synchronized static S3 getInstance() {
if (s3 == null) s3 = new S3();
return s3;
}

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* 效率较高且安全的懒汉式
*/
public class S4 {
public static volatile S4 s4;

public static S4 getInstance() {
if (s4 != null) return s4;
synchronized (S4.class) {
if (s4 != null) {
s4 = new S4();
}
}
return s4;
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* 线程安全,无锁,懒汉,比较推荐
*/
public class S5 {

private static final class S5Holder {
static final S5 s5 = new S5();
}


public static S5 getInstance() {
return S5Holder.s5;
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* 懒汉,使用Atomic,乐观锁
*/
public class S6 {

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


public static S6 getInstance() {
S6 s6 = INSTANCE.get();
if (s6 != null) return s6;
INSTANCE.compareAndSet(null, new S6());
return INSTANCE.get();
}


}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* 简洁高效,但用处不多,无法实现继承
*/
public enum S7 {


S_7;

public S7 getInstance() {
return S_7;
}

}

单例模式根据情况选取合适的即可,这几种单例,在功能上的实现都是一致的。

适配器模式

适配器模式,显然易见就是为了适配某种东西的,例如电源适配器,将220V的电压适配到电子设备适合的电压,适配器模式的使用范围还是非常广的,在很多地方都能看见,在之前的设计模式中也有体现。通常都是将方法签名或方法参数进行适配。

目录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
├─action
│ │ Ac1.java
│ │ Ac2.java
│ │ IAdapter.java
│ │ package-info.java
│ │
│ └─impl
│ Ac1Impl.java
│ Ac2Impl.java

└─parameter
package-info.java
│ ParameterAdapter.java

└─e
M.java
M1.java
M2.java

在进行方法签名的适配时,采用将字段进行一一对应,并将初始对象进行序列化,最后在进行反序列化的到参数即可。

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


String userId;

String orderId;


Date date;


public String getUserId() {
return userId;
}

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

public String getOrderId() {
return orderId;
}

public void setOrderId(String orderId) {
this.orderId = orderId;
}

public Date getDate() {
return date;
}

public void setDate(String dateStr) throws ParseException {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS", Locale.ENGLISH);
this.date = dateFormat.parse(dateStr);
}

@Override
public String toString() {
return "M{" +
"userId='" + userId + '\'' +
", orderId='" + orderId + '\'' +
", date=" + date +
'}';
}
}


public record M1(

String orderId,


String uid,


LocalDateTime date

) {

}


public record M2(
String skuId,


String userId,


Date date
) {


}

public class ParameterAdapter {


public static M adapter(String jsonStr, Map<String, String> keyValue) throws InvocationTargetException, NoSuchMethodException, IllegalAccessException {
Map<?, ?> map = JSON.parseObject(jsonStr, Map.class);
return adapter(map, keyValue);
}


public static M adapter(Map<?, ?> obj, Map<String, String> keyValue) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
M m = new M();
for (var e : keyValue.entrySet()) {
var val = obj.get(e.getKey());
String value = e.getValue();
String methodName = "set" + value.substring(0, 1).toUpperCase() + value.substring(1);
M.class.getMethod(methodName, String.class).invoke(m, val.toString());
}
return m;
}

}

在进行行为适配时,采用中间加一个接口的方式,使得不同的实现有同一个接口。

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

public void helloMethod(String hello, String world) {
System.out.println(hello + " " + world);
}

}

public class Ac2 {

public void methodHello(String hello, String world) {
System.out.println(world + " " + hello);
}

}

1
2
3
4
5
6
7
public interface IAdapter {


void hello(String hello, String world);

}

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

Ac1 ac1 = new Ac1();

@Override
public void hello(String hello, String world) {
ac1.helloMethod(hello, world);
}
}

public class Ac2Impl implements IAdapter {

Ac2 ac2 = new Ac2();

@Override
public void hello(String hello, String world) {
ac2.methodHello(hello, world);
}
}

桥接模式

当我们需要将多种功能进行组合或者笛卡尔积时,使用桥接模式是一种不错的选择,例如将微信支付和支付宝支付,加上指纹、面容支付时,此处使用桥接模式实现。

目录

1
2
3
4
5
6
7
8
9
10
11
12
F:.
└─pay
│ IPayMode.java
│ Pay.java
│ WxPay.java
│ ZfbPay.java

└─impl
CyPayMode.java
FacePayMode.java
FingerPayMode.java

使用PayMode接口来定义多种校验方式

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 interface IPayMode {


boolean checkSecurity(String uid);

}


public class CyPayMode implements IPayMode {
@Override
public boolean checkSecurity(String uid) {
// 业务逻辑
System.out.println("密码校验");
return false;
}
}



public class FacePayMode implements IPayMode {
@Override
public boolean checkSecurity(String uid) {
//业务逻辑
System.out.println("面容校验");
return true;
}
}


public class FingerPayMode implements IPayMode {
@Override
public boolean checkSecurity(String uid) {
//业务逻辑
System.out.println("指纹校验");
return true;
}
}

定义Pay抽象类,并定义PayMode成员变量,在其基类进行创建时就指定好PayMode

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 abstract class Pay {

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

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

protected abstract String trade(String uid, String tradeId, BigDecimal amount);

}

public class WxPay extends Pay{

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


@Override
public String trade(String uid, String tradeId, BigDecimal amount) {

logger.info("模拟微信渠道支付划账开始。uId:{} tradeId:{} amount:{}", uid, tradeId, amount);
boolean isSecurity = this.payMode.checkSecurity(uid);
logger.info("校验成功, 成功支付");
return "0x00000000000";
}
}


public class ZfbPay extends Pay{


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

@Override
public String trade(String uid, String tradeId, BigDecimal amount) {
logger.info("模拟支付宝渠道支付划账开始。uId:{} tradeId:{} amount:{}", uid, tradeId, amount);
boolean isSecurity = this.payMode.checkSecurity(uid);
logger.info("校验成功, 成功支付");
return "0x00000000000";
}
}

桥接模式可以很方便的实现多种模式之间的组合,使用桥接模式满足开闭原则和单一职责原则。

组合模式

实现决策树来体现组合模式,在对特定人群进行量化时,可以采取决策树来进行筛选、决策。

实现树的创建,分别是TreeRootTreeNodeTreeLink,分别代表树根、节点、树链,树根本身就是一个树节点,树节点又分为普通节点和叶子节点,普通节点上没有值,只有叶子节点上有,节点之间通过树链进行连接,节点上存有限制类型,链上存有比较类型,和比较值,通过这样构成一颗决策树,来将不同用户筛选进不同值。

目录

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
│  Strategy.java

├─aggregation
│ TreeRich.java

├─engine
│ │ BaseEngine.java
│ │ Engine.java
│ │ EngineConfig.java
│ │
│ └─impl
│ TreeEngine.java

├─logic
│ │ BaseFilter.java
│ │ Filter.java
│ │
│ └─impl
│ UserAgeFilter.java
│ UserGenderFilter.java

├─res
│ R.java

└─tree
TreeLink.java
TreeNode.java
TreeRoot.java
TreeType.java

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
//tree的构建
@Data
@AllArgsConstructor
@NoArgsConstructor
public class TreeRoot {

String treeId;


String desc;


String treeName;

String treeRootId;

}

@Data
@AllArgsConstructor
@NoArgsConstructor
public class TreeNode {


String rootId;

String treeId;


TreeType treeType;


String val;

List<TreeLink> links;

Strategy strategy;

}

@Data
@AllArgsConstructor
@NoArgsConstructor
public class TreeLink {


public enum StrategyType {
EQ,

GT,

LT
}

String treeRootId;


String fromTreeId;


String toTreeId;

String limitVal;

StrategyType strategyType;

}

public enum TreeType {

NORMAL,

LEAF

}


public enum Strategy {


AGE,

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
26
27
28
29
30
31
32
33
34
35
36
37
//入参组合
@Data
@NoArgsConstructor
@AllArgsConstructor
public class TreeRich {


@Data
@NoArgsConstructor
@AllArgsConstructor
public static class User {
String userId;

String userName;

String age;

String gender;
}

TreeRoot root;


Map<String, TreeNode> map;

User user;

}

//出参
public record R(
String userId,
String val
) {


}
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
//Filter实现
public interface Filter {


String filter(String realVal, List<TreeLink> links);



boolean judge(String realVal, TreeLink.StrategyType strategyType, String limitVal);

}

public abstract class BaseFilter implements Filter {

@Override
public String filter(String realVal, List<TreeLink> links) {
for (var link : links) {
if (judge(realVal, link.getStrategyType(), link.getLimitVal())) return link.getToTreeId();
}
return "-1";
}


public abstract String getRealVal(TreeRich.User user);


@Override
public boolean judge(String realVal, TreeLink.StrategyType strategyType, String limitVal) {
switch (strategyType) {
case EQ -> {
return realVal.equals(limitVal);
}
case GT -> {
return Double.parseDouble(realVal) >= Double.parseDouble(limitVal);
}
case LT -> {
return Double.parseDouble(realVal) <= Double.parseDouble(limitVal);
}
default -> {
return false;
}
}
}
}

public class UserAgeFilter extends BaseFilter {
@Override
public String getRealVal(TreeRich.User user) {
return user.getAge();
}
}

public class UserGenderFilter extends BaseFilter {

@Override
public String getRealVal(TreeRich.User user) {
return user.getGender();
}
}


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
//engine实现
public interface Engine {


R process(TreeRich treeRich);

}

public class EngineConfig {

static final ConcurrentHashMap<Strategy, BaseFilter> map = new ConcurrentHashMap<>();



static {
map.put(Strategy.AGE, new UserAgeFilter());
map.put(Strategy.GENDER, new UserGenderFilter());
}


}

public abstract class BaseEngine extends EngineConfig implements Engine {

public static final Logger logger = LoggerFactory.getLogger(BaseEngine.class);

@Override
public abstract R process(TreeRich treeRich);


public TreeNode doProcess(TreeRich treeRich) {
TreeRoot root = treeRich.getRoot();
TreeRich.User user = treeRich.getUser();
Map<String, TreeNode> treeNodeMap = treeRich.getMap();
TreeNode node = treeNodeMap.get(root.getTreeId());
while (node.getTreeType().equals(TreeType.NORMAL)) {
logger.info("决策到节点{}", node);
BaseFilter filter = map.get(node.getStrategy());
String realVal = filter.getRealVal(user);
String nextTreeId = filter.filter(realVal, node.getLinks());
if ("-1".equals(nextTreeId)) break;
node = treeNodeMap.get(nextTreeId);
}
return node;
}

}

public class TreeEngine extends BaseEngine {
@Override
public R process(TreeRich treeRich) {
TreeNode treeNode = this.doProcess(treeRich);
if (treeNode.getTreeType() != TreeType.LEAF) return new R(treeRich.getUser().getUserId(), "未能匹配结果");
return new R(treeRich.getUser().getUserId(), treeNode.getVal());
}
}

决策树的实现可以将复杂的判断交给内部进行实现,外部调用时依然是比较方便,体现了开闭原则,当有新的决策时只需要更换一颗新的决策树即可,此决策树的实现数据库存储也可以这样实现。

装饰器模式

装饰器模式主要是用于解决对某一个类进行补充或者增强,而非通过直接继承实现,通过创建一个抽象的装饰器类,即可非常简单的实现对类的扩充及其增强。这里以SpringMVC中的拦截器为例,对拦截器实现增强。

目录

1
2
3
4
5
6
7
8
9
10
11
12
13
│  Main.java

└─controller
│ MainConfig.java
│ MainController.java

├─decorator
│ InterceptorDecorator.java

└─interceptor
MainEnhanceInterceptor.java
MainInterceptor.java

1
2
3
4
5
6
7
8
9
//基类的拦截器
public class MainInterceptor implements HandlerInterceptor {

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return request.getHeader("A").equals("a");
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//装饰器
public abstract class InterceptorDecorator implements HandlerInterceptor {

protected HandlerInterceptor handlerInterceptor; //将其给构建进来

protected InterceptorDecorator(HandlerInterceptor handlerInterceptor) {
this.handlerInterceptor = handlerInterceptor;
}

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return this.handlerInterceptor.preHandle(request, response, handler);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
//增强实现
public class MainEnhanceInterceptor extends InterceptorDecorator {
public MainEnhanceInterceptor(HandlerInterceptor handlerInterceptor) {
super(handlerInterceptor);
}


@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
boolean handle = super.preHandle(request, response, handler);
if (!handle) return false;
return request.getHeader("B").equals("b");
}
}

门面模式

门面模式通常用于处理对外部提供统一的api之类,像springboot自定义starter就是一种门面模式的体现。

目录

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
│  pom.xml

├─app
│ │ pom.xml
│ │
│ ├─src
│ │ ├─main
│ │ │ ├─java
│ │ │ │ └─app
│ │ │ │ │ Main.java
│ │ │ │ │
│ │ │ │ ├─controller
│ │ │ │ │ MainController.java
│ │ │ │ │
│ │ │ │ └─entity
│ │ │ │ Res.java
│ │ │ │
│ │ │ └─resources
│ │ │ application.properties
│ │ │
│ │ └─test
│ │ └─java
│ └─target
│ ├─classes
│ │ │ application.properties
│ │ │
│ │ └─app
│ │ │ Main.class
│ │ │
│ │ ├─controller
│ │ │ MainController.class
│ │ │
│ │ └─entity
│ │ Res.class
│ │
│ └─generated-sources
│ └─annotations
└─share
│ pom.xml

├─src
│ ├─main
│ │ ├─java
│ │ │ ├─annotation
│ │ │ │ AInterceptor.java
│ │ │ │
│ │ │ ├─aop
│ │ │ │ Asp.java
│ │ │ │
│ │ │ └─config
│ │ │ Config.java
│ │ │ ConfigProperties.java
│ │ │ ConfigService.java
│ │ │
│ │ └─resources
│ │ └─META-INF
│ │ └─spring
│ │ org.springframework.boot.autoconfigure.AutoConfiguration.imports
│ │
│ └─test
│ └─java
└─target
│ share-2.7.5.jar

├─classes
│ ├─annotation
│ │ AInterceptor.class
│ │
│ ├─aop
│ │ Asp.class
│ │
│ ├─config
│ │ Config.class
│ │ ConfigProperties.class
│ │ ConfigService.class
│ │
│ └─META-INF
│ │ spring-configuration-metadata.json
│ │
│ └─spring
│ org.springframework.boot.autoconfigure.AutoConfiguration.imports

├─generated-sources
│ └─annotations
├─generated-test-sources
│ └─test-annotations
├─maven-archiver
│ pom.properties

├─maven-status
│ └─maven-compiler-plugin
│ ├─compile
│ │ └─default-compile
│ │ createdFiles.lst
│ │ inputFiles.lst
│ │
│ └─testCompile
│ └─default-testCompile
│ createdFiles.lst
│ inputFiles.lst

└─test-classes

通过springboot自定义sharestarter(命名不规范),内部通过aop实现参数验证,将包装好的工程交给app来测试。在自定义starter时需要注意将这个跳过。

1
2
3
4
5
6
7
8
9
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
</plugins>

代码细节就是aop的使用,均在项目中详细写出。

享元模式

享元模式在平常的开发中还是很常遇见的,通常为了节约对象资源,将对象中不变的属性保存起来,每次只对对象变得部分进行更改,redis的使用就是享元模式的体现。

目录

1
2
3
4
5
6
7
8
├─dy
│ RedisUtil.java

├─im
│ QueryFactory.java

└─re
Shop.java

QueryFactory是来缓存对象中不变的属性,RedisUtil用于保存对象动态变化的部分,在查询时两者结合就可以查询出完整的对象,大大节约了内存消耗,以及时间消耗。

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

private static ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1);


private static AtomicInteger stock = new AtomicInteger();

static {
executorService.scheduleAtFixedRate(() -> {
stock.addAndGet(1);
}, 0, 100000, TimeUnit.MICROSECONDS);
}


public static Integer getStock(Integer id) {
return stock.get();
}

}
1
2
3
4
5
6
7
8
9
10
11
12
public class QueryFactory {

public static final ConcurrentHashMap<Integer, Shop> map = new ConcurrentHashMap<>();

public static Shop get(Integer id) {
if (map.containsKey(id)) return map.get(id);
Shop shop = new Shop(id, "xx", null);
map.put(id, shop);
return shop;
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Shop {

Integer id;


String name;


Integer stock;

}

在有大量重复对象时,使用享元模式可以节约不少的空间,但是当动态变得多的时候,享元工厂就得好好设计下了,避免对象的构建变得太复杂。

代理模式

代理模式主要是用于一个程序(框架)对于使用者的易用性而服务的,在很多框架,组件中都有使用,Java中最常见的代理就是JDK代理和CGLIB代理,JDK代理只能代理接口,CGLIB代理可以代理接口和类。本次模拟MyBatis中使用接口就可以实现SQL语句的操作。

创建注解Select和接口UserMapper,并通过Spring配置文件的方式将MyBeanDefinitionRegistryPostProcessor注入为Bean,再通过实现BeanDefinitionRegistryPostProcessor接口的postProcessBeanDefinitionRegistry重新配置创建的Bean,将其配置为UserMapper代理,并在代理类中实现对于Select注解的匹配。代理的实现可以使用JDK或CGLIB。

目录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
├─java
│ ├─annotation
│ │ Select.java
│ │
│ ├─factory
│ │ MapperFactoryBean.java
│ │
│ ├─handler
│ │ │ MyBeanDefinitionRegistryPostProcessor.java
│ │ │
│ │ ├─cglib
│ │ │ CGLIBInvocationHandler.java
│ │ │
│ │ └─jdk
│ │ JDKInvocationHandler.java
│ │
│ └─mapper
│ UserMapper.java

└─resources
spring-config.xml
1
2
3
4
5
6
7
8
9
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"
default-autowire="byName">

<bean id="userMapper" class="handler.MyBeanDefinitionRegistryPostProcessor"/>

</beans>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public interface UserMapper {


@Select("""
select ...
""")
String selectUserInfo(String userId);

}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Select {

String value();
}
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
public class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
//在bean创建前可以对其操作(增删改查)
var beanDefinition = new RootBeanDefinition();
beanDefinition.setBeanClass(MapperFactoryBean.class);
beanDefinition.setScope("singleton");
var values = new MutablePropertyValues();
values.add("interfaceClass", UserMapper.class);
beanDefinition.setPropertyValues(values);
beanDefinitionRegistry.registerBeanDefinition("userMapper", beanDefinition);
}

@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
// do not anything
}
}
@SuppressWarnings("unchecked")
public class MapperFactoryBean<T> implements FactoryBean<T> {

Class<T> interfaceClass;


@Override
public T getObject() throws Exception {
// return (T) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class[] {interfaceClass}, new JDKInvocationHandler());
return (T) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class[] {interfaceClass}, new CGLIBInvocationHandler());
}

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

@Override
public boolean isSingleton() {
return true;
}

public void setInterfaceClass(Class<T> interfaceClass) {
this.interfaceClass = interfaceClass;
}
}
//代理实现
public class CGLIBInvocationHandler implements InvocationHandler {

@Override
public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
Select annotation = method.getAnnotation(Select.class);
String value = annotation.value();
return value + objects[0];
}
}

public class JDKInvocationHandler implements InvocationHandler {


@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Select annotation = method.getAnnotation(Select.class);
String value = annotation.value();
return value + args[0];
}
}

代理模式可以使得代码变得简洁,使用代理模式可以很轻松的扩展我们的业务逻辑,代理模式+门面模式经常在中间件开发中使用。

责任链模式

当某一个程序需要经历很多层计算或者是某个业务需要很多层审批的时候,使用责任链模式可以使代码变得简洁,易懂,这里模拟一道题目需要层层计算得到最终答案的流程。

目录

1
2
3
4
5
6
7
8
9
10
├─algorithm
│ Algorithm.java

├─impl
│ Alogrithm1.java
│ Alogrithm2.java
│ Alogrithm3.java

└─res
R.java

定义核心算法抽象类Algorithm,并在其中创建算法链,当每创建一个算法的时候就将该算法放置在链中,定义抽象方法calculate,基于实现类实现具体的算法细节,每次计算后都返回答案和是否需要进行下一轮计算。

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 abstract class Algorithm {
public static List<Algorithm> chain = new ArrayList<>();

protected static Logger logger = LoggerFactory.getLogger(Algorithm.class);

String name;



public Algorithm(String _name) {
name = _name;
chain.add(this);
}

public String getName() {return this.name;}

public abstract R calculate(String question);

public static R doCalculate(String question) {
for (var e : chain) {
R res = e.calculate(question);
if (res.haveNext()) question = res.answer();
else return res;
}
return new R(null,null, null);
}

public static void buildAlgorithmChain(Algorithm ... algorithms) {
chain.addAll(Arrays.asList(algorithms));
}

}
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 Alogrithm1 extends Algorithm {


public Alogrithm1(String _name) {
super(_name);
}

@Override
public R calculate(String question) {
boolean condition = false;
logger.info("执行" + getName() + "算法成功");
return new R("执行" + getName() + "算法成功", question + "*", condition);
}
}


public class Alogrithm2 extends Algorithm {

public Alogrithm2(String _name) {
super(_name);
}

@Override
public R calculate(String question) {
boolean condition = true;
logger.info("执行" + getName() + "算法成功");
return new R("执行" + getName() + "算法成功", question + "*", condition);
}
}

public class Alogrithm3 extends Algorithm {
public Alogrithm3(String _name) {
super(_name);
}

@Override
public R calculate(String question) {
boolean condition = true;
logger.info("执行" + getName() + "算法成功");
return new R("执行" + getName() + "算法成功", question + "*", condition);
}
}

责任链模式体现了单一职责和开闭原则,我们只需要定义出需要哪些层,而不需要懂得这些层具体细节,在外部调用的时候极大缩减了上手难度。

命令模式

命令模式的核心大致有两点,一个就是将参数以对象的形式进行传递,另一个就是将命令,执行者,调用者分开,调用者只需创建对象,统一化下达命令,其余有执行者进行实现。本次模拟开灯和关灯的操作使用命令模式实现。

目录

1
2
3
4
5
6
7
8
9
10
com
└─cm
│ ICommand.java
│ Invoker.java
│ Light.java

└─impl
TurnOffCommand.java
TurnOnCommand.java

定义Light类,里面实现开灯和关灯的方法。

1
2
3
4
5
6
7
8
9
10
11
public class Light {

public void turnOn() {
System.out.println("开灯");
}

public void turnOff() {
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 interface ICommand {


void execute();

}

public class TurnOffCommand implements ICommand {

private final Light light;
public TurnOffCommand(Light light) {
this.light = light;
}

@Override
public void execute() {
light.turnOff();
}
}

public class TurnOnCommand implements ICommand {

private final Light light;
public TurnOnCommand(Light light) {
this.light = light;
}

@Override
public void execute() {
light.turnOn();
}
}

实现Invoker

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Invoker {

private final Runnable action;


public Invoker(Runnable action) {
this.action = action;
}
public void invoke() {
action.run();
}

}

通过命令模式可以很好的将各层进行分开,互不干涉,需要其他的命令只需添加即可,无需改动当前的代码,体现了开闭原则和单一职责。

迭代器模式

迭代器模式在Java的集合体系中使用的相当广泛,每一个数据结构都实现了迭代功能,迭代器模式可以自定义遍历的方式,外部只需要要遍历无需关心遍历的细节。这里手撕一个堆来举例。

目录

1
MyHeap.java

Java本身就提供了迭代的接口Iterable,只需实现这个并重写里面的iterator方法即可,iterator方法返回的是一个Iterator对象,进而创建Iterator,实现里面的hasNextnext即可。关于堆的实现不做阐述,属于算法内容。

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
import java.util.Comparator;
import java.util.Iterator;


/**
* 堆是一个完全二叉树
* 小根堆的最小为根
* 大根堆的最大为根
* 存储: 使用一个数组来存, 1号点为根, 左儿子为2x,右儿子为2x+1
* 两个基本操作: down(x), up(x)
* 删除的时候有技巧: 用最后一个元素覆盖第一个元素, 然后删除最后一个元素
*/
public class MyHeap<T> implements Iterable<T>{


private Comparator<? super T> comparator;

private final T[] array;

private int size;

private int sum;

public MyHeap(int initSize, Comparator<T> comparator) {
this.comparator = comparator;
array = (T[]) new Object[initSize + 1];
}

public MyHeap(int maxSize) {
array = (T[]) new Object[maxSize + 1];
}


/**
*
* @param maxSize 最大容量
* @param heapifyArray 需要被heapify的数组
* 时间复杂度O(n)
*/
public MyHeap(int maxSize, T[] heapifyArray) {
array = (T[]) new Object[maxSize + 1];
System.arraycopy(heapifyArray, 1, array, 1, heapifyArray.length - 1);
size = heapifyArray.length;
for (int i = heapifyArray.length / 2; i >= 1; i--) down(i);
}

private int compare(T a, T b) {
if (comparator != null) return comparator.compare(a, b);
if (a instanceof Comparable<?>) return ((Comparable<? super T>) a).compareTo(b);
throw new UnsupportedOperationException("必须有一个比较规则");
}

/**
* 核心算法: 向下调整
* @param u 从此节点开始down
*/
private void down(int u) {
int t = u;
if (u * 2 <= size && compare(array[u * 2], array[u]) < 0) t = u * 2;
if (u * 2 + 1 <= size && compare(array[u * 2 + 1], array[t]) < 0) t = u * 2 + 1;
if (u != t) {
swap(u, t);
down(t);
}
}

/**
* 核心算法: 向上调整
* @param u 从此节点开始up
*/
private void up(int u) {
while (u / 2 > 0 && compare(array[u / 2], array[u]) > 0) {
swap(u, u / 2);
u /= 2;
}
}

private void swap(int i, int j) {
T temp = array[i];
array[i] = array[j];
array[j] = temp;
}

public T poll() {
if (size == 0) throw new IllegalStateException("堆是空的!");
T ret = array[1];
array[1] = array[size];
size--;
down(1);
if (ret instanceof Integer av) {
sum -= av;
}
return ret;
}

public void add(T v) {
if (size == array.length -1) throw new IllegalStateException("堆满了!");
size++;
array[size] = v;
up(size);
if (v instanceof Integer av) {
sum += av;
}

}

public boolean isEmpty() {
return size == 0;
}

public T peek() {
return array[1];
}

public int size() {
return size;
}

public int getSum() {
return sum;
}

@Override
public Iterator<T> iterator() {
return new Iterator<T>() {

int it = 1;

@Override
public boolean hasNext() {
return it <= size();
}

@Override
public T next() {
if (!hasNext()) throw new IllegalStateException("已经到尾了");
return array[it++];
}
};
}
}

通常情况下无需我们实现迭代器,但是在特殊情况下,根据业务需求还是可能用得到的。

中介者模式

中介者模式主要用于各个对象之间的解耦,当有多种多类的对象需要相互作用的时候,行为的关系将变得非常复杂,整个关系图会变得非常复杂,但是如果采用中介者模式进行优化的话,那么这个图将会变成一个星行图。

目录

1
2
3
4
5
6
7
8
9
10
11
├─lease
│ HouseLease.java
│ Lease.java

├─mediator
│ HouseMediator.java
│ Mediator.java

└─rent
HouseRent.java
Rent.java

抽象出租者、卖家、买家三个角色,通过中介者来凑合它们。

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
public abstract class Mediator {

protected static Deque<Rent> rentQueue = new ArrayDeque<>();

protected static Deque<Lease> leaseQueue = new ArrayDeque<>();



protected abstract void trade(Lease lease);

protected abstract void trade(Rent rent);
public void buy(Rent rent) {
rentQueue.add(rent);
trade(rent);
}

public void sell(Lease lease) {
leaseQueue.add(lease);
trade(lease);
}
}


public abstract class Rent {


protected String name;

protected Long money;

protected Mediator mediator; //miːdieɪtər/


protected Rent(String name, Long money, Mediator mediator) {
this.money = money;
this.name = name;
this.mediator = mediator;
}


public abstract void rent();

public String getName() {
return name;
}

public Long getMoney() {
return money;
}

}

public abstract class Lease {

protected String name;

protected Long money;

protected Mediator mediator; //miːdieɪtər/


protected Lease(String name, Long money, Mediator mediator) {
this.money = money;
this.name = name;
this.mediator = mediator;
}


public abstract void lease();


public String getName() {
return name;
}

public Long getMoney() {
return money;
}

}
//这里后来想了想,感觉把角色抽象成一个抽象类,行为定义成接口似乎更好一点。
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
public class HouseMediator extends Mediator{
@Override
protected void trade(Lease lease) {

Iterator<Rent> iterator = Mediator.rentQueue.iterator();
while (iterator.hasNext()) {
var r = iterator.next();
if (r.getName().equals(lease.getName()) && r.getMoney() >= lease.getMoney()) {
System.out.println("双方交易成功");
iterator.remove();
return;
}
}
System.out.println("暂无买家");
}

@Override
protected void trade(Rent rent) {
Iterator<Lease> iterator = Mediator.leaseQueue.iterator();
while (iterator.hasNext()) {
var l = iterator.next();
if (l.getName().equals(rent.getName()) && rent.getMoney() >= l.getMoney()) {
System.out.println("双方交易成功");
iterator.remove();
return;
}
}
System.out.println("暂无卖家");
}
}


public class HouseLease extends Lease{
public HouseLease(String name, Long money, Mediator mediator) {
super(name, money, mediator);
}

@Override
public void lease() {
super.mediator.sell(this);
}


}

public class HouseRent extends Rent{

public HouseRent(String name, Long money, Mediator mediator) {
super(name, money, mediator);
}

@Override
public void rent() {
super.mediator.buy(this);
}


}

在此案例中,出租房的中介者的行为就很明确,就是进行交易,无论是买家还是买家,它们只需要和中介者进行交流即可,无需构建复杂的关系图。

观察者模式

观察者模式又被称作发布订阅模式,当被观察者做出某种行为时,观察者也会做出一些行为,通知其他有关的对象。本案例模仿买票的时候,买完票一般都会有个短信通知。

目录

1
2
3
4
5
6
7
8
├─listener
│ Listener.java
│ ListenerManager.java
│ MessageListener.java

└─sell
Sell.java
SellImpl.java
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 interface Listener {

void doEvent(Long userId);

}

public class MessageListener implements Listener{
@Override
public void doEvent(Long userId) {
System.out.println(userId + "成功购买了一张票, 发个短信通知下他");
}
}

//监听器的管理者,可以支持订阅,取消订阅,推送
public final class ListenerManager {

public enum EventType {

MESSAGE

}

Map<Enum<EventType>, Listener> map = new HashMap<>();

public void subscribe(Enum<EventType> eventTypeEnum, Listener listener) {
map.put(eventTypeEnum, listener);
}

public void unsubscribe(Enum<EventType> eventTypeEnum) {
map.remove(eventTypeEnum);
}

public void notify(Enum<EventType> eventTypeEnum, Long userId) {
map.get(eventTypeEnum).doEvent(userId);
}

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

void sell(Long userId);

}
//在卖票成功的时候发布通知
public class SellImpl implements Sell{

ListenerManager manager;

public SellImpl() {
manager = new ListenerManager();
manager.subscribe(ListenerManager.EventType.MESSAGE, new MessageListener());
}

@Override
public void sell(Long userId) {
System.out.println("成功卖票");
manager.notify(ListenerManager.EventType.MESSAGE, userId);
}
}

通过观察者模式可以看出,当进行某一操作时,如果有需要和其关联的事件发生时,就能够通过简单的配置得以通知,并不需要写死代码。观察者模式在我们使用MQ时能够体现出。

状态机模式

状态机模式(State Pattern)是一种行为型设计模式,它允许一个对象在其内部状态改变时改变其行为。这种类型的设计模式属于状态模式的一种,它提供了一种方法来封装对象的状态变化以及转换的逻辑。状态机模式的核心思想是将一个复杂的问题分解为若干个状态,并将每个状态相关的行为封装在一个类的各个状态中。通过将行为与状态进行关联,状态机可以根据当前状态在运行时灵活地切换行为。本案例以一个活动的执行来举例。

目录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
├─r
│ ActivityInfo.java
│ DefaultRefuseResult.java
│ Result.java
│ Status.java

├─service
│ ActivityService.java

└─state
│ State.java
│ StateMachine.java

└─impl
CheckState.java
CloseState.java
DoingState.java
EditingState.java
OpenState.java
PassState.java
RefuseState.java

定义基本信息,活动详情,返回对象,状态枚举。

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

private String activityId; // 活动ID
private String activityName; // 活动名称
private Enum<Status> status; // 活动状态
private Date beginTime; // 开始时间
private Date endTime; // 结束时间

public String getActivityId() {
return activityId;
}

public void setActivityId(String activityId) {
this.activityId = activityId;
}

public String getActivityName() {
return activityName;
}

public void setActivityName(String activityName) {
this.activityName = activityName;
}

public Enum<Status> getStatus() {
return status;
}

public void setStatus(Enum<Status> status) {
this.status = status;
}

public Date getBeginTime() {
return beginTime;
}

public void setBeginTime(Date beginTime) {
this.beginTime = beginTime;
}

public Date getEndTime() {
return endTime;
}

public void setEndTime(Date endTime) {
this.endTime = endTime;
}
}

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


@Override
public boolean equals(Object obj) {
if (obj instanceof Result result) {
return Objects.equals(this.code, result.code) && this.info.equals(result.info);
}
return false;
}
}


public class DefaultRefuseResult extends Result{
public DefaultRefuseResult() {
super("0000", "当前状态不可达此状态");
}
}

public enum Status {

// 1创建编辑、2待审核、3审核通过(任务扫描成活动中)、4审核拒绝(可以撤审到编辑状态)、5活动中、6活动关闭、7活动开启(任务扫描成活动中)
Editing {
@Override
public Status defaultNextStatus(Status status) {
return Check;
}
},
Check {
@Override
public Status defaultNextStatus(Status status) {
return Pass;
}
},
Pass {
@Override
public Status defaultNextStatus(Status status) {
return Doing;
}
},
Refuse,
Doing {
@Override
public Status defaultNextStatus(Status status) {
return Close;
}
},
Close,
Open;

public Status defaultNextStatus(Status status) {
throw new IllegalStateException("未定义默认下一个状态");
}

}
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
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
//定义状态接口
public interface State {


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

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

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

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

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

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

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


default Result doDefaultNextAction(String activityId, Enum<Status> currentStatus) {
throw new IllegalStateException("没有下一个默认的操作");
}
}

//每种状态均实现,在其内部定义状态转变
public class CheckState implements State {


@Override
public Result arraignment(String activityId, Enum<Status> currentStatus) {
return new DefaultRefuseResult();
}

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

@Override
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 DefaultRefuseResult();
}

@Override
public Result close(String activityId, Enum<Status> currentStatus) {
return new DefaultRefuseResult();
}

@Override
public Result open(String activityId, Enum<Status> currentStatus) {
return new DefaultRefuseResult();
}

@Override
public Result doing(String activityId, Enum<Status> currentStatus) {
return new DefaultRefuseResult();
}

@Override
public Result doDefaultNextAction(String activityId, Enum<Status> currentStatus) {
return this.checkPass(activityId, currentStatus);
}
}

public class CloseState implements State {
@Override
public Result arraignment(String activityId, Enum<Status> currentStatus) {
return new DefaultRefuseResult();
}

@Override
public Result checkPass(String activityId, Enum<Status> currentStatus) {
return new DefaultRefuseResult();
}

@Override
public Result checkRefuse(String activityId, Enum<Status> currentStatus) {
return new DefaultRefuseResult();
}

@Override
public Result checkRevoke(String activityId, Enum<Status> currentStatus) {
return new DefaultRefuseResult();
}

@Override
public Result close(String activityId, Enum<Status> currentStatus) {
return new DefaultRefuseResult();
}

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

@Override
public Result doing(String activityId, Enum<Status> currentStatus) {
return new DefaultRefuseResult();
}
}

public class DoingState implements State {
@Override
public Result arraignment(String activityId, Enum<Status> currentStatus) {
return new DefaultRefuseResult();
}

@Override
public Result checkPass(String activityId, Enum<Status> currentStatus) {
return new DefaultRefuseResult();
}

@Override
public Result checkRefuse(String activityId, Enum<Status> currentStatus) {
return new DefaultRefuseResult();
}

@Override
public Result checkRevoke(String activityId, Enum<Status> currentStatus) {
return new DefaultRefuseResult();
}

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

@Override
public Result open(String activityId, Enum<Status> currentStatus) {
return new DefaultRefuseResult();
}

@Override
public Result doing(String activityId, Enum<Status> currentStatus) {
return new DefaultRefuseResult();
}

@Override
public Result doDefaultNextAction(String activityId, Enum<Status> currentStatus) {
return this.close(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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
//定义状态转换机
public class StateMachine {

private static final Map<Enum<Status>, State> stateMap = new HashMap<>();


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

public Result doDefaultNextAction(String activityId, Enum<Status> currentStatus) {
return stateMap.get(currentStatus).doDefaultNextAction(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
//service类
public class ActivityService {

private static final Map<String, Enum<Status>> currentStatus = new HashMap<>();



public static void init(String activityId, Enum<Status> initStatus) {
currentStatus.put(activityId, initStatus);
}

public static synchronized void execStatus(String activityId, Enum<Status> beforeStatus, Enum<Status> nextStatus) {
if (!currentStatus.get(activityId).equals(beforeStatus)) return;
currentStatus.put(activityId, nextStatus);
}

public Enum<Status> queryActivityStatus(String activityId) {
return currentStatus.get(activityId);
}

public ActivityInfo queryActivityInfo(String activityId) {
return new ActivityInfo() {{
setActivityId(activityId);
setActivityName("一个活动");
setBeginTime(new Date());
setEndTime(new Date());
setStatus(queryActivityStatus(activityId));
}};
}

}

状态机模式将状态和行为封装在各个状态类中,使得代码更加清晰和易于理解,将状态转换和行为分离,使得代码更加模块化,易于维护和扩展,允许通过添加新的状态类来扩展状态机的功能,而无需修改现有的代码,体现开闭原则。

策略模式

策略模式是一种行为型设计模式,主要用于解决在软件构建过程中,某些对象使用的算法可能多种多样,经常发生变化的问题。策略模式的核心思想是定义一系列算法,并将每个算法封装起来,使它们可以相互替换。这样,算法就可以独立于它们的客户变化,从而提高系统的灵活性和可维护性。本案例以营销时的折扣为场景。

目录

1
2
3
4
5
6
7
8
9
strategy
│ IStrategy.java

├─impl
│ DiscountStrategy.java
│ FullReductionStrategy.java

└─service
ShopService.java
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 interface IStrategy {


Double calculateDiscount(Double price);

}

public class DiscountStrategy implements IStrategy {

private Double discount;

public DiscountStrategy(Double discount) {
this.discount = discount;
}

@Override
public Double calculateDiscount(Double price) {
return discount * price;
}
}

public class FullReductionStrategy implements IStrategy {
@Override
public Double calculateDiscount(Double price) {
if (price >= 1000) return price - 50;
if (price >= 200) return price - 200;
return price;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//在service类中传入策略
public class ShopService {

private IStrategy strategy;


public ShopService(IStrategy strategy) {
this.strategy = strategy;
}

public Double calculatePrice(Double money) {
return strategy.calculateDiscount(money);
}

}

策略模式是一种很常见的设计模式,使用策略模式可以使代码的扩展性大大提高,使用策略的时候只需要简单配置就可以进行切换。

模板模式

模板模式也是一种很常见的设计模式,模板模式是基于抽象类定义某一种方法或行为的基本准则,实现类只需实现其中的核心细节,而无需关心流程顺序及其他。这里以实现负载均衡常见的一致性hash算法为例。

目录

1
2
3
4
5
template
ArrayListOfConsistentHash.java
BaseConsistentHash.java
TreeMapOfConsistentHash.java

一致性哈希(Consistent Hashing)是一种分布式哈希技术,它可以在节点数量发生变化时,最小化数据重新分布的次数。在一致性哈希中,哈希环是一个连续的哈希值空间,每个节点和数据都映射到这个空间中。当需要添加或删除节点时,只需要重新分配哈希环上相邻节点之间的数据,而不需要重新分配整个哈希环上的数据。

一致性哈希的优点是,当节点数量发生变化时,只需要重新分配哈希环上相邻节点之间的数据,而不需要重新分配整个哈希环上的数据。这样可以大大减少数据重新分布的次数,提高系统的性能和可扩展性。

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
//定义基本一致性hash类,把通用的方法和模板流程定义好,子类只需要实现核心的方法即可。
public abstract class BaseConsistentHash {

protected abstract void add(long key, String value);

protected static final Logger logger = LoggerFactory.getLogger(BaseConsistentHash.class);

protected void sort() {}


protected abstract String getFirstNodeValue(String key);


public final synchronized String process(List<String> values, String key) {
processBefore();
for (String value : values) {
add(hash(value), value);
}
sort();
return getFirstNodeValue(key);
}

protected abstract void processBefore();


public Long hash(String value) {
MessageDigest md5;
try {
md5 = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("MD5 not supported", e);
}
md5.reset();
byte[] keyBytes;
keyBytes = value.getBytes(StandardCharsets.UTF_8);

md5.update(keyBytes);
byte[] digest = md5.digest();

// hash code, Truncate to 32-bits
long hashCode = ((long) (digest[3] & 0xFF) << 24)
| ((long) (digest[2] & 0xFF) << 16)
| ((long) (digest[1] & 0xFF) << 8)
| (digest[0] & 0xFF);

return hashCode & 0xffffffffL;
}

}

public class ArrayListOfConsistentHash extends BaseConsistentHash {

record Pair(
Long key,
String value
) {
}

private final ArrayList<Pair> list = new ArrayList<>();

@Override
protected void sort() {
list.sort(Comparator.comparing(Pair::key).thenComparing(Pair::value));
}

@Override
protected void add(long key, String value) {
//先搞几个虚拟节点
int NODE_SIZE = 2;
for (int i = 0; i < NODE_SIZE; i++) {
list.add(new Pair(super.hash("node" + i + key), value));
}
list.add(new Pair(key, value));
}

@Override
protected String getFirstNodeValue(String key) {
//基于二分实现
Long hash = hash(key);
int l = 0, r = list.size();
while (l < r) {
int mid = l + r >> 1;
if (list.get(mid).key >= hash) r = mid;
else l = mid + 1;
}
return list.get(l).value;
}

@Override
protected void processBefore() {
list.clear();
logger.info("只需arraylist实现的一致性hash");
}
}

public class TreeMapOfConsistentHash extends BaseConsistentHash{


private final TreeMap<Long, String> treeMap = new TreeMap<>();


private static final int NODE_SIZE = 2;

/**
* 加入一些虚拟节点
* @param key key
* @param value value
*/
@Override
protected void add(long key, String value) {
for (int i = 0; i < NODE_SIZE; i++) {
treeMap.put(super.hash("node" + key + i), value);
}
treeMap.put(super.hash(String.valueOf(key)), value);
}

/**
* @param key key
* @return value
*/
@Override
protected String getFirstNodeValue(String key) {

Long hash = super.hash(key);
SortedMap<Long, String> last = treeMap.tailMap(hash); //这个方法返回从这里开始向后的元素(包含这个)
if (!last.isEmpty()) {
return last.get(last.firstKey());
}
if (treeMap.isEmpty()) throw new IllegalStateException("not support");
return treeMap.firstEntry().getValue();
}

/**
*
*/
@Override
protected void processBefore() {
treeMap.clear();
logger.info("只需arraylist实现的一致性hash");
}
}

模板模式非常适用于一些同类化的实现,模板定义好,只需要实现类实现核心的东西就可以。基本每个框架中我们都可以看到模板模式的身影。

访问者模式

此设计模式非常高级,也是比较难理解的,它允许在不改变其类的情况下在一个类群上尽可能的添加新的操作。它用于封装一些应用于某种数据结构中的各元素的操作。这里以公司内对不同职位员工的工资发放为例。

1
2
3
4
5
6
7
8
9
10
11
│  SalaryStructure.java

└─ele
BonusVisitor.java
Element.java
Employee.java
Leader.java
OutsourcedEmployee.java
SalaryVisitor.java
Visitor.java

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
//定义element基类,也可以是抽象类
public interface Element {
void accept(Visitor visitor); //通常都为accept方法,不知道为什么
}

public class Employee implements Element {

int monthlySalary = 3000;

int bonus = 500;

@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}

}

public class Leader implements Element {

int monthlySalary = 5000;
int bonus = 100;

@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}

}

public class OutsourcedEmployee implements Element {

int hourlyRate = 100;

int bonus = 30;

@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}

}

//定义visitor接口
public interface Visitor {
void visit(Leader leader);
void visit(Employee employee);
void visit(OutsourcedEmployee outsourcedEmployee);
}

//实现两种visitor
public class SalaryVisitor implements Visitor {

@Override
public void visit(Leader leader) {
// 假定领导有2倍的基础薪水和额外的奖金
int salary = leader.monthlySalary * 2 + 1000;
System.out.println("Pay " + salary + " to leader");
}

@Override
public void visit(Employee employee) {
int salary = employee.monthlySalary;
System.out.println("Pay " + salary + " to employee");
}

@Override
public void visit(OutsourcedEmployee outsourcedEmployee) {
// 假定外包员工按小时支付,每月工作160小时
int salary = outsourcedEmployee.hourlyRate * 160;
System.out.println("Pay " + salary + " to outsourced employee");
}
}

public class BonusVisitor implements Visitor {
@Override
public void visit(Leader leader) {
System.out.println("Pay " + leader.bonus + " to leader");
}

@Override
public void visit(Employee employee) {
System.out.println("Pay " + employee.bonus + " to employee");
}

@Override
public void visit(OutsourcedEmployee outsourcedEmployee) {
System.out.println("Pay " + outsourcedEmployee.bonus + " to outsourcedEmployee");
}
}

//定义数据看板
public class SalaryStructure {
private List<Element> elements;

public SalaryStructure() {
elements = new ArrayList<>();
// 这部分可以根据实际需求来生成对象
elements.add(new Leader());
elements.add(new Employee());
elements.add(new OutsourcedEmployee());
}

public void show(Visitor visitor) {
for (Element element : elements) {
element.accept(visitor);
}
}
}

从此案例中可以看出,通过访问者模式拓展了计算工资和奖金的功能。但是访问者模式它的设计还是比较复杂难懂的。访问者模式通过将数据结构和操作分离,提供了一种灵活、可扩展、易于维护的解决方案,适用于需要对数据结构进行多种不同操作的场景。(不懂这句话)

总结

设计模式就这样草草结束了…


设计模式
http://example.com/2024/03/20/设计模式/
作者
ykexc
发布于
2024年3月20日
许可协议