跳到主要内容工作中常用的几种设计模式实战 | 极客日志Javajava算法
工作中常用的几种设计模式实战
文章分享了六种工作高频使用的设计模式。策略模式解决多分支逻辑臃肿问题;责任链模式处理订单校验等流程控制;模板方法定义算法骨架;观察者模式实现注册后的异步通知;工厂模式优化对象创建;单例模式确保全局唯一实例。结合 Spring 生态与 Guava EventBus 提供了具体代码示例,帮助开发者提升代码可维护性与扩展性。
FrontendX0 浏览 前言
日常开发中,我们往往容易陷入流水线式的编码习惯,业务逻辑跑通就行。但要想写出高质量、易维护的代码,使用设计模式优化业务逻辑是关键。今天聊聊实际工作中高频使用的几种设计模式。
1. 策略模式
1.1 业务场景
假设大数据系统推送文件过来,需要根据不同类型采取不同的解析方式。新手通常会写成这样:
if(type == "A") {
} else if (type == "B") {
} else {
}
这种写法的问题很明显:分支多了代码臃肿,难以维护;新增类型时还得修改原有逻辑,违背了面向对象编程的开闭原则和单一职责原则。如果条件分支可以封装替换,就该考虑用策略模式。
1.2 核心定义
策略模式定义了一系列算法,把它们封装起来,让它们可以相互替换。算法的变化独立于使用算法的客户。
通俗点说,就像约会不同性格的女生,有的适合看电影,有的适合逛街,目的都是'追到',只是策略不同。针对一组算法,将每个算法封装到具有共同接口的独立类中,实现灵活切换。
1.3 实战落地
在 Spring 环境下,我们可以利用 ApplicationContextAware 接口自动管理策略实例。
第一步:定义策略接口
public interface IFileStrategy {
FileTypeResolveEnum gainFileType();
void resolve(Object objectParam);
}
第二步:实现具体策略
@Component
public class AFileResolve implements IFileStrategy {
@Override
public FileTypeResolveEnum gainFileType() {
return FileTypeResolveEnum.File_A_RESOLVE;
}
@Override
{
logger.info(, objectParam);
}
}
public
void
resolve
(Object objectParam)
"A 类型解析文件,参数:{}"
@Component
public class StrategyUseService implements ApplicationContextAware {
private Map<FileTypeResolveEnum, IFileStrategy> iFileStrategyMap = new ConcurrentHashMap<>();
public void resolveFile(FileTypeResolveEnum fileTypeResolveEnum, Object objectParam) {
IFileStrategy strategy = iFileStrategyMap.get(fileTypeResolveEnum);
if (strategy != null) {
strategy.resolve(objectParam);
}
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
Map<String, IFileStrategy> tempMap = applicationContext.getBeansOfType(IFileStrategy.class);
tempMap.values().forEach(strategy ->
iFileStrategyMap.put(strategy.gainFileType(), strategy)
);
}
}
通过 Spring 容器自动注入所有实现类并放入 Map,使用时直接查表即可,完全消除了 if-else 判断。
2. 责任链模式
2.1 业务场景
下订单接口通常包含参数校验、安全校验、黑名单拦截等步骤。很多伙伴习惯用异常处理流程控制:
public void checkNullParam(Object param) { throw new RuntimeException(); }
public void checkSecurity() { throw new RuntimeException(); }
阿里开发手册明确规定:禁止用异常做流程控制。异常处理效率低且语义不清。优化方案是引入责任链模式。
2.2 核心定义
当有多个对象有机会处理同一个请求时,将它们串成一条链。请求沿链传递,直到某个节点处理成功或结束。这实现了发送者和接收者的解耦。
2.3 实战落地
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 (nextHandler != null) {
nextHandler.filter(request, response);
}
}
protected abstract void doFilter(Request request, Response response);
}
@Component
@Order(1)
public class CheckParamFilterObject extends AbstractHandler {
@Override
public void doFilter(Request request, Response response) {
System.out.println("非空参数检查");
}
}
@Component
@Order(2)
public class CheckSecurityFilterObject extends AbstractHandler {
@Override
public void doFilter(Request request, Response response) {
System.out.println("安全调用校验");
}
}
@Component("ChainPatternDemo")
public class ChainPatternDemo {
@Autowired
private List<AbstractHandler> abstractHandleList;
private AbstractHandler firstHandler;
@PostConstruct
public void initializeChainFilter() {
for (int i = 0; i < abstractHandleList.size(); i++) {
if (i == 0) {
firstHandler = abstractHandleList.get(i);
} else {
abstractHandleList.get(i - 1).setNextHandler(abstractHandleList.get(i));
}
}
}
public Response exec(Request request, Response response) {
firstHandler.filter(request, response);
return response;
}
}
3. 模板方法模式
3.1 业务场景
内部系统对接外部第三方,流程通常是:查询商户信息 -> 加签 -> 发送 HTTP -> 验签。不同商户可能走代理或直连,如果每个商户都写一套完整流程,代码重复率极高。
3.2 核心定义
定义一个操作中的算法骨架,而将一些步骤延迟到子类中。子类可以不改变算法结构即可重定义特定步骤。
3.3 实战落地
abstract class AbstractMerchantService {
Resp handlerTempPlate(req) {
queryMerchantInfo();
signature();
httpRequest();
verifySignature();
}
abstract boolean isRequestByProxy();
}
CompanyAServiceImpl extends AbstractMerchantService {
Resp hander(req) {
return handlerTempPlate(req);
}
boolean isRequestByProxy() {
return true;
}
}
通过这种方式,通用逻辑固化在父类,差异逻辑下沉到子类,扩展新商户只需继承即可,无需改动现有代码。
4. 观察者模式
4.1 业务场景
用户注册成功后,需要发 IM 消息、邮件、短信等。如果直接在 register 方法里写死这些逻辑,一旦增加通知渠道就得修改主流程,违反开闭原则。且同步发送失败会影响注册体验。
4.2 核心定义
定义对象间的一对多依赖关系。当一个对象状态改变时,所有依赖它的对象都会收到通知并更新。常用于异步通知场景。
4.3 实战落地
自己封装比较麻烦,推荐使用 Guava EventBus。
public class EventBusCenter {
private static EventBus eventBus = new EventBus();
public static void register(Object obj) {
eventBus.register(obj);
}
public static void post(Object obj) {
eventBus.post(obj);
}
}
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());
}
}
EventListener listener = new EventListener();
EventBusCenter.register(listener);
EventBusCenter.post(new NotifyEvent("13372817283", "[email protected]", "666"));
注册逻辑只需触发一次事件,后续通知由 EventBus 分发,互不干扰。
5. 工厂模式
5.1 业务场景
工厂模式常配合策略模式使用,用于消除大量的 if-else 或 switch-case 条件语句,专注于对象的创建过程。
5.2 核心定义
定义一个创建对象的接口,让子类决定实例化哪一个工厂类。创建过程延迟到子类进行。
5.3 实战落地
结合前面的策略模式例子,Spring 容器本身就是一种工厂。我们在 StrategyUseService 中将策略对象放入 Map,本质上就是利用了工厂思想,根据类型信手拈来所需对象。
6. 单例模式
6.1 业务场景
保证一个类仅有一个实例,并提供全局访问点。数据库连接池、任务管理器等都是典型应用。
6.2 常见实现方式
public class LazySingleton {
private static LazySingleton instance;
private LazySingleton() {}
public static synchronized LazySingleton getInstance() {
if (instance == null) {
instance = new LazySingleton();
}
return instance;
}
}
注意:必须加 synchronized 保证线程安全。
public class EagerSingleton {
private static final EagerSingleton instance = new EagerSingleton();
private EagerSingleton() {}
public static EagerSingleton getInstance() {
return instance;
}
}
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;
}
}
兼顾了懒加载和性能,既保证了线程安全又避免了不必要的同步开销。
利用 JVM 类加载机制,只有调用 getInstance 时才加载内部类,天然线程安全且懒加载。
public enum SingletonEnum {
INSTANCE;
}
代码简洁,防止反射攻击和序列化破坏,是 Java 中最推荐的单例实现方式。
相关免费在线工具
- Keycode 信息
查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
- Escape 与 Native 编解码
JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
- JavaScript / HTML 格式化
使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
- JavaScript 压缩与混淆
Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
- 加密/解密文本
使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
- Base64 字符串编码/解码
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online