One article teaches you how to use design patterns to refactor projects

This article refers to Raytheon of Shang Silicon Valley on 12.29 who cleverly used design patterns to refactor projects

1. Overview of design patterns

  • overall classification

image-20230113210444492

  • Choose different design patterns in different periods~

image-20230114113220058

  • Design patterns essentially play is: 封装, 继承,多态
  • Six principles of design patterns

image-20230114113416409

Two, template method pattern case

**Description:** The parent class (interface, abstract class) provides 定义算法的骨架aAllows subclasses to provide implementations for one or more steps, redefine some steps of the algorithm without changing the structure of the algorithm.

Case: Order Processing

1. Define the order processing template

/**
 * @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. Define the implementation class: NetOrderandStoreOrder

/**
 * @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("发票已经发送给用户邮箱: [email protected]");
    }

    @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. Test

/**
 * 设计模式:  多定义接口、抽象类
 * 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();
    }
}

operation result:

image-20230113220611870

3. Strategy pattern case

**Description:** Defined 算法家族,分别封装so that they can be used 相互替换, this mode makes the algorithm change independent of the client who uses the algorithm.

Case: sorting algorithm

1. Define the algorithm interface

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

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

2. Define the strategy class and implement the algorithm interface

/**
 * @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. Define the context object (entry class): hold the policy object

/**
 * @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. Use policy objects to execute algorithms

/**
 * @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);

    }
}

Test Results:

image-20230113220248469

4. Payment Transformation

4.1 Thought Analysis

Use 模板方法模式: defineThe complete process of payment;

Use 策略模式: definedifferent implementations of payments;

4.2 Implementation Diagram:

Analysis : PayStrategy uses a strategy pattern~ processNotify in PayService contains a series of abstract method calls, and different payment methods will have different implementations. The template method pattern is used here~

4.3 Code implementation:

1. Payment strategy interface: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. Interface realization: AlipayStrategyWeixinPayStrategy

/**
 * @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. Payment Service code writing

/**
 * @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. Realization of Payment 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;
    }
}

analyze:

image-20230113223331649

image-20230114125758333

4.4 Effect Demonstration

1. After the user enters the payment page:

image-20230114112440663

2. When choosing WeChat payment, enter the WeChat QR code page

image-20230114112525508

3. When you choose Alipay to pay, enter the Alipay page

image-20230114112626541

4. After the payment is successful, it will jump to the corresponding success page (WeChat success page, Alipay success page...)

4.5 How to extend

Finally, we need to think about a question. What are the benefits of payment after we use strategy + template transformation and traditional writing?

1,easy to expand, if we want to add new payment methods: UnionPay payment, Baitiao payment... we only need to define the corresponding payment strategy class~ When not needed, delete the corresponding implementation class directly. Then when calling, it will adaptively use the corresponding strategy ~ conforming to 对修改关闭,对扩展开放the characteristics!

image-20230114124615619

2. The template method in Service also allows ourLess code redundancy, the logic is clearer~

Guess you like

Origin blog.csdn.net/LXYDSF/article/details/128684193