设计模式共23种,可分为创建型、结构型和行为型。这些设计模式并不是要求我们都要记得滚瓜烂熟,可以先掌握工作中常用的几种设计模式,用于解决工作中绝大部分的设计问题,这样在从容应对工作内容之余我们才有更多的精力去深入拓展更多的知识点。
每种设计模式并不是独立的、割裂的,它们是可以组合的。另外,不要为了用设计模式而用,如果场景单一、逻辑简单,比如用基本的if...else逻辑控制语句就能满足业务要求的情况下就没必要强行套模板去使用策略模式。
1. 模板方法+策略模式+责任链(行为型)
模板方法、策略模式、责任链常结合使用,模板定义抽象封装算法、策略即多态实现、责任链用于实现业务校验链。
抽象模板用于定义算法的框架、步骤,对业务进行抽象,我们可以先分析出各个业务场景的实现逻辑都分为1、2、3、4哪几个步骤,将这些“不变”的公共步骤封装在一个方法里,而某些步骤每个场景逻辑都不同的“变”的部分则由子类实现。因此,模板方法也是用于分离“变”和“不变”的一种手段。
策略其实就是多态,不同的业务类型、业务场景有不同的实现逻辑,往往实际工作中每一种场景的实现逻辑又比较复杂,如果直接用if...else进行处理随着业务迭代的开展,不断叠加逻辑,代码会变成一个泥团,不具备扩展性且牵一发动全身,因此需要多态实现,每种场景划分在独立的类中,这样场景逻辑清晰,可扩展。
责任链一般可用于业务校验条件多且复杂的场景中,同理如果直接用if...else逻辑分支实现代码会变得冗长,随着业务场景的叠加后期会变得难以维护,而使用责任链其实就是把每个校验逻辑职责分离,抽象成一个节点,这些节点连成一个链表组合完成了整个校验逻辑,如果后续需要增删校验逻辑则只需要增删节点,这种设计能更好地适应业务需求的变化。
为了简化逻辑,使用下载功能作为举例,有商品SKU、官网订单等各类单据,每类单据的下载有公共逻辑也有个性化逻辑,用户通过页面点击按钮进行下载。设计图如下:
代码样例:
代码样例简化了具体实现,比如出入参直接使用了Object(实际开发中需要具体定义参数类)、方法逻辑只用注释进行说明等,这是因为样例侧重于体现设计模式的应用,而不是具体的代码细节。
package org.example.designpattern;
/**
* 下载接口
*/
public interface IDownload {
void download(Object param);
}
package org.example.designpattern;
/**
* 下载业务抽象模板类:定义下载的框架、步骤
*/
public abstract class AbstractDownloadService implements IDownload {
@Override
public void download(Object param) {
// 1. 业务校验
String errMsg = this.validate(param);
if (!"".equals(errMsg)) {
return;
}
// 2. 取数(例如查询用户信息、单据数据、下载模板配置)
Object downloadData = this.queryDownloadData(param);
// 3. 组装数据
Object downloadContent = this.buildDownloadContent(downloadData);
// 4. 下载
this.doDownload(downloadContent);
// 5. 记录操作日志
this.saveOperateLog(downloadData);
}
/**
* 业务校验抽象方法,由子类提供实现
*
* @param param 下载参数
* @return String 错误信息
*/
protected abstract String validate(Object param);
/**
* 查询要下载的单据数据(父类提供默认实现,子类有个性化需求可以重写)
*
* @param param 下载参数
* @return Object 这里为了简化使用了Object,实际开发中需要自定义一个返参
*/
protected Object queryDownloadData(Object param) {
// 1. 模拟根据param中的用户id从db查询用户信息
// 2. 模拟根据param中的下载模板配置id查询下载模板配置
// 3. 模拟根据param中的单据表名tableName和whereCondition查询单据数据
return new Object();
}
/**
* 组装下载的excel内容抽象方法,由子类提供实现
*
* @param downloadData 下载所需的数据,例如单据数据、下载模板配置
* @return Object 返回excel内容
*/
protected abstract Object buildDownloadContent(Object downloadData);
/**
* 下载excel(父类提供默认实现,子类有个性化需求可以重写)
*
* @param downloadContent 文件内容
*/
protected void doDownload(Object downloadContent) {
// 1. 模拟将文件内容downloadContent写入到excel完成下载
}
/**
* 保存操作日志(父类提供默认实现,子类有个性化需求可以重写)
*
* @param downloadData 下载所需的数据,例如用户信息
*/
protected void saveOperateLog(Object downloadData) {
// 1. 保存操作日志,操作用户信息从downloadData获取
}
}
package org.example.designpattern;
/**
* 商品sku下载子类
*/
public class SkuDownloadServiceImpl extends AbstractDownloadService {
@Override
protected String validate(Object param) {
BaseDownloadValidator validator = new BaseDownloadValidator();
return validator.validate(param);
}
@Override
protected Object queryDownloadData(Object param) {
return super.queryDownloadData(param);
}
@Override
protected Object buildDownloadContent(Object downloadData) {
// 模拟实现父类抽象方法,根据单据数据、下载模板配置组装出下载的文件内容
return new Object();
}
@Override
protected void doDownload(Object downloadContent) {
super.doDownload(downloadContent);
}
@Override
protected void saveOperateLog(Object downloadData) {
super.saveOperateLog(downloadData);
}
}
package org.example.designpattern;
/**
* 财务报表下载子类
*/
public class FinancialReportDownloadServiceImpl extends AbstractDownloadService {
@Override
protected String validate(Object param) {
BaseDownloadValidator validator = new BaseDownloadValidator();
return validator.validate(param);
}
@Override
protected Object queryDownloadData(Object param) {
// 重写父类方法,模拟个性化实现查询下载所需的数据
return new Object();
}
@Override
protected Object buildDownloadContent(Object downloadData) {
// 模拟实现父类抽象方法,根据单据数据、下载模板配置组装出下载的文件内容
return new Object();
}
@Override
protected void doDownload(Object downloadContent) {
super.doDownload(downloadContent);
}
@Override
protected void saveOperateLog(Object downloadData) {
super.saveOperateLog(downloadData);
}
}
package org.example.designpattern;
import java.util.Objects;
/**
* 业务校验函数式接口
*/
@FunctionalInterface
public interface ValidateFunction<T, R> {
R apply(T param);
default ValidateFunction<T, R> andThen(ValidateFunction<T, R> after) {
Objects.requireNonNull(after);
return (T t) -> {
R result = apply(t);
if(!"".equals(result)){
return result;
}
return after.apply(t);
};
}
}
package org.example.designpattern;
/**
* 公共下载校验器(为了简化示例,只定义了一个公共的校验器,实际开发中可以将公共校验逻辑抽取到基类,个性化校验放在子类中)
* 网络上大多通过定义一个类似链表结构来实现责任链,调用setNext设置下一节点。其实也可以像本示例一样利用函数式接口实现,代码更加简洁
*/
public class BaseDownloadValidator {
protected final ValidateFunction<Object, String> validateParamNotEmpty = param -> {
// 1. 入参非空校验
return "";
};
protected final ValidateFunction<Object, String> validateUser = param -> {
// 2. 用户信息校验
return "";
};
protected final ValidateFunction<Object, String> validateBill = param -> {
// 3. 单据数据非空校验
return "";
};
protected final ValidateFunction<Object, String> validateDownloadConfig = param -> {
// 4. 下载配置非空校验
return "";
};
/**
* 使用函数式接口实现责任链调用
*
* @param param 下载参数
* @return String 错误信息
*/
public String validate(Object param) {
return validateParamNotEmpty.andThen(validateUser).andThen(validateBill).andThen(validateDownloadConfig).apply(param);
}
}
package org.example.designpattern;
/**
* 下载策略类
*/
public class DownloadStrategy {
public void download(Object param) {
// 假设从下载参数获取的单据类型billType为sku,根据类型获取具体的业务类
IDownload downloadService = getServiceByType("sku");
// 调用下载接口
downloadService.download(param);
}
/**
* 根据单据类型billType获取具体的下载业务类
*
* @param billType 单据类型
* @return IDownload 返回实现IDownload接口的实现子类
*/
private IDownload getServiceByType(String billType) {
if ("sku".equals(billType)) {
return new SkuDownloadServiceImpl();
}
if ("financialReport".equals(billType)) {
return new FinancialReportDownloadServiceImpl();
}
throw new UnsupportedOperationException("不支持的单据类型");
}
}
2. 代理模式(结构型)
代理就是你授权给我,我帮你干一些活,比如现实生活中房屋中介就是介于租客和房东间的代理例子。在Spring项目开发中我们通过AOP实现代理,通常在Aspect类中编写业务方法拦截逻辑对原方法进行增强,比如打印方法执行耗时。
Spring代码样例:
package org.example.designpattern.proxy;
import org.springframework.stereotype.Service;
/**
* 被代理的目标类
*/
@Service
public class TargetService {
public void doSomething(){
System.out.println("执行业务逻辑");
}
}
package org.example.designpattern.proxy;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
/**
* 切面类
*/
@Aspect
@Component
public class LoggingAspect {
/**
* 拦截业务方法打印方法执行耗时
*/
@Around("execution(* org.example.designpattern.proxy.TargetService.doSomething(..))")
public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();
Object proceed = joinPoint.proceed();
long executionTime = System.currentTimeMillis() - start;
System.out.println(joinPoint.getSignature() + " 方法耗时:" + executionTime + "ms");
return proceed;
}
}
3. 工厂方法(创建型)
工厂方法在代码实现上和策略模式很相似,都涉及定义接口、多个实现该接口的实现类以及运行时的多态特性。但是它们的应用场景不同,策略模式侧重于算法或者说行为的选择,通过策略类选择具体的算法实现类的具体方法执行,而工厂方法侧重于对象的创建,通过工厂类提供的生产对象的方法获取到具体对象,强调对象的创建和使用的解耦,而不是行为(方法)的选择。
日常业务开发中使用工厂方法的频率可能没有上面提到的模板、策略、责任链这几种这么高,因为大多场景下我们都是直接用的Spring的依赖注入,只有当对象的创建过程比较复杂时使用工厂方法才能获得较大的收益,比如需要根据各种复杂业务规则决定具体创建哪个类的对象实例。
代码样例:
package org.example.designpattern.factory;
/**
* 促销活动接口
*/
public interface IPromotion {
void promote();
}
package org.example.designpattern.factory;
/**
* 满减促销活动
*/
public class SpendToGetDiscountPromotion implements IPromotion {
@Override
public void promote() {
}
}
package org.example.designpattern.factory;
/**
* 抽奖促销活动
*/
public class LotteryPromotion implements IPromotion {
@Override
public void promote() {
}
}
package org.example.designpattern.factory;
/**
* 创建促销活动的工厂类
*/
public class PromotionFactory {
/**
* 根据促销类型创建促销活动
*
* @param promotionType 促销类型
* @return IPromotion 促销活动实例
*/
public IPromotion createPromotion(String promotionType) {
if ("1".equals(promotionType)) {
return new SpendToGetDiscountPromotion();
}
if ("2".equals(promotionType)) {
return new LotteryPromotion();
}
throw new UnsupportedOperationException("不支持的促销类型");
}
}
4. 单例模式(创建型)
平常业务开发可能较少直接使用单例模式来创建对象,但是我们要做有准备的人,当工作中需要我们设计一个技术组件或中台框架时,可能单例模式就派上了用场,比如框架的上下文类、业务线程池类等。
推荐采用静态内部类方式,利用了类加载的机制来保证线程安全和延迟初始化:
package org.example.designpattern.singleton;
/**
* 静态内部类实现单例模式
*/
public class Singleton {
private Singleton() {
}
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
标签:编程,return,Object,param,org,设计模式,职场,public,下载
From: https://blog.csdn.net/u012012134/article/details/144619628