文章目录

  • ​​一、设计模式总览​​
  • ​​二、模板方法模式案例​​
  • ​​三、策略模式案例​​
  • ​​四、支付改造​​
  • ​​4.1 思路分析​​
  • ​​4.2 实现图解:​​
  • ​​4.3 代码实现:​​
  • ​​4.4 效果演示​​
  • ​​4.5 如何扩展​​

本文参考自12.29日尚硅谷雷神的 ​​巧妙使用设计模式重构项目​

一、设计模式总览

  • 总体分类

一文教会你巧用设计模式重构项目_java

  • 不同时期选择不同的设计模式~

一文教会你巧用设计模式重构项目_设计模式_02

  • 设计模式本质上玩的就是:​​封装​​​,​​继承​​​,​​多态​
  • 设计模式遵循的六大原则

一文教会你巧用设计模式重构项目_设计模式_03

二、模板方法模式案例

**描述:**父类(接口、抽象类)提供了一种​​定义算法的骨架​​,允许子类为一个或多个步骤提供实现,在不改变算法结构的情况下,重新定义算法的某些步骤。

一文教会你巧用设计模式重构项目_System_04

案例:订单处理

1、定义订单处理模板

/**
* @author lfy
* @Description 定义订单处理模板
* @create 2022-12-29 20:21
*/
public abstract class OrderProcessTemplate {

/**
* 处理订单: 定义好算法骨架
*/
public final void processOrder(){
//1、选择商品
doSelect();
//2、进行支付
doPayment();
//3、开具发票
doReceipt();
//4、派送商品
doDelivery();

}


public abstract void doSelect();
public abstract void doPayment();
public abstract void doReceipt();
public abstract void doDelivery();


}

2、定义实现类:​​NetOrder​​​ 和 ​​StoreOrder​

/**
* @author lfy
* @Description 网络订单:算法细节实现
* @create 2022-12-29 20:24
*/
public class NetOrder extends OrderProcessTemplate {
@Override
public void doSelect() {
System.out.println("把 xiaomi11 加入购物车");
}

@Override
public void doPayment() {
System.out.println("在线微信支付 1999");
}

@Override
public void doReceipt() {
System.out.println("发票已经发送给用户邮箱: aaaa@qq.com");
}

@Override
public void doDelivery() {
System.out.println("顺丰次日达:投送商品");
}
}
/**
* @author lfy
* @Description 门店订单:子类实现具体算法
* @create 2022-12-29 20:26
*/
public class StoreOrder extends OrderProcessTemplate {
@Override
public void doSelect() {
System.out.println("用户选择了:3号货架-xiaomi11 商品");
}

@Override
public void doPayment() {
System.out.println("刷卡机:刷卡支付 1999");
}

@Override
public void doReceipt() {
System.out.println("打印发票,和物品一起包装");
}

@Override
public void doDelivery() {
System.out.println("把商品交给用户,用漂亮的袋子");
}
}

3、进行测试

/**
* 设计模式: 多定义接口、抽象类
* 1)、依赖倒置; 依赖抽象
* 2)、多态; 随便替换实现
* @author lfy
* @Description 模板方法模式测试 ; 核心: 父类定义算法骨架,子类实现算法细节
* @create 2022-12-28 20:41
*/
public class TemplateMethodPatternTest {

public static void main(String[] args) {
//行为型模式玩的就是一个多态
//1、外界调用模板类【遵循依赖反转原则】【依赖抽象而不是细节】
OrderProcessTemplate processTemplate = new NetOrder();
System.out.println("网络订单:");
//处理订单
processTemplate.processOrder(); //定义了算法的模板


processTemplate = new StoreOrder();
System.out.println("门店订单:");
// 根据需要,选择不同的实现类,从而达到我们需要的效果:网络订单 或 门店订单
processTemplate.processOrder();
}
}

运行结果:

一文教会你巧用设计模式重构项目_重构_05

三、策略模式案例

**描述:**定义​​算法家族,分别封装​​​起来,让它们之间可以​​相互替换​​,此模式让算法的变化独立于使用算法的客户。

一文教会你巧用设计模式重构项目_重构_06

案例:排序算法

1、定义算法接口

/**
* @author lfy
* @Description 算法接口:排序策略
* @create 2022-12-29 20:36
*/
public interface SortStrategy {

/**
* 排序
*/
void sort(Integer[] arr);
}

2、定义策略类,实现算法接口

/**
* @author lfy
* @Description 策略1:冒泡排序策略
* @create 2022-12-29 20:38
*/
public class BubbleSortStrategy implements SortStrategy {
@Override
public void sort(Integer[] arr) {
System.out.println("开始冒泡排序....");
for (int i=0;i< arr.length-1;i++){
for (int j = 0; j < arr.length- 1 - i ; j++) {
if(arr[j] > arr[j+1]){
Integer temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
System.out.println("排序结果:"+ Arrays.asList(arr));

}
}
/**
* @author lfy
* @Description
* @create 2022-12-29 20:44
*/
public class QuickSortStrategy implements SortStrategy {
@Override
public void sort(Integer[] arr) {
System.out.println("快速排序开始");
Arrays.sort(arr);
System.out.println("排序结果:"+Arrays.asList(arr));

}
}

3、定义上下文对象(入口类):持有策略对象

/**
* @author lfy
* @Description 上下文:入口类
* @create 2022-12-29 20:45
*/
public class SortService {
/**
* 拿到一个排序算法
*/
private SortStrategy strategy;

/**
* 为了强制要求用户必须传入一个排序算法
* @param strategy
*/
public SortService(SortStrategy strategy){
this.strategy =strategy;
}

/**
* 随时动态更新排序算法
* @param strategy
*/
public void setStrategy(SortStrategy strategy) {
this.strategy = strategy;
}

/**
* 才是别人调用的排序方法
* @param arr
*/
public void sort(Integer[] arr){
strategy.sort(arr);
}
}

4、使用策略对象执行算法

/**
* @author lfy
* @Description 模板模式定义大框架、策略默认定义小细节
* @create 2022-12-28 21:17
*/
public class StrategyPatternTest {

public static void main(String[] args) {
Integer[] arr = new Integer[]{2,4,6,3,1,7,9,8};
// 传入不同的策略,就使用对应的策略
SortService sortService = new SortService(new BubbleSortStrategy());
sortService.sort(arr);

System.out.println("===============");

//更新策略
sortService.setStrategy(new QuickSortStrategy());
sortService.sort(arr);

}
}

测试结果:

一文教会你巧用设计模式重构项目_System_07

四、支付改造

4.1 思路分析

使用​​模板方法模式​​:定义支付的完整流程

使用​​策略模式​​:定义支付的不同实现

一文教会你巧用设计模式重构项目_ide_08

4.2 实现图解:

一文教会你巧用设计模式重构项目_java_09

分析:PayStrategy使用的是策略模式~ PayService中的processNotify里面包含了 一系列抽象方法的调用,不同的支付方式会有不同的实现,这里使用了模板方法模式~

4.3 代码实现:

1、支付策略接口:​​PayStrategy​

/**
* @author lfy
* @Description 支付策略
* @create 2022-12-28 22:39
*/
public interface PayStrategy {

/**
* 支持哪种支付
* @param type
* @return
*/
boolean supports(String type);

/**
* 为某个订单展示收银台页面
* @return
*/
String cashierPage(OrderInfo orderInfo);

/**
* 验证签名
* @param request 原生请求
* @param body 请求体数据【请求体只能读取一次,所以controller拿到以后都往下传递即可】
* @return
*/
boolean checkSign(HttpServletRequest request,String body);


/**
* 验签错误处理
* @return
*/
Object signError();


/**
* 验签通过返回
* @return
*/
Object signOk();

/**
* 验签成功后处理通知数据: 把通知的所有数据封装指定对象
* @param request
* @return
*/
Map<String,Object> process(HttpServletRequest request,String body);
}

2、接口实现:​​AlipayStrategy​​​ 、​​WeixinPayStrategy​

/**
* @author lfy
* @Description 支付宝
* @create 2022-12-28 22:40
*/
@Slf4j
@Component
public class AlipayStrategy implements PayStrategy {

@Autowired
AlipayProperties alipayProperties;

@Autowired
AlipayClient alipayClient;

@Override
public String cashierPage(OrderInfo orderInfo) {
//1、创建一个 AlipayClient

//2、创建一个支付请求
AlipayTradePagePayRequest alipayRequest = new AlipayTradePagePayRequest();

//3、设置参数
alipayRequest.setReturnUrl(alipayProperties.getReturn_url()); //同步回调:支付成功以后,浏览器要跳转到的页面地址
alipayRequest.setNotifyUrl(alipayProperties.getNotify_url()); //通知回调:支付成功以后,支付消息会通知给这个地址


//商户订单号(对外交易号)
String outTradeNo = orderInfo.getId().toString();
//付款金额
BigDecimal totalAmount = orderInfo.getPrice();
//订单名称
String orderName = "尚品汇-订单-"+outTradeNo;
//商品描述
String tradeBody = orderInfo.getDesc();

//详细:https://opendocs.alipay.com/open/028r8t?scene=22
//业务参数
Map<String,Object> bizContent = new HashMap<>();
bizContent.put("out_trade_no",outTradeNo);
bizContent.put("total_amount",totalAmount);
bizContent.put("subject",orderName);
bizContent.put("body",tradeBody);
bizContent.put("product_code","FAST_INSTANT_TRADE_PAY");
//自动关单
String date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(orderInfo.getExpireTime());
bizContent.put("time_expire",date);
alipayRequest.setBizContent(JSON.toJSONString(bizContent));


//生成支付页面
String page = null;
try {
page = alipayClient.pageExecute(alipayRequest).getBody();
} catch (AlipayApiException e) {
e.printStackTrace();
}

return page;
}

@Override
public boolean checkSign(HttpServletRequest request,String body) {
Map<String, String> params = HttpUtils.getParameterMap(request);
log.info("支付宝通知验证签名...");
//验证签名
try {
//调用SDK验证签名
boolean signVerified = AlipaySignature.rsaCheckV1(params,
alipayProperties.getAlipay_public_key(), alipayProperties.getCharset(),
alipayProperties.getSign_type());
return signVerified;
} catch (AlipayApiException e) {
e.printStackTrace();
}
return false;
}

@Override
public Object signError() {
return "error";
}

@Override
public Map<String,Object> process(HttpServletRequest request,String body) {
Map<String, String> map = HttpUtils.getParameterMap(request);
String json = JSON.toJSONString(map);
Map<String, Object> data = JSON.parseObject(json, new TypeReference<Map<String, Object>>() {
});
return data;
}

@Override
public Object signOk() {
//支付宝要求成功返回 success 字符串
return "success";
}


@Override //对新增开放,对修改关闭
public boolean supports(String type) {
return "alipay".equalsIgnoreCase(type);
}
}
/**
* @author lfy
* @Description 微信支付
* @create 2022-12-28 22:40
*/
@Slf4j
@Component
public class WeixinPayStrategy implements PayStrategy {
// 具体实现省略,可以到代码仓库查看
}

3、支付Service代码编写

/**
* @author lfy
* @Description
* @create 2022-12-28 22:36
*/
public interface PayService {

/**
* 生成支付收银台页
* @param type
* @param orderId
* @return
*/
String payPage(String type, Long orderId);


/**
* 处理支付通知
* @param request
* @return
*/
Object processNotify(HttpServletRequest request,String body);
}

4、支付Service实现

/**
* @author lfy
* @Description 支付上下文引用支付策略; 这个上下文也是模板类;定义好算法步骤
* @create 2022-12-28 22:46
*/
@Service
@Slf4j //模板类
public class PayServiceImpl implements PayService {

@Autowired
List<PayStrategy> payStrategies; //注入支付策略

/**
* 生成收银台页面
* @param type
* @param orderId
* @return
*/
@Override
public String payPage(String type, Long orderId) {
//1、查询数据库订单
OrderInfo orderInfo = getOrderInfo(orderId);

//2、生成支付页
for (PayStrategy strategy : payStrategies) {
if(strategy.supports(type)){
//获取收银台页面
return strategy.cashierPage(orderInfo);
}
}
//3、如果以上都不支持,打印错误
return "不支持这种支付方式";
}


/**
* 定义通知处理模板;
* 微信通知
* 支付宝通知
* 1)、验证签名
* 2)、验证通过改订单为已支付
* 3)、验证通过给支付宝(success)微信(200状态码json)返回数据
* 4)、xxx
* @param request
* @param body
* @return
*/
@Override
public Object processNotify(HttpServletRequest request,String body) {
Object result = "不支持此方式";

//1、判断是那种通知
String type = getNotifyType(request);
Map<String, Object> data = null;

//2、验证签名
for (PayStrategy strategy : payStrategies) {
if(strategy.supports(type)){
//签名校验
boolean checkSign = strategy.checkSign(request,body);
if(!checkSign){
log.error("签名验证失败,疑似攻击请求");
//验签失败返回
return strategy.signError();
}else {
log.info("签名验证成功,提取通知数据");
//验签成功处理数据
data = strategy.process(request,body);
//验签成功返回
result = strategy.signOk();
}
}
}

//3、通用的后续处理算法;处理订单数据
processOrder(data);

return result;
}

/**
* 处理订单数据
* @param data
*/
private void processOrder(Map<String, Object> data) {
//TODO 把支付成功信息等保存数据库,并修改订单状态,通知库存系统等...
log.info("订单支付成功,状态修改完成,已通知库存系统,详细数据:{}",data);
}

/**
* 判断通知类型
* @param request
* @return
*/
private String getNotifyType(HttpServletRequest request) {
String header = request.getHeader("wechatpay-serial");
if(StringUtils.hasText(header)){
return "weixin";
}

String app_id = request.getParameter("app_id");
if(StringUtils.hasText(app_id)){
return "alipay";
}


return "unknown";
}

public OrderInfo getOrderInfo(Long orderId){
log.info("查询数据库订单:{}",orderId);
OrderInfo orderInfo = new OrderInfo();
orderInfo.setId(orderId);
orderInfo.setTitle("尚品汇-商城-订单");
orderInfo.setComment("快点发货");
orderInfo.setDesc("买了一堆商品");
orderInfo.setPrice(new BigDecimal("9098.00"));
orderInfo.setExpireTime(new Date(System.currentTimeMillis()+30*60*1000));

return orderInfo;
}
}

分析:

一文教会你巧用设计模式重构项目_重构_10

一文教会你巧用设计模式重构项目_System_11

4.4 效果演示

1、用户进入支付页面后:

一文教会你巧用设计模式重构项目_重构_12

2、当选择微信支付时,进入微信二维码页面

一文教会你巧用设计模式重构项目_System_13

3、当选择支付宝支付,进入支付宝页面

一文教会你巧用设计模式重构项目_设计模式_14

4、支付成功之后会跳转到对应的成功页面(微信成功页面、支付宝成功页面…)

4.5 如何扩展

最后我们需要思考一个问题,经过我们使用策略+模板改造的支付 和传统的写法有啥好处呢?

1、易于扩展,如果我们想要增加新的支付方式:银联支付、白条支付…我们只需要定义对应的支付策略类即可~ 当不需要时,直接删除对应的实现类。然后在调用的时候,会自适应使用对应的策略~ 符合​​对修改关闭,对扩展开放​​的特点!

一文教会你巧用设计模式重构项目_重构_15

2、Service中的模板方法也让我们的代码冗余度更低,逻辑更加清晰~