必用的6种JAVA设计模式

1.单例模式

1.1 业务场景

单例模式,保证一个类仅有一个实例,并提供一个访问它的全局访问点。例如,Windows里面的Task Manager(任务管理器)也是很典型的单例模式。

1.2 单例模式的经典写法

单例模式还有有好几种实现方式,如懒汉模式、饿汉模式、双重校验锁,静态内部类,枚举等实现方式。

  • 懒汉:

    实例在需要用到的时候,才去创建,存在线程安全的问题,需要加下 synchronized关键字。

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

    private static LanHanSingleton instance;

    private LanHanSingleton(){

    }

    public static LanHanSingleton getInstance(){
    if (instance == null) {
    instance = new LanHanSingleton();
    }
    return instance;
    }

    }
  • 饿汉:

    实例在初始化的时候就已经建好了, 没有线程安全问题,但是浪费空间。

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

private static EHanSingleton instance = new EHanSingleton();

private EHanSingleton(){
}

public static EHanSingleton getInstance() {
return instance;
}

}
  • 双重校验锁
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class DoubleCheckSingleton {

private volatile static DoubleCheckSingleton instance;

private DoubleCheckSingleton() { }

public static DoubleCheckSingleton getInstance(){
if (instance == null) {
synchronized (DoubleCheckSingleton.class) {
if (instance == null) {
instance = new DoubleCheckSingleton();
}
}
}
return instance;
}
}

2.工厂模式

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

3.策略模式

3.1.业务场景

假设有这样的业务场景,大数据系统把文件推送过来,根据不同类型采取不同的解析方式。多数的小伙伴就会写出以下的代码:

1
2
3
4
5
6
7
8
if(type=="A"){
//按照A格式解析

}else if(type=="B"){
//按B格式解析
}else{
//按照默认格式解析
}

这个代码可能会存在哪些问题呢?

如果分支变多,这里的代码就会变得臃肿,难以维护,可读性低。
如果你需要接入一种新的解析类型,那只能在原有代码上修改。

说得专业一点的话,就是以上代码,违背了面向对象编程的开闭原则以及单一原则。

开闭原则(对于扩展是开放的,但是对于修改是封闭的):增加或者删除某个逻辑,都需要修改到原来代码
单一原则(规定一个类应该只有一个发生变化的原因):修改任何类型的分支逻辑代码,都需要改动当前类的代码。

如果你的代码就是酱紫:有多个if…else等条件分支,并且每个条件分支,可以封装起来替换的,我们就可以使用策略模式来优化。

3.2.策略模式使用

策略模式怎么使用呢?

  • 一个接口或者抽象类,里面两个方法(一个方法匹配类型,一个可替换的逻辑实现方法)
  • 不同策略的差异化实现(不同策略的实现类)
  • 使用策略模式
3.2.1 一个接口,两个方法
1
2
3
4
5
6
7
8
public interface IFileStrategy {

//属于哪种文件解析类型
FileTypeResolveEnum gainFileType();

//封装的公用算法(具体的解析方法)
void resolve(Object objectparam);
}
3.2.2 不同策略的差异化实现
  • A 类型策略具体实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Component
public class AFileResolve implements IFileStrategy {

@Override
public FileTypeResolveEnum gainFileType() {
return FileTypeResolveEnum.File_A_RESOLVE;
}

@Override
public void resolve(Object objectparam) {
logger.info("A 类型解析文件,参数:{}",objectparam);
//A类型解析具体逻辑
}
}
  • B 类型策略具体实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Component
public class BFileResolve implements IFileStrategy {

@Override
public FileTypeResolveEnum gainFileType() {
return FileTypeResolveEnum.File_B_RESOLVE;
}


@Override
public void resolve(Object objectparam) {
logger.info("B 类型解析文件,参数:{}",objectparam);
//B类型解析具体逻辑
}
}
  • 默认类型策略具体实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Component
public class DefaultFileResolve implements IFileStrategy {

@Override
public FileTypeResolveEnum gainFileType() {
return FileTypeResolveEnum.File_DEFAULT_RESOLVE;
}

@Override
public void resolve(Object objectparam) {
logger.info("默认类型解析文件,参数:{}",objectparam);
//默认类型解析具体逻辑
}
}

4.责任链模式

4.1 业务场景

常见的一个业务场景下订单,下订单接口,基本的逻辑,一般有参数非空校验、安全校验、黑名单校验、规则拦截等等。很多伙伴会使用异常来实现:

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

public void checkNullParam(Object param){
//参数非空校验
throw new RuntimeException();
}
public void checkSecurity(){
//安全校验
throw new RuntimeException();
}
public void checkBackList(){
//黑名单校验
throw new RuntimeException();
}
public void checkRule(){
//规则拦截
throw new RuntimeException();
}

public static void main(String[] args) {
Order order= new Order();
try{
order.checkNullParam();
order.checkSecurity ();
order.checkBackList();
order2.checkRule();
System.out.println("order success");
}catch (RuntimeException e){
System.out.println("order fail");
}
}
}

这段代码使用了异常来做逻辑条件判断,如果后续逻辑越来越复杂的话,会出现一些问题:

如异常只能返回异常信息,不能返回更多的字段,这时候需要自定义异常类。

并且,需要用异常逻辑做判断,而阿里开发手册规定:禁止用异常做逻辑判断。

4.2 责任链模式使用

责任链模式怎么使用呢?

  • 一个接口或者抽象类
  • 每个对象差异化处理
  • 对象链(数组)初始化(连起来)
4.2.1 一个接口或者抽象类

这个接口或者抽象类,需要:

有一个指向责任下一个对象的属性
一个设置下一个对象的set方法
给子类对象差异化实现的方法(如以下代码的doFilter方法)

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

//责任链中的下一个对象
private AbstractHandler nextHandler;

/**
* 责任链的下一个对象
*/
public void setNextHandler(AbstractHandler nextHandler){
this.nextHandler = nextHandler;
}

/**
* 具体参数拦截逻辑,给子类去实现
*/
public void filter(Request request, Response response) {
doFilter(request, response);
if (getNextHandler() != null) {
getNextHandler().filter(request, response);
}
}

public AbstractHandler getNextHandler() {
return nextHandler;
}

abstract void doFilter(Request filterRequest, Response response);

}
4.2.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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
/**
* 参数校验对象
**/
@Component
@Order(1) //顺序排第1,最先校验
public class CheckParamFilterObject extends AbstractHandler {

@Override
public void doFilter(Request request, Response response) {
System.out.println("非空参数检查");
}
}

/**
* 安全校验对象
*/
@Component
@Order(2) //校验顺序排第2
public class CheckSecurityFilterObject extends AbstractHandler {

@Override
public void doFilter(Request request, Response response) {
//invoke Security check
System.out.println("安全调用校验");
}
}
/**
* 黑名单校验对象
*/
@Component
@Order(3) //校验顺序排第3
public class CheckBlackFilterObject extends AbstractHandler {

@Override
public void doFilter(Request request, Response response) {
//invoke black list check
System.out.println("校验黑名单");
}
}

/**
* 规则拦截对象
*/
@Component
@Order(4) //校验顺序排第4
public class CheckRuleFilterObject extends AbstractHandler {

@Override
public void doFilter(Request request, Response response) {
//check rule
System.out.println("check rule");
}
}
4.3.3 对象链连起来(初始化) 使用
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
@Component("ChainPatternDemo")
public class ChainPatternDemo {

//自动注入各个责任链的对象
@Autowired
private List<AbstractHandler> abstractHandleList;

private AbstractHandler abstractHandler;

//spring注入后自动执行,责任链的对象连接起来
@PostConstruct
public void initializeChainFilter(){

for(int i = 0;i<abstractHandleList.size();i++){
if(i == 0){
abstractHandler = abstractHandleList.get(0);
}else{
AbstractHandler currentHander = abstractHandleList.get(i - 1);
AbstractHandler nextHander = abstractHandleList.get(i);
currentHander.setNextHandler(nextHander);
}
}
}

//直接调用这个方法使用
public Response exec(Request request, Response response) {
abstractHandler.filter(request, response);
return response;
}

public AbstractHandler getAbstractHandler() {
return abstractHandler;
}

public void setAbstractHandler(AbstractHandler abstractHandler) {
this.abstractHandler = abstractHandler;
}
}

5.模板方法模式

5.1 业务场景

假设我们有这么一个业务场景:

内部系统不同商户,调用我们系统接口,去跟外部第三方系统交互(http方式)。走类似这么一个流程,如下:

Request -> 查询商户信息 -> 加签 -> 发送http请求 -> 验签

一个请求都会经历这几个流程。

这里,有的商户可能是走代理出去的,有的是走直连。假设当前有A,B商户接入,不少伙伴可能这么实现,伪代码如下:

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
// 商户A处理句柄
CompanyAHandler implements RequestHandler {
Resp hander(req){
//查询商户信息
queryMerchantInfo();
//加签
signature();
//http请求(A商户假设走的是代理)
httpRequestbyProxy()
//验签
verify();
}
}
// 商户B处理句柄
CompanyBHandler implements RequestHandler {
Resp hander(Rreq){
//查询商户信息
queryMerchantInfo();
//加签
signature();
// http请求(B商户不走代理,直连)
httpRequestbyDirect();
// 验签
verify();
}
}

假设新加一个C商户接入,你需要再实现一套这样的代码。显然,这样代码就重复了,一些通用的方法,却在每一个子类都重新写了这一方法。

如何优化呢?可以使用模板方法模式。

5.2 模板方法模式定义

定义一个操作中的算法的骨架流程,而将一些步骤延迟到子类中,使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。

它的核心思想就是:定义一个操作的一系列步骤,对于某些暂时确定不下来的步骤,就留给子类去实现,这样不同的子类就可以定义出不同的步骤。

5.3 模板方法使用

  • 一个抽象类,定义骨架流程(抽象方法放一起)

  • 确定的共同方法步骤,放到抽象类(去除抽象方法标记)

  • 不确定的步骤,给子类去差异化实现

    我们继续那以上的举例的业务流程例子,来一起用 模板方法优化一下哈:

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* 1.抽象类定义骨架流程(查询商户信息,加签,http请求,验签)
*/
abstract class AbstractMerchantService {
//查询商户信息
abstract queryMerchantInfo();
//加签
abstract signature();
//http 请求
abstract httpRequest();
// 验签
abstract verifySinature();
}

确定共同方法步骤,放到抽象类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
abstract class AbstractMerchantService  { 
//模板方法流程
Resp handlerTempPlate(req){
//查询商户信息
queryMerchantInfo();
//加签
signature();
//http 请求
httpRequest();
// 验签
verifySinature();
}
// Http是否走代理(提供给子类实现)
abstract boolean isRequestByProxy();
}

不确定的步骤,给子类去差异化实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
* 商户A的请求实现
*/
CompanyAServiceImpl extends AbstractMerchantService{
Resp hander(req){
return handlerTempPlate(req);
}
//走http代理的
boolean isRequestByProxy(){
return true;
}

/**
* 商户B的请求实现
*/
CompanyBServiceImpl extends AbstractMerchantService{
Resp hander(req){
return handlerTempPlate(req);
}
//公司B是不走代理的
boolean isRequestByProxy(){
return false;
}

6.观察者模式

6.1 业务场景

登陆注册应该是最常见的业务场景了。就拿注册来说事,我们经常会遇到类似的场景,就是用户注册成功后,我们给用户发一条消息,又或者发个邮件等等,因此经常有如下的代码:

1
2
3
4
5
void register(User user){
insertRegisterUser(user);
sendIMMessage();
sendEmail();
}

这块代码会有什么问题呢?

如果产品又加需求:现在注册成功的用户,再给用户发一条短信通知。于是你又得改register方法的代码了。。。这是不是违反了开闭原则啦。

1
2
3
4
5
6
void register(User user){
insertRegisterUser(user);
sendIMMessage();
sendMobileMessage();
sendEmail();
}

并且,如果调发短信的接口失败了,是不是又影响到用户注册了?!这时候,是不是得加个异步方法给通知消息才好。。

实际上,我们可以使用观察者模式优化。

6.2 观察者模式定义

观察者模式属于行为模式,一个对象(被观察者)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知,进行广播通知。它的主要成员就是观察者和被观察者。

使用场景: 完成某件事情后,异步通知场景。如,登陆成功,发个IM消息等等。

  • 被观察者(Observerable):目标对象,状态发生变化时,将通知所有的观察者。
  • 观察者(observer):接受被观察者的状态变化通知,执行预先定义的业务。

6.3 观察者模式使用

  • 一个被观察者的类Observerable ;
  • 多个观察者Observer ;
  • 观察者的差异化实现
6.3.1 一个被观察者的类Observerable 和 多个观察者Observer
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 Observerable {

private List<Observer> observers = new ArrayList<Observer>();
private int state;

public int getState() {
return state;
}
public void setState(int state) {
notifyAllObservers();
}

//添加观察者
public void addServer(Observer observer){
observers.add(observer);
}

//移除观察者
public void removeServer(Observer observer){
observers.remove(observer);
}

//通知
public void notifyAllObservers(int state){
if(state!=1){
System.out.println(“不是通知的状态”);
return ;
}

for (Observer observer : observers) {
observer.doEvent();
}
}
}
6.3.2 观察者的差异化实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//观察者
interface Observer {
void doEvent();
}
//Im消息
IMMessageObserver implements Observer{
void doEvent(){
System.out.println("发送IM消息");
}
}

//手机短信
MobileNoObserver implements Observer{
void doEvent(){
System.out.println("发送短信消息");
}
}
//EmailNo
EmailObserver implements Observer{
void doEvent(){
System.out.println("发送email消息");
}
}

EventBus实战
自己搞一套观察者模式的代码,还是有点小麻烦。实际上,Guava EventBus就封装好了,它 提供一套基于注解的事件总线,api可以灵活的使用。

我们来看下EventBus的实战代码哈,首先可以声明一个EventBusCenter类,它类似于以上被观察者那种角色Observerable。

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 EventBusCenter {
private EventBusCenter() {
}

private static EventBus eventBus = new EventBus();

public static EventBus getInstance() {
return eventBus;
}
//添加观察者
public static void register(Object obj) {
eventBus.register(obj);
}
//移除观察者
public static void unregister(Object obj) {
eventBus.unregister(obj);
}
//把消息推给观察者
public static void post(Object obj) {
eventBus.post(obj);
}
}


//然后再声明观察者EventListener
public class EventListener {

@Subscribe //加了订阅,这里标记这个方法是事件处理方法
public void handle(NotifyEvent notifyEvent) {
System.out.println("发送IM消息" + notifyEvent.getImNo());
System.out.println("发送短信消息" + notifyEvent.getMobileNo());
System.out.println("发送Email消息" + notifyEvent.getEmailNo());
}
}

//通知事件类
public class NotifyEvent {

private String mobileNo;

private String emailNo;

private String imNo;

public NotifyEvent(String mobileNo, String emailNo, String imNo) {
this.mobileNo = mobileNo;
this.emailNo = emailNo;
this.imNo = imNo;
}
}

demo测试:

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

public static void main(String[] args) {

EventListener eventListener = new EventListener();
EventBusCenter.register(eventListener);
EventBusCenter.post(new NotifyEvent("13372817283", "123@qq.com", "666"));
}
}


发送IM消息666
发送短信消息13372817283
发送Email消息123@qq.com

reference:

https://mp.weixin.qq.com/s/H-OurMV7I6g5EqW_MIaOww


觉得不错的话,支持一根棒棒糖吧 ୧(๑•̀⌄•́๑)૭



wechat pay



alipay

必用的6种JAVA设计模式
http://yuting0907.github.io/posts/2026/01/bce8f298.html
作者
Echo Yu
发布于
2026年1月5日
许可协议