为什么使用设计模式
今天终于有时间系统的整理一下这几个设计模式了, 这几个真是最常用的,用好了它们,你就在也不用一大堆的if else 了。能更好的处理大量的代码冗余问题。
在我们的实际开发中,肯定会有这样的场景:我们的某个方法被多次重复调用,但是每次呢,还需要稍微的改动里面一部分的内容。但是我们常常的写法就是,既然能复用,那我就把原来的接口copy出来一份再改一该里面的内容。(哦,nice,真快,三下五除二解决)但是这样的代码其实就真的只是能运行,给后期运维会带来很大的压力。
倘若我们系统第一版本就这么开发完成了,到第二版需求迭代时。
老板说:“把某某某功能改一下,原来的逻辑统一变了,这个就小改动应该很快吧,给你半天时间。”
然后你说:“我靠,那这工作量太大了, 我每个接口都有这个代码,这一变动,就要全部修改,我点需要两天时间。”
老板说:“这么个破修改你要两天,别再这摸鱼了啊,半天肯定能完事,我自己都能改完, 半天改不完,baibai”
然后你默默翻到我的文章,周末两天连续加班,修改了所有代码,全用上了这套设计模式,以后老板给你估算半天修改时间, 你抢答说:“不用,一两个小时吧就够了”。哈哈哈
设计模式介绍
工厂模式(Factory Pattern)是一种创建型设计模式,用于创建对象的实例化逻辑与客户端代码的分离。在工厂模式中,通过工厂类封装实例化对象的过程,客户端代码不需要直接调用构造函数来实例化对象,而是通过工厂类来创建对象。这样可以提供更灵活性和可维护性,且符合开闭原则。工厂模式常见的几种形式包括简单工厂模式、工厂方法模式和抽象工厂模式。
模板模式(Template Pattern)是一种行为设计模式,用于定义算法的骨架,将可变部分延迟到子类中实现。在模板模式中,定义一个模板方法用于规定算法的流程,同时定义一些抽象方法或钩子方法,子类可以重写这些方法以定义具体实现。通过模板模式,可以避免重复的代码,并提供一个统一的算法框架。
策略模式(Strategy Pattern)是一种行为设计模式,用于定义一系列算法,并将每个算法封装成一个独立的策略对象,客户端可以根据需要在运行时选择算法。在策略模式中,算法之间可以互相替换,独立于客户端,并相互独立运行。策略模式可以提高代码的灵活性和复用性,同时符合开闭原则。
实战代码讲解
常规写法
我们就拿最简单最热门的业务,电商项目的支付功能来讲解。
首先我们创建一个支付的接口。
public interface WxPayService {
void pay(PayParams payParams);
}
public interface AliPayService {
void pay(PayParams payParams);
}
随便定义一下这个接口参数
@Data
public class PayParams {
private String name;
}
然后像我们正常的Javaweb项目一样,创建正常的MVC三层结构。
public class WxPayServiceImpl implements PayService{
// 实现接口中的方法
@Override
public void pay(PayParams payParams) {
// TODO 第一步:检验一些数据参数是否合格
// TODO 第二步:微信支付具体步骤
// TODO 第三步:支付成功,进行一些回写、赋值、返回等等操作。
}
}
public class AliPayServiceImpl implements PayService{
// 实现接口中的方法
@Override
public void pay(PayParams payParams) {
// TODO 第一步:检验一些数据参数是否合格
// TODO 第二步:支付宝支付具体步骤
// TODO 第三步:支付成功,进行一些回写、赋值、返回等等操作。
}
}
最后再定义一个controller层,调用
@Autowired
private WxPayService wxPayService;
@Autowired
private AliPayService aliPayService;
@PostMapping(value = "/WxPay")
public ResponseResult pay(@RequestBody PayParams params) {
wxPayService.pay(params);
return ResponseResult.success();
}
@PostMapping(value = "/AliPay")
public ResponseResult pay(@RequestBody PayParams params) {
aliPayService.pay(params);
return ResponseResult.success();
}
这样是实现了我们的支付功能,一个微信支付,一个支付宝支付。但是我们会发现,这样写出来的代码,冗余非常多。
接下来我把这个业务代码修改为使用设计模式的格式。
设计模式后的代码
首先我们还是创建一个支付接口。
public interface PayService {
void pay(PayParams payParams);
}
然后我们来,实现这个接口,因为我们目前就有两种支付方式,等项目后续升级肯定还会接入越来越多的支付方式,所以我们这里目前就要有两个实现类来实现这个接口, 以后可能更多。
观看上图我们有发现,在实际的业务逻辑中,其实每个支付的代码中的第一步和第三步都是一样的,所以我们可以把它抽离成一个模板。模板模式这就被使用了。代码如下:
public abstract class PayServiceAbstract implements PayService{
// 实现接口中的方法
@Override
public void pay(PayParams payParams) {
before(payParams);
realPay(payParams);
after(payParams);
}
// 把这个核心支付功能定义为抽象的方法,留给具体的策略实现类去实现。
public abstract void realPay(PayParams payParams);
// 支付之前、之后的操作在抽象类中就定义一个模板方法,
// 只要掉用了这个抽象方法就自己有该模板可使用。
public void before(PayParams payParams){
System.out.println("支付前操作");
}
public void after(PayParams payParams){
System.out.println("支付后操作");
}
}
上面代码中,我们是用一个抽象类实现了接口,但是抽象还不是具体的实现,它只是固定了一个模板,接下来我们就要有每个支付方式自己的类去做具体的实现,这个也就是策略模式。对一个接口分别使用不同的策略来做不同的事。
支付宝支付的具体策略服务
@Service
public class AliPayServiceAbstractImpl extends PayServiceAbstract{
@Override
public void realPay(PayParams payParams) {
System.out.println("支付宝支付处理中。。。。");
}
}
微信支付的具体策略服务
@Service
public class WxPayServiceAbstractImpl extends PayServiceAbstract{
@Override
public void realPay(PayParams payParams) {
System.out.println("微信支付处理中。。。。");
}
}
ok,到目前,策略模式 + 模板模式就使用成功了,每当需求再叠加时,我们只需要再加一个具体的策略服务就ok了。别的地方都不需要动。
接下来时控制层的调用。我们如果还是用原来的controller,定义一个微信支付接口一个支付宝支付接口,那也太麻烦了。而且如果以后再有个银行卡支付,apple支付,那每次还要新增接口,真麻烦。所以我们不妨使用工厂模式来解决这个问题,让工厂来帮我们确定到底要用哪种支付。这样就来到了我们的工厂模式。
定义一个工厂
@Service
public class PayFactory {
// payType 哪个具体策略服务
public PayServiceAbstract getFactory(String payType){
try {
Class<?> aClass = Class.forName("com.bihe.controller."+payType);
PayServiceAbstract payServiceAbstract = (PayServiceAbstract) aClass.getDeclaredConstructor().newInstance();
return payServiceAbstract;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
上面的代码中,我们是用了Java反射,根据我们传递进来的类名,自动反射创建出具体的策略对象。
最后就是我们的控制层了。
@PostMapping(value = "/pay")
public ResponseResult pay(@RequestBody PayParams params) {
PayServiceAbstract payServiceAbstract = payFactory.getFactory(params.getName());
payServiceAbstract.pay(params);
return ResponseResult.success();
}
这就非常简单了, 我们永远只用这一样接口,前端也不需要判断我什么时候调用weixin支付,什么时候调用ali支付…
前端可以一直调用这个一个接口就可以了,只是把响应支付的关键参数传递过来就可以了。
前端调用