创建型
作用:如何创建对象
1 单例模式
单例模式(Singleton)创建分为饿汉式和懒汉式,但是在开源项目中使用最多的主要有两种写法。
1.1 静态常量
静态常量方式属于饿汉式,以静态变量的方式声明对象。这种单例模式在 Spring 中使用的比较多,举个例子,在 Spring 中对于 Bean 的名称生成有个类 AnnotationBeanNameGenerator 就是单例的。

1.2 双重检查机制
双重检查机制(DCL)方式属于懒汉式。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public class Singleton { private volatile static Singleton INSTANCE;
private Singleton() {}
public static Singleton getInstance() { if (INSTANCE == null) { synchronized (Singleton.class) { if (INSTANCE == null) { INSTANCE = new Singleton(); } } } return INSTANCE; } }
|
外层判断 null 的作用:其实就是为了减少进入同步代码块的次数,提高效率。你想一下,其实去了外层的判断其实是可以的,但是每次获取对象都需要进入同步代码块,实在是没有必要。
内层判断 null 的作用:防止多次创建对象。假设 AB 同时走到同步代码块,A 先抢到锁,进入代码,创建了对象,释放锁,此时 B 进入代码块,如果没有判断 null,那么就会直接再次创建对象,那么就不是单例的了,所以需要进行判断 null,防止重复创建单例对象。
volatile 关键字的作用:防止重排序。因为创建对象的过程不是原子,大概会分为三个步骤
- 第一步:分配内存空间给 Singleton 这个对象
- 第二步:初始化对象
- 第三步:将
INSTANCE 变量指向 Singleton 这个对象内存地址
假设没有使用 volatile 关键字发生了重排序,第二步和第三步执行过程被调换了,也就是先将 INSTANCE 变量指向 Singleton 这个对象内存地址,再初始化对象。这样在发生并发的情况下,另一个线程经过第一个 if 非空判断时,发现已经为不为空,就直接返回了这个对象,但是此时这个对象还未初始化,内部的属性可能都是空值,一旦被使用的话,就很有可能出现空指针这些问题。
2 建造者模式
将一个复杂对象的构造与它的表示分离,使同样的构建过程可以创建不同的表示,这样的设计模式被称为建造者模式(Builder)。它是将一个复杂的对象分解为多个简单的对象,然后一步一步构建而成。
这种模式用于分离复杂对象的构建和表示,构建时可以灵活地指定只构建哪部分的信息,在创建对象的时候看起来比较优雅,当构造参数比较多的时候,适合使用建造者模式。
我们都知道,Spring 在创建 Bean 之前,会将每个 Bean 的声明封装成对应的一个 BeanDefinition,而 BeanDefinition 会封装很多属性,所以 Spring 为了更加优雅地创建 BeanDefinition,就提供了 BeanDefinitionBuilder 这个建造者类。

建造者模式不仅在开源项目中有所使用,在 JDK 源码中也有使用到,比如 StringBuilder 类。
示例:
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
| class Person { private int id; private String name; private int age; private double weight; private int score; private Location loc; public static class PersonBuilder { Person p = new Person(); public PersonBuilder basicInfo(int id, String name, int age) { p.id = id; p.name = name; p.age = age; return this; } public PersonBuilder weight(double weight) { p.weight = weight; return this; } public PersonBuilder score(int score) { p.score = score; return this; } public PersonBuilder loc(String city, String street) { p.loc = new Location(city, street); return this; } public Person build() { return p; } } }
class Location { private String city; private String street;
public Location(String city, String street) { this.city = city; this.street = street; } }
public class Main { public static void main(String[] args) { Person p = new Person.PersonBuilder() .basicInfo(1, "申杉杉", 24) .loc("北京", "上园村3号") .build(); } }
|
参考资料:
建造者模式 | 菜鸟教程 (runoob.com)
3 工厂模式
工厂模式(Factory)可以分为:
3.1 简单工厂模式
比如说,现在有个动物接口 Animal,具体的实现有猫 Cat、狗 Dog 等,而每个具体的动物对象创建过程很复杂,有各种各样地步骤,此时就可以使用简单工厂来封装对象的创建过程,调用者不需要关心对象是如何具体创建的。
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
| class SimpleAnimalFactory {
public Animal createAnimal(String animalType) { if ("cat".equals(animalType)) { Cat cat = new Cat(); return cat; } else if ("dog".equals(animalType)) { Dog dog = new Dog(); return dog; } else { throw new RuntimeException("animalType=" + animalType + "无法创建对应对象"); } }
}
public class Main { public static void main(String[] args) { SimpleAnimalFactory animalFactory = new SimpleAnimalFactory(); Animal cat = animalFactory.createAnimal("cat"); } }
|
需要注意的是,一般来说如果每个动物对象的创建只需要简单地 new 一下就行了,那么其实就无需使用工厂模式,工厂模式适合对象创建过程复杂的场景。
3.2 工厂方法模式
上面说的简单工厂模式看起来没啥问题,但是还是违反了七大设计原则的开闭原则。所谓的开闭原则就是对修改关闭,对扩展开放。
什么叫对修改关闭?就是尽可能不修改的意思。就拿上面的例子来说,如果现在新增了一种动物兔子,那么 createAnimal 方法就得修改,增加一种类型的判断,那么就此时就出现了修改代码的行为,也就违反了对修改关闭的原则。
所以解决简单工厂模式违反开闭原则的问题,就可以使用工厂方法模式来解决。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| public interface AnimalFactory { Animal createAnimal(); }
public class CatFactory implements AnimalFactory { @Override public Animal createAnimal() { Cat cat = new Cat(); return cat; } }
public class DogFactory implements AnimalFactory { @Override public Animal createAnimal() { Dog dog = new Dog(); return dog; } }
public class Main { public static void main(String[] args) { AnimalFactory animalFactory = new CatFactory(); Animal cat = animalFactory.createAnimal(); } }
|
此时假设需要新增一个动物兔子,那么只需要实现 AnimalFactory 接口就行,对于原来的猫和狗的实现,其实代码是不需要修改的,遵守了对修改关闭的原则,同时由于是对扩展开放,实现接口就是扩展的意思,那么也就符合扩展开放的原则。
3.3 抽象工厂模式
工厂方法模式其实是创建一个产品的工厂,比如上面的例子中,AnimalFactory 其实只创建动物这一个产品。而抽象工厂模式特点就是创建一系列产品,比如说,不同的动物吃的东西是不一样的,那么就可以加入食物这个产品,通过抽象工厂模式来实现。
在动物工厂中,新增了创建食物的接口,小狗小猫的工厂去实现这个接口,创建狗粮和猫粮,这里就不去写了。
1 2 3 4 5 6 7
| public interface AnimalFactory { Animal createAnimal(); Food createFood(); }
|
Spring 中的 Bean 是通过 BeanFactory 创建的,BeanFactory 就是 Bean 生成的工厂。一个 Spring Bean 在生成过程中会经历复杂的一个生命周期,而这些生命周期对于使用者来说是无需关心的,所以就可以将 Bean 创建过程的逻辑给封装起来,提取出一个 Bean 的工厂。

结构型
作用:如何组合对象
1 代理模式
2 适配器模式
行为型
如何:对象间如何交互、划分职责
1 策略模式
策略模式(Strategy)是指做同一件事的时候,采用不同的策略方式,不修改做这件事的代码,而是只用添加(扩展)不同的策略(开闭原则)。
举例:通过自己定义不同的比较器来实现按不同规则进行排序。
假设现在有一个需求,需要将消息推送到不同的平台。最简单的做法其实就是使用 if else 来做判断就行了,根据不同的平台类型进行判断,调用对应的 api 发送消息。
1 2 3 4 5 6 7
| public void notifyMessage(User user, String content, int notifyType) { if (notifyType == 0) { } else if (notifyType == 1) { } }
|
虽然这样能实现功能,但是跟上面的提到的简单工厂的问题是一样的,同样违反了开闭原则。当需要增加一种平台类型,比如邮件通知,那么就得修改 notifyMessage 的方法,再次进行 else if 的判断,然后调用发送邮件的邮件发送消息。
此时就可以使用策略模式来优化了。
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 MessageNotifier {
boolean support(int notifyType);
void notify(User user, String content);
}
public class SMSMessageNotifier implements MessageNotifier { @Override public boolean support(int notifyType) { return notifyType == 0; }
@Override public void notify(User user, String content) { } }
public class AppMessageNotifier implements MessageNotifier { @Override public boolean support(int notifyType) { return notifyType == 1; }
@Override public void notify(User user, String content) { } }
public class Main { private List<MessageNotifier> messageNotifiers;
public void notifyMessage(User user, String content, int notifyType) { for (MessageNotifier messageNotifier : messageNotifiers) { if (messageNotifier.support(notifyType)) { messageNotifier.notify(user, content); } } } }
|
那么如果现在需要支持通过邮件通知,只需要实现 MessageNotifier 接口,注入到 Spring 容器就行,其余的代码根本不需要有任何变动。
就拿上面举的例子来说,短信通知,app 通知等其实都是发送消息一种策略,而策略模式就是需要将这些策略进行封装,抽取共性,使这些策略之间相互替换。
2 模板方法模式
模板方法模式(TemplateMethod)是指,在父类中定义一个操作中的框架,而操作步骤的具体实现交由子类做。其核心思想就是,对于功能实现的顺序步骤是一定的,但是具体每一步如何实现交由子类决定。
比如说,对于旅游来说,一般有以下几个步骤:
- 做攻略,选择目的地
- 收拾行李
- 乘坐交通工具去目的地
- 玩耍、拍照
- 乘坐交通工具去返回
但是对于去哪,收拾什么东西都,乘坐什么交通工具,都是由具体某个旅行来决定。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| public abstract class Travel {
public void travel() { makePlan(); packUp(); toDestination(); play(); backHome(); }
protected abstract void makePlan();
protected abstract void packUp();
protected abstract void toDestination();
protected abstract void play();
protected abstract void backHome();
}
|
protected 访问符是为了适用继承的场景,父类想把一些属性和方法暴露给子类使用或者重写,又不想把这种权限放的很大,只想给子类,public、default 和 private 是不能够满足这个场景的,所以诞生了 protected。
3 责任链模式
在责任链模式(Chain Of Responsibility)里,很多对象由每一个对象对其下家的引用而连接起来形成一条链。请求在这个链上传递,由该链上的某一个对象或者某几个对象决定处理此请求,每个对象在整个处理过程中值扮演一个小小的角色。
举个例子,现在有个请假的审批流程,根据请假的人的级别审批到的领导不同,比如有有组长、主管、HR、分管经理等等。
先需要定义一个处理抽象类,抽象类有个下一个处理对象的引用,提供了抽象处理方法,还有一个对下一个处理对象的调用方法。
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
| public abstract class ApprovalHandler {
protected ApprovalHandler next;
public void nextHandler(ApprovalHandler approvalHandler) { this.next = approvalHandler; }
public abstract void approval(ApprovalContext approvalContext);
protected void invokeNext(ApprovalContext approvalContext) { if (next != null) { next.approval(approvalContext); } }
}
public class GroupLeaderApprovalHandler extends ApprovalHandler { @Override public void approval(ApprovalContext approvalContext) { System.out.println("组长审批"); invokeNext(approvalContext); } }
public class DirectorApprovalHandler extends ApprovalHandler { @Override public void approval(ApprovalContext approvalContext) { System.out.println("主管审批"); invokeNext(approvalContext); } }
public class HrApprovalHandler extends ApprovalHandler { @Override public void approval(ApprovalContext approvalContext) { System.out.println("hr审批"); invokeNext(approvalContext); } }
public class ApprovalHandlerChain {
@Autowired private GroupLeaderApprovalHandler groupLeaderApprovalHandler; @Autowired private DirectorApprovalHandler directorApprovalHandler; @Autowired private HrApprovalHandler hrApprovalHandler;
public ApprovalHandler getChain() { groupLeaderApprovalHandler .nextHandler(directorApprovalHandler); directorApprovalHandler .nextHandler(hrApprovalHandler); return groupLeaderApprovalHandler; }
}
|
之后对于调用方而言,只需要获取到链条,开始处理就行。一旦后面出现需要增加或者减少审批人,只需要调整链条中的节点就行,对于调用者来说是无感知的。
4 观察者模式
当对象间存在一对多关系时,则使用观察者模式(Observer)。比如,当一个对象被修改时,则会自动通知依赖它的对象。
举个例子来说,假设发生了火灾,可能需要打 119、救人,那么就可以基于观察者模式来实现,打 119、救人的操作只需要观察火灾的发生,一旦发生,就触发相应的逻辑。
观察者的核心优点就是观察者和被观察者是解耦合的。就拿上面的例子来说,火灾事件(被观察者)根本不关系有几个监听器(观察者),当以后需要有变动,只需要扩展监听器就行,对于事件的发布者和其它监听器是无需做任何改变的。