简介

本篇将使用大白话加自己的理解详细介绍23种设计模式

什么是设计模式

设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。 毫无疑问,设计模式于己于他人于系统都是多赢的,设计模式使代码编制真正工程化,设计模式是软件工程的基石,如同大厦的一块块砖石一样。项目中合理的运用设计模式可以完美的解决很多问题,每种模式在现在中都有相应的原理来与之对应,每一个模式描述了一个在我们周围不断重复发生的问题,以及该问题的核心解决方案,这也是它能被广泛应用的原因。简单说:

  • 模式:在某些场景下,针对某类问题的某种通用的解决方案
  • 场景:项目所在的环境
  • 问题:约束条件,项目目标等
  • 解决方案:通用、可复用的设计,解决约束达到目标

设计模式的三个分类

  • 创建型模式:对象实例化的模式,创建型模式用于解耦对象的实例化过程。
  • 结构型模式:把类或对象结合在一起形成一个更大的结构。
  • 行为型模式:类和对象如何交互,及划分责任和算法。

各分类中模式的关键点

创建型模式

  • 单例模式:某个类只能有一个实例,提供一个全局的访问点。
  • 简单工厂:一个工厂类根据传入的参量决定创建出那一种产品类的实例。
  • 工厂方法:定义一个创建对象的接口,让子类决定实例化那个类。
  • 抽象工厂:创建相关或依赖对象的家族,而无需明确指定具体类。
  • 建造者模式:封装一个复杂对象的构建过程,并可以按步骤构造。
  • 原型模式:通过复制现有的实例来创建新的实例。

结构型模式

  • 适配器模式:将一个类的方法接口转换成客户希望的另外一个接口。
  • 组合模式:将对象组合成树形结构以表示“”部分-整体“”的层次结构。
  • 装饰模式:动态的给对象添加新的功能。
  • 代理模式:为其他对象提供一个代理以便控制这个对象的访问。
  • 亨元(蝇量)模式:通过共享技术来有效的支持大量细粒度的对象。
  • 外观模式:对外提供一个统一的方法,来访问子系统中的一群接口。
  • 桥接模式:将抽象部分和它的实现部分分离,使它们都可以独立的变化。

行为型模式

  • 模板模式:定义一个算法结构,而将一些步骤延迟到子类实现。
  • 解释器模式:给定一个语言,定义它的文法的一种表示,并定义一个解释器。
  • 策略模式:定义一系列算法,把他们封装起来,并且使它们可以相互替换。
  • 状态模式:允许一个对象在其对象内部状态改变时改变它的行为。
  • 观察者模式:对象间的一对多的依赖关系。
  • 备忘录模式:在不破坏封装的前提下,保持对象的内部状态。
  • 中介者模式:用一个中介对象来封装一系列的对象交互。
  • 命令模式:将命令请求封装为一个对象,使得可以用不同的请求来进行参数化。
  • 访问者模式:在不改变数据结构的前提下,增加作用于一组对象元素的新功能。
  • 责任链模式:将请求的发送者和接收者解耦,使的多个对象都有处理这个请求的机会。
  • 迭代器模式:一种遍历访问聚合对象中各个元素的方法,不暴露该对象的内部结构。

详解

创建型模式

创建型模式的作用就是创建对象,说到创建一个对象,最熟悉的就是 new 一个对象,然后 set 相关属性。但是,在很多场景下,我们需要给客户端提供更加友好的创建对象的方式,尤其是那种我们定义了类,但是需要提供给其他开发者用的时候。

简单工厂模式

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
/**
* 简单工厂模式
* @author KThirty
* @since 2020-5-7
* 简单地说,简单工厂模式通常就是这样,一个工厂类 XxxFactory,里面有一个静态方法,根据我们不同的参数,返回不同的派生自同一个父类(或实现同一接口)的实例对象。
*/
// 汽车父类
interface Car{public void whoAmI();}
// 宝马
class BMWCar implements Car {
@Override
public void whoAmI() { System.out.println("I`m BWM");}
}
// 奔驰
class BenzCar implements Car{
@Override
public void whoAmI() { System.out.println("I`m Benz"); }
}

public class SimpleFactory {
public static Car create(String name){
if("Benz".equals(name)){
return new BenzCar();
}
if("BWM".equals(name)){
return new BMWCar();
}
throw new InvalidParameterException("参数异常");
}

public static void main(String[] args) {
SimpleFactory.create("Benz").whoAmI(); // 输出I`m Benz
SimpleFactory.create("BWM").whoAmI(); // 输出I`m BWM
}
}

工厂模式

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
/**
* 工厂模式
* @author KThirty
* @since 2020-5-7
* 这个其实和简单工厂模式差不太多,就是将工厂继续拆分,单一分工,达到更明细的产品生产
*/
public class Factory {
public static void main(String[] args) {
CarFactory carFactory1 = new ChineseFactory();
CarFactory carFactory2 = new GermanyFactory();
carFactory1.create("Benz").whoAmI(); // I`m Chinese Benz
carFactory1.create("BWM").whoAmI(); // I`m Chinese BWM
carFactory2.create("Benz").whoAmI(); // I`m Germany Benz
carFactory2.create("BWM").whoAmI(); // I`m Germany BWM
}
}
// 国产奔驰
class ChineseBenz implements Car{
@Override
public void whoAmI() { System.out.println("I`m Chinese Benz"); }
}
// 进口奔驰
class GermanyBenz implements Car{
@Override public void whoAmI() { System.out.println("I`m Germany Benz"); }
}
// 国产宝马
class ChineseBWM implements Car{
@Override
public void whoAmI() { System.out.println("I`m Chinese BWM"); }
}
// 进口宝马
class GermanyBWM implements Car{
@Override public void whoAmI() { System.out.println("I`m Germany BWM"); }
}
// 工厂基类
interface CarFactory{
public Car create(String name);
}
// 国内工厂
class ChineseFactory implements CarFactory{
@Override
public Car create(String name) {
if("Benz".equals(name)){
return new ChineseBenz();
}
if("BWM".equals(name)){
return new ChineseBWM();
}
throw new InvalidParameterException("参数异常");
}
}
// 德国工厂
class GermanyFactory implements CarFactory{
@Override
public Car create(String name) {
if("Benz".equals(name)){
return new GermanyBenz();
}
if("BWM".equals(name)){
return new GermanyBWM();
}
throw new InvalidParameterException("参数异常");
}
}

抽象工厂模式

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
/**
* 抽象工厂模式
* @author KThirty
* @since 2020-5-7
* 抽象工厂模式主要设计产品组的概念,就是某一个工厂生产出配套的一系列产品。
* 比如,一个奔驰的工厂,可以生产轮胎,车架,发动机....等零件,再由三方进行拼装
*/
public class AbstractFactory {
public static void main(String[] args) {
// 选择一个工厂
AssemblyCarFactory assemblyCarFactory = new BenzAssemblyCarFactory();
Engine engine = assemblyCarFactory.createEngine();
Frame frame = assemblyCarFactory.createFrame();
Tire tire = assemblyCarFactory.createTire();
// 组装成车 , 全奔驰配件的组装车
AssemblyCar benzAssemblyCar = new AssemblyCar(engine, frame, tire);
System.out.println(benzAssemblyCar);// AssemblyCar(engine=Engine(name=Benz Engine), frame=Frame(name=Benz Frame), tire=Tire(name=Benz Tire))

}
}
// 奔驰组装车工厂
class BenzAssemblyCarFactory implements AssemblyCarFactory{
public Tire createTire(){
return new BenzTire();
}
public Frame createFrame(){
return new BenzFrame();
}
public Engine createEngine(){
return new BenzEngine();
}
}

// 组装车工程父类(定义配件)
interface AssemblyCarFactory{
public Tire createTire();
public Frame createFrame();
public Engine createEngine();
}

// 轮胎
@Data
@AllArgsConstructor
class Tire{
private String name;
}
class BenzTire extends Tire{
public BenzTire() { super("Benz Tire"); }
}

// 车架
@Data
@AllArgsConstructor
class Frame{
private String name;
}
class BenzFrame extends Frame{
public BenzFrame() { super("Benz Frame"); }
}
// 引擎
@Data
@ToString
@AllArgsConstructor
class Engine{
private String name;
}
class BenzEngine extends Engine{ public BenzEngine() { super("Benz Engine"); } }
@Data
@AllArgsConstructor
@ToString
class AssemblyCar {
private Engine engine;
private Frame frame;
private Tire tire;
}

单例模式

单例模式(Singleton Pattern)是 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
/**
* 单例模式
* @author KThirty
* @since 2020-5-7
* 单例模式又分 饿汉模式、饱汉模式、嵌套模式
*/
public class Singleton {
public static void main(String[] args) {
Singleton1.getInstance().say();// 输出:饿汉模式
Singleton2.getInstance().say();// 输出: 饱汉模式(线程安全)
Singleton3.getInstance().say();// 输出:嵌套类模式
}
}

/**
* 饿汉模式:饿汉模式,很饿很着急,所以类加载时即创建实例对象,但是加载比较慢,获取对象比较快。
*/
class Singleton1{
// 构造方法私有化,禁止外部调用
private Singleton1() {};
// 创建私有静态实例,意味着这个类第一次使用的时候就会进行创建
private static Singleton1 instance = new Singleton1();
public static Singleton1 getInstance() {
return instance;
}
public void say(){ System.out.println("饿汉模式"); }
/**
* 本方法无意义,仅为了演示一种情况
* 随便写一个静态方法,假如现在只想要调用静态方法(Singleton1.getData),并不需要生成Singleton1实例,但是没办法,已经生成了
* 这样就造成了资源的浪费
*/
public static String getData(String mode) {return mode;}
}

/**
* 饱汉模式(线程安全): 饱汉模式,很饱不着急,延迟加载,啥时候用啥时候创建实例,存在线程安全问题
*/
class Singleton2{
// 构造方法私有化,禁止外部调用
private Singleton2() {};
// 和饿汉模式相比,这边不需要先实例化出来,注意这里的 volatile,它是必须的
private static volatile Singleton2 instance = null;
public static Singleton2 getInstance() {
if (instance == null) {
// 加锁
synchronized (Singleton2.class) {
// 这一次判断也是必须的,不然会有并发问题
if (instance == null) {
instance = new Singleton2();
}
}
}
return instance;
}
public void say(){
System.out.println("饱汉模式(线程安全)");
}
}

/**
* 嵌套类模式: 利用嵌套类可以访问外部类的静态属性和静态方法
*/
class Singleton3 {
// 构造方法私有化,禁止外部调用
private Singleton3() {}
// 主要是使用了 嵌套类可以访问外部类的静态属性和静态方法 的特性
private static class Holder {
private static Singleton3 instance = new Singleton3();
}
public static Singleton3 getInstance() {
return Holder.instance;
}
public void say(){
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
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
/**
* 建造者模式Builder
* 套路就是先 new 一个 Builder,然后可以链式地调用一堆方法,最后再调用一次 build() 方法,我们需要的对象就有了。
* 其实就是有一个专门的builder用来构建对象
*/
public class Builder {
public static void main(String[] args) {
User user = User.builder().name("张三").password("123456").build();
System.out.println(user); // User(name=张三, password=123456)
// lombok自动生成版
User1 user1 = User1.builder().name("李四").password("123").build();
System.out.println(user1);// User1(name=李四, password=123)
}
}
@Data
@ToString
class User{
private String name;
private String password;
private User(String name,String password){
this.name = name;
this.password = password;
}
static class UserBuilder {
private String name;
private String password;
public UserBuilder name(String name){
this.name = name;
// 链式调用
return this;
}
public UserBuilder password(String password){
this.password = password;
return this;
}
public User build(){
if (name == null || password == null) {
throw new RuntimeException("用户名和密码必填");
}
return new User(name, password);
}
}
public static UserBuilder builder() {
return new UserBuilder();
}

}

/**
* Lombok自动生成builder
*/
@Data
@ToString
@lombok.Builder
class User1 {
private String name;
private String password;

private User1(String name, String password) {
this.name = name;
this.password = password;
}
}

原型模式

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
/**
* 原型模式:这个用的很少
* 作用是:用于创建重复对象。需要实现Cloneable 可以选择重写clone()方法。clone分为浅克隆和深克隆。浅克隆只是克隆引用,对象还是一个。深克隆是对象也新创建了一个
*/
public class Prototype {
public static void main(String[] args) {
CloneableUser user5 = CloneableUser.builder().name("王五").password("000").build();
CloneableUser user6 = user5.clone();
System.out.println(user5); // CloneableUser(name=王五, password=000)
user5.setName("王五-1");
System.out.println(user5); // CloneableUser(name=王五-1, password=000)
System.out.println(user6); // CloneableUser(name=王五, password=000)
}
}
@Data
@Builder
@AllArgsConstructor
class CloneableUser {
private String name;
private String password;
@Override
protected CloneableUser clone() {
return new CloneableUser(this.name,this.password);
}
}

创建型模式总结

创建型模式总体上比较简单,它们的作用就是为了产生实例对象,算是各种工作的第一步了,因为我们写的是面向对象的代码,所以我们第一步当然是需要创建一个对象了。简单工厂模式最简单;工厂模式在简单工厂模式的基础上增加了选择工厂的维度,需要第一步选择合适的工厂;抽象工厂模式有产品族的概念,如果各个产品是存在兼容性问题的,就要用抽象工厂模式。单例模式就不说了,为了保证全局使用的是同一对象,一方面是安全性考虑,一方面是为了节省资源;建造者模式专门对付属性很多的那种类,为了让代码更优美,也可以更好的做变量检查;原型模式用得最少,了解和 Object 类中的 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
34

/**
* 代理模式
* 用一个代理来隐藏具体实现类的实现细节,通常还用于在真实的实现的前后添加一部分逻辑。
* 既然说是代理,那就要对客户端隐藏真实实现,由代理来负责客户端的所有请求。当然,代理只是个代理,它不会完成实际的业务逻辑,而是一层皮而已,但是对于客户端来说,它必须表现得就是客户端需要的真实实现。
*/
public class ProxyMode {
public static void main(String[] args) {
SayHelloProxy sayHelloProxy = new SayHelloProxy();
sayHelloProxy.say();
// 开始SayHello
// Hello, I`m SayHelloServiceImpl!
// 结束SayHello
}
}
interface ISayHelloService{
public void say();
}
class SayHelloServiceImpl implements ISayHelloService{
@Override
public void say() {
System.out.println("Hello, I`m SayHelloServiceImpl!");
}
}
// 代理类,实际逻辑不变,一般在前后加其他操作(日志记录,耗时记录)
class SayHelloProxy implements ISayHelloService{
private ISayHelloService sayHelloService = new SayHelloServiceImpl();
@Override
public void say() {
System.out.println("开始SayHello");
sayHelloService.say();
System.out.println("结束SayHello");
}
}

适配器模式

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
/**
* 适配器模式
* 分为:默认适配器模式、对象适配器模式、类适配器模式三类
* 适配器模式做的就是,有一个接口需要实现,但是我们现成的对象都不满足,需要加一层适配器来进行适配。
*/
public class AdapterMode {
public static void main(String[] args) {
// ---------- 默认适配器
// I`m Adapter Func1
// I`m Func1
new DefaultAdapter().func1();
// ----------- 默认适配器结束
}
}
// ---------- 默认适配器开始-------------------
/**
* 默认适配器模式,一般用于顶级接口有很多接口,但是我们现在并不需要实现所有接口,可以加一个适配器,默认实现接口但是什么都不做
*/
class DefaultAdapter extends TopInterfaceAdapter{
@Override
public void func1() {
super.func1();
System.out.println("I`m Func1");
}
}
interface TopInterface{
void func1();
void func2();
void func3();
}
// 适配器
class TopInterfaceAdapter implements TopInterface{
@Override
public void func1() {
System.out.println("I`m Adapter Func1");
}
@Override
public void func2() { }
@Override
public void func3() { }
}
// ---------- 默认适配器结束-------------------
// ----------- 对象适配器开始-----------

/**
* 对象适配器,一般用来将一个对象转换为其他类型对象,下面将用一个鸡和一个鸭来演示如何用适配器将鸡适配成鸭
*/
class ObjectAdapter{
public static void main(String[] args) {
Cock cock = new WildCock();
// 创建适配器,将野鸡转换为鸭类
Duck duck = new CockAdapter(cock);
duck.fly(); // 鸡也会飞哦
duck.quack(); // 咕咕叫
}
}
interface Duck {
public void quack(); // 鸭的呱呱叫
public void fly(); // 飞
}
interface Cock {
public void gobble(); // 鸡的咕咕叫
public void fly(); // 飞
}
// 野鸡
class WildCock implements Cock {
public void gobble() {
System.out.println("咕咕叫");
}
public void fly() {
System.out.println("鸡也会飞哦");
}
}
/*
现在假设有一只野鸡,怎么样才能将它适配成鸭类呢,fly方法已经有了,但是没有鸭的quack方法,所以需要实现这个方法
*/
@AllArgsConstructor
class CockAdapter implements Duck{
// 鸡类
Cock cock;
@Override
public void quack() {
// 实际是鸡叫
cock.gobble();
}

@Override
public void fly() {
cock.fly();
}
}
// ----------- 对象适配器结束-----------
// ----------- 类适配器开始-----------

/**
* 类适配器其实就是通过继承的形式,将顶级接口的某些方法默认实现
*/
interface Target{
void func1();
void func2();
void func3();
}
class SomeThing{
public void func1(){
System.out.println("I`m SomeThing Func1");
}
public void func2(){
System.out.println("I`m SomeThing Func2");
}
}
class TargetAdapter extends SomeThing implements Target{
@Override
public void func3() {
System.out.println("I`m TargetAdapter func3");
}
}
class ClassAdapter{
public static void main(String[] args) {
Target target = new TargetAdapter();
target.func1();// I`m SomeThing Func1
target.func2();// I`m SomeThing Func2
target.func3();// I`m TargetAdapter func3
}
}
// ---------------类适配器结束---------------

总结:

  • 类适配和对象适配的异同:
    • 一个采用继承,一个采用组合;
    • 类适配属于静态实现,对象适配属于组合的动态实现,对象适配需要多实例化一个对象。
    • 总体来说,对象适配用得比较多。
  • 适配器模式和代理模式的异同:
    • 比较这两种模式,其实是比较对象适配器模式和代理模式,在代码结构上,它们很相似,都需要一个具体的实现类的实例。但是它们的目的不一样,代理模式做的是增强原方法的活;适配器做的是适配的活,为的是提供“把鸡包装成鸭,然后当做鸭来使用”,而鸡和鸭它们之间原本没有继承关系。

桥接模式

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
package top.kthirty.structural;

/**
* @author KThirty
* @since 2020-5-8
* 桥接模式: 桥接模式的目的是为了将抽象部分与实现部分解耦,可以将一个 N * N 的系统进行拆分,减少类的数量。
* 比如我们现在要画红色矩形、蓝色矩形、红色圆形、蓝色圆形 ,这样形状和颜色就形成了一个N*N 的系统
* 普通形式的话,我们需要一共四个类实现各种画法,且不利于扩展
* 桥接模式的优点是: 优秀的扩展能力
* 缺点很明显:会增加系统的理解与设计难度
*/
public class BridgeMode {
public static void main(String[] args) {
// 蓝色圆形 , new 一个圆形传入蓝色笔,就可以画出来蓝色圆形
Shape blueCircle = new Circle(10, new BluePen());
blueCircle.draw();// 用蓝色笔画图,radius:10, x:0, y:0
Shape redCircle = new Circle(10, new RedPen());
redCircle.draw(); // 用红色笔画图,radius:10, x:0, y:0
Shape blueRectangle = new Rectangle(1,2, new BluePen());
blueRectangle.draw(); // 用蓝色笔画图,radius:0, x:1, y:2
Shape redRectangle = new Rectangle(1,2, new RedPen());
redRectangle.draw(); // 用红色笔画图,radius:0, x:1, y:2
}
}

// 我们首先需要一个桥梁,它是一个接口,定义提供的接口方法。也就是画笔的父类
interface DrawAPI {
public void draw(int radius, int x, int y);
}

// 红色笔
class RedPen implements DrawAPI {
@Override
public void draw(int radius, int x, int y) {
System.out.println("用红色笔画图,radius:" + radius + ", x:" + x + ", y:" + y);
}
}

// 蓝色笔
class BluePen implements DrawAPI {
@Override
public void draw(int radius, int x, int y) {
System.out.println("用蓝色笔画图,radius:" + radius + ", x:" + x + ", y:" + y);
}
}

// 后续如果需要增加颜色直接添加实现类即可
// 定义一个抽象类用来画的动作,可以传入不同颜色的画笔
abstract class Shape {
protected DrawAPI drawAPI;
protected Shape(DrawAPI drawAPI) {
this.drawAPI = drawAPI;
}
public abstract void draw();
}

// 圆形
class Circle extends Shape {
private int radius;
public Circle(int radius, DrawAPI drawAPI) {
super(drawAPI);
this.radius = radius;
}
public void draw() {
drawAPI.draw(radius, 0, 0);
}
}

// 长方形
class Rectangle extends Shape {
private int x;
private int y;

public Rectangle(int x, int y, DrawAPI drawAPI) {
super(drawAPI);
this.x = x;
this.y = y;
}
public void draw() {
drawAPI.draw(0, x, y);
}
}

装饰模式

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
/**
* 装饰模式:给某个类添加装饰(其实就是添加某种功能)
* 类似代理模式,但是代理模式用来做与原业务无关的事,不会修改原业务,装饰模式则是用来修改原业务的
* 比如我们现在有个制造奶茶的类,如果需要记录制造耗时,则使用代理模式
* 如果需要在奶茶添加佐料或者添加其他附加品则使用装饰模式
* 装饰模式一般用在功能迭代上
*/
public class DecorativeMode {
public static void main(String[] args) {
// 红茶加柠檬
beverage = new Lemon(new RedTea());
System.out.println(beverage.getDescription());// 红茶加柠檬
System.out.println(beverage.cost());// 12.0
}
}
// 抽象饮料类
abstract class Beverage{
// 返回描述
public abstract String getDescription();
// 返回价格
public abstract double cost();
}
// 红茶
class RedTea extends Beverage{
@Override
public String getDescription() {
return "红茶";
}
@Override
public double cost() {
return 10;
}
}

// 定义调料,也就是装饰者的基类,此类必须继承自 饮料类:
abstract class Condiment extends Beverage { }
// 柠檬调料
@AllArgsConstructor
class Lemon extends Condiment{
private Beverage beverage;
@Override
public String getDescription() {
return beverage.getDescription()+"加柠檬";
}
@Override
public double cost() {
// 加柠檬另加两元
return beverage.cost()+2;
}
}

门面模式(又叫外观模式)

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 FacadeMode {
public static void main(String[] args) {
ShapeMaker shapeMaker = new ShapeMaker();
shapeMaker.drawRedCircle(); // 用红色笔画图,radius:10, x:0, y:0
shapeMaker.drawRedRectangle(); // 用红色笔画图,radius:0, x:1, y:2
}
}
/**
* 方便起见,就直接使用桥接模式中定义好的类了,主要是为了解释门面模式的用法
*/
class ShapeMaker{
private Shape redCircle;
private Shape redRectangle;
public ShapeMaker() {
redCircle = new Circle(10,new RedPen());
redRectangle = new Rectangle(1,2,new RedPen());
}
// 画红色圆
public void drawRedCircle(){
redCircle.draw();
}
// 画红色矩形
public void drawRedRectangle(){
redRectangle.draw();
}
}

组合模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* 组合模式:组合模式是将存在某种包含关系的数据组织在一起,
* 典型的例子就是树状结构。例如菜单功能,一个菜单除了自己该有的属性,还可能包含子菜单;
*/
public class PortfolioMode {
public static void main(String[] args) {
Menu menu = new Menu("测试一", new ArrayList<>()).addChild(new Menu("测试二", new ArrayList<>()));
System.out.println(menu);
}
}
@Data
@ToString
@AllArgsConstructor
class Menu{
private String name;
private List<Menu> childMenus;
public Menu addChild(Menu menu){
childMenus.add(menu);
return this;
}
}

享元模式

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
/**
* 享元模式:享元模式尽可能的让用户复用已经有的对象,从而避免造成反复创建对象的资源浪费。
* 比如Java字符串常量池,核心其实就是减少资源浪费
*/
public class FlyweightPattern {
//定义一个池子
private static Map<String,Flyweight> pool= new HashMap<String,Flyweight>();
public static Flyweight getFlyweight(String subject){
Flyweight flyweight;
// 不存在则添加,存在就复用
if (pool.containsKey(subject)){
flyweight=pool.get(subject);
}else {
flyweight = new RealFlyweight(subject);
pool.put(subject,flyweight);
}
return flyweight;
}
public static void main(String[] args) {
System.out.println(pool.size());//0
getFlyweight("math");
System.out.println(pool.size());//1
getFlyweight("english");
System.out.println(pool.size());//2
getFlyweight("math");
System.out.println(pool.size());//2
}
}
abstract class Flyweight{
//内部状态
private String name;
private String age;
//外部状态
private final String subject;
protected Flyweight(String subject) {
this.subject = subject;
}
//行为
public abstract void exam();
public String getSubject() {
return subject;
}
}
class RealFlyweight extends Flyweight {
@Override
public void exam() {
System.out.println(this.getSubject()+" is examing...");
}
public RealFlyweight(String subject){
super(subject);
}
}

结构型模式总结

代理模式是做方法增强的,适配器模式是把鸡包装成鸭这种用来适配接口的,桥梁模式做到了很好的解耦,装饰模式从名字上就看得出来,适合于装饰类或者说是增强类的场景,门面模式的优点是客户端不需要关心实例化过程,只要调用需要的方法即可,组合模式用于描述具有层次结构的数据,享元模式是为了在特定的场景中缓存已经创建的对象,用于提高性能。

行为型模式

行为型模式关注的是各个类之间的相互作用,将职责划分清楚,使得我们的代码更加地清晰。

策略模式

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
/**
* 策略模式
* 个人理解:比如说现在有用户需要支付,而支付有多种途径(策略),现在就可以把支付动作提取出来,有多种实现方式
* 从而达到微信支付类只管微信支付的目的
*/
public class StrategyPattern {
public static void main(String[] args) {
Payer payer = new Payer();
payer.setPay(new WechatPay());
payer.pay(1L,100D); // 用户1使用微信支付100.0元
}
}
interface IPay{
public void pay(Long userId,Double amount);
}
class WechatPay implements IPay{
@Override
public void pay(Long userId, Double amount) {
System.out.println(String.format("用户%s使用微信支付%s元",userId,amount));
}
}
class AliPay implements IPay{
@Override
public void pay(Long userId, Double amount) {
System.out.println(String.format("用户%s使用支付宝支付%s元",userId,amount));
}
}
@Data
class Payer{
private IPay pay;
public void pay(Long userId, Double amount){
this.pay.pay(userId, 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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
/**
* 观察者模式:多个观察者观察到某对象的变化后进行相应操作
* 下面展示一个例子,比如需要订单状态变更后给开发者发送短信以及邮件通知
*/
public class ObserverPattern {
public static void main(String[] args) {
Order order = new Order();
order.setId(1L);
order.setStatus("1");
order.attach(new OrderStatusChangeSendMailObserver()); // 发送邮件:订单1状态变更,变更前状态1,变更后状态2
order.attach(new OrderStatusChangeSendMsgObserver()); // 发送短信:订单1状态变更,变更前状态1,变更后状态2
order.setStatus("2");
}
}
abstract class OrderStatusChangeObserver {
protected Order order;
// 变化事件(变化订单,前更前状态,变更后状态)
public abstract void change(Order order,String beforeStatus,String afterStatus);
}
@ToString
class Order{
// 多个观察者
private List<OrderStatusChangeObserver> orderStatusChangeObservers = new ArrayList<>();
private String status; // 当前状态
private Long id; // id
// 添加观察者
public void attach(OrderStatusChangeObserver orderStatusChangeObserver){
orderStatusChangeObservers.add(orderStatusChangeObserver);
}
public String getStatus() {
return status;
}
public Long getId() {
return id;
}
public void setStatus(String status){
String beforeStatus = this.status;
this.status = status;
orderStatusChangeObservers.forEach(o -> o.change(this,beforeStatus,status));
}
public void setId(Long id){
this.id = id;
}
}
class OrderStatusChangeSendMsgObserver extends OrderStatusChangeObserver{
@Override
public void change(Order order, String beforeStatus, String afterStatus) {
System.out.println(String.format("发送短信:订单%s状态变更,变更前状态%s,变更后状态%s",order.getId(),beforeStatus,afterStatus));
}
}
class OrderStatusChangeSendMailObserver extends OrderStatusChangeObserver{
@Override
public void change(Order order, String beforeStatus, String afterStatus) {
System.out.println(String.format("发送邮件:订单%s状态变更,变更前状态%s,变更后状态%s",order.getId(),beforeStatus,afterStatus));
}
}

责任链模式

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 class ResponsibilityChainPattern {
public static void main(String[] args) {
RuleHandler goodsCountValidRuleHandler = new GoodsCountValidRuleHandler();// 商品库存
goodsCountValidRuleHandler.setSuccessor(new UserAmountValidRuleHandler()); // 用户余额
User user = new User(1L, 100D);// 一个余额为100的用户
Goods goods = new Goods(1L, 110D, 1L); // 一个售价110库存1的商品
goodsCountValidRuleHandler.apply(user,goods); // 理想:余额验证不通过
}
}
// 用户
@Data
@AllArgsConstructor
class User{
private Long id;
private Double amount;
}
// 商品
@Data
@AllArgsConstructor
class Goods{
private Long id;
private Double price;
private Long count;
}
// 规则父类
@Data
abstract class RuleHandler {
// 后继节点
protected RuleHandler successor;
public abstract void apply(User user,Goods goods);
//验证完成后发送给后续节点
public void sendNext(User user,Goods goods){
if(successor != null){
successor.apply(user,goods);
}
}
}
// 商品库存
class GoodsCountValidRuleHandler extends RuleHandler{
@Override
public void apply(User user, Goods goods) {
if(goods.getCount()<=0){
throw new RuntimeException("库存不足");
}
sendNext(user,goods);
}
}
// 用户余额
class UserAmountValidRuleHandler extends RuleHandler{
@Override
public void apply(User user, Goods goods) {
if(user.getAmount() < goods.getPrice()){
throw new RuntimeException("余额不足");
}
sendNext(user,goods);
}
}

模板方法模式

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

/**
* 模板方法模式: 由父类定义一个模板(自定方法第一步做什么第二步做什么,具体实现由子类实现)
* @author KThirty
* @since 2020-5-10
*/
public class TemplateMethodPattern {
public static void main(String[] args) {
AbstractTemplate t = new ConcreteTemplate();
t.templateMethod();
}
}
abstract class AbstractTemplate{
// 这就是模板方法
public void templateMethod(){
init();
apply(); // 这个是重点
end(); // 可以作为钩子方法
}
protected void init() {
System.out.println("init 抽象层已经实现,子类也可以选择覆写");
}
// 留给子类实现
protected abstract void apply();
protected void end() {}
}
class ConcreteTemplate extends AbstractTemplate {
public void apply() {
System.out.println("子类实现抽象方法 apply");
}
public void end() {
System.out.println("我们可以把 method3 当做钩子方法来使用,需要的时候覆写就可以了");
}
}

状态模式

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
/**
* 状态模式: 简单来说,就是一个对象有不同的状态,根据状态不同,可能有不同的行为。
* 这样可以达到每个类用来修改每个不同的状态,以及其他操作
* @author KThirty
* @since 2020-5-10
*/
public class StatePattern {
public static void main(String[] args) {
Context context = new Context();
context.setId(1L);
StartState startState = new StartState();
startState.doAction(context);// 1Start State doAction
StopState stopState = new StopState();
stopState.doAction(context);// 1Stop State doAction
}
}
@Data
class Context {
private Long id;
private State state = null;
}
interface State {
public void doAction(Context context);
}
class StartState implements State{
@Override
public void doAction(Context context) {
System.out.println(context.getId()+"Start State doAction");
}
}
class StopState implements State{
@Override
public void doAction(Context context) {
System.out.println(context.getId()+"Stop State doAction");
}
}

行为型模式总结

行为型模式部分介绍了策略模式、观察者模式、责任链模式、模板方法模式和状态模式,其实,经典的行为型模式还包括备忘录模式、命令模式等,但是它们的使用场景比较有限,而且本文篇幅也挺大了,我就不进行介绍了。

总结

学习设计模式的目的是为了让我们的代码更加的优雅、易维护、易扩展。

  • 创建型模式: 创建对象
  • 结构型模式: 解耦,利于拓展维护
  • 行为型模式: 拆分分工,单类只负责一种逻辑