代码重构之设计模式
1、什么是重构?
重构大致可以理解为,在保持功能不变的前提下,利用设计思想、原则、模式、编程规范等理论来优化代码,修改设计上的不足,提高代码质量。
2、 为什么要重构?
首先随着项目的不断演进,代码在堆砌,不免会产生一些过时的、冗余代码等。就需要重新整理,重新设计,使代码恢复设计模式和设计原则的限制。当然这里对于设计而言,重构是避免过度设计的有效手段。同时对于个人而言,重构也很锻炼程序员的功底,这是对设计模式,设计原则的实际应用,能快速提升程序员的水平。
3、介绍本次讲解重构中用到的4种设计模式:
1 工厂方法
追 MM 少不了请吃饭了,麦当劳的鸡翅和肯德基的鸡翅都是 MM 爱吃的东西,虽然口味有所不同,但不管你带 MM 去麦当劳或肯德基,只管向服务员说「来四个鸡翅」就行了。麦当劳和肯德基就是生产鸡翅的 Factory 工厂模式:客户类和工厂类分开。
消费者任何时候需要某种产品,只需向工厂请求即可。消费者无须修改就可以接纳新产品。缺点是当产品修改时,工厂类也要做相应的修改。如:如何创建及如何向客户端提供。
2 策略模式
跟不同类型的 MM 约会,要用不同的策略,有的请电影比较好,有的则去吃小吃效果不错,有的去海边浪漫最合适,其实都是为了得到 MM 的芳心,追 MM 锦囊中有好多 Strategy 哦。策略模式:策略模式针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换。
策略模式使得算法可以在不影响到客户端的情况下发生变化。策略模把行为和环境分开。环境类负责维持和查询行为类,各种算法在具体的策略类中提供。由于算法和环境独立开来,算法的增减,修改都不会影响到环境和客户端。
3 模板方法模式
比如说追女孩子步骤分为巧遇、打破僵局、展开追求、约会等步骤 (Template method),但每个步骤针对不同的情况,都有不一样的做法,这就要看你随机应变啦 (具体实现);
模板方法模式:模板方法模式准备一个抽象类,将部分逻辑以具体方法以及具体构造子类的形式实现,然后声明一些抽象方法来迫使子类实现剩余的逻辑。不同的子类可以以不同的方式实现这些抽象方法,从而对剩余的逻辑有不同的实现。先制定一个*逻辑框架,而将逻辑的细节留给具体的子类去实现。
4 责任链模式
晚上去上英语课,为了好开溜坐到了最后一排,哇,前面坐了好几个漂亮的 MM 哎,找张纸条,写上 “Hi, 可以做我的女朋友吗?如果不愿意请向前传”,纸条就一个接一个的传上去了,糟糕,传到第一排的 MM 把纸条传给老师了,听说是个老处女呀,快跑!
责任链模式:在责任链模式中,很多对象由每一个对象对其下家的引用而接起来形成一条链。请求在这个链上传递,直到链上的某一个对象决定处理此请求。客户并不知道链上的哪一个对象最终处理这个请求,系统可以在不影响客户端的情况下动态的重新组织链和分配责任。处理者有两个选择:承担责任或者把责任推给下家。一个请求可以最终不被任何接收端对象所接受。
在我们的项目中if ... else往往是用的最多的,而且到处都能看到大量的if ... else。那么if ... else 如果太多就会存在以下缺点:
a、代码逻辑复杂,维护性差,极容易引发 bug。
b、 耦合性强,不利于拓展。
c、容易引起误解和理解困难。
d、业务复杂还会导致代码臃肿。
4、常用的场景案例讲解:
public interface PayService { void toPay(String code); } @Service public class AliPay implements IPay { @Override public void pay() { // 处理支付宝支付逻辑 System.out.println("===支付宝支付==="); } } @Service public class UnionPay implements IPay { @Override public void pay() { // 银联支付逻辑 System.out.println("===银联支付==="); } } @Service public class WechatPay implements IPay { @Override public void pay() { // 微信支付逻辑 System.out.println("===微信支付==="); } } @Service public class PayServiceImpl implements PayService { @Autowired private AliPay aliPay; @Autowired private UnionPay unionPay; @Autowired private WechatPay wechatPay; @Override public void toPay(String code) { // 支付宝支付 if (PayCodeEnum.ALI_PAY.getCode().equals(code)) { aliPay.pay(); // 银联支付 } else if (PayCodeEnum.UNION_PAY.getCode().equals(code)) { unionPay.pay(); // 微信支付 } else if (PayCodeEnum.WECHAT_PAY.getCode().equals(code)) { wechatPay.pay(); } else { System.out.println("找不到支付方式"); } } }
思考下,这段代码应该如何去优化??? 这段代码存在哪些问题???
存在问题:
试想一下,如果支付方式越来越多,比如:后续项目需要增加京东支付、百度支付、美团支付等等,就需要修改toPay方法的代码,增加更多的else...if判断,业务越来越多,判断多了就会导致逻辑越来越多。
同时也违反了设计模式六大原则的:“开闭原则” 和“单一职责原则”
开闭原则:对扩展开放,对修改关闭。就是说增加新功能要尽量少改动已有代码。
单一职责原则:逻辑尽量单一,不要太复杂,便于复用 。
有哪些优化方案????
5、优化方案:
优化方案一:注解
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface PayCode { String value(); String name(); } @PayCode(value = "ALI_PAY",name = "支付宝支付") @Service public class AliPay implements IPay { @Override public void pay() { // 处理支付宝支付逻辑 System.out.println("===支付宝支付==="); } } @PayCode(value = "UNION_PAY", name = "银联支付") @Service public class UnionPay implements IPay { @Override public void pay() { // 银联支付逻辑 System.out.println("===银联支付==="); } } @PayCode(value = "WECHAT_PAY", name = "微信支付") @Service public class WechatPay implements IPay { @Override public void pay() { // 微信支付逻辑 System.out.println("===微信支付==="); } } @Service public class PayServiceAnnotationImpl implements PayService, ApplicationListener<ContextRefreshedEvent> { private static Map<String, IPay> payMap = null; @Override public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) { ApplicationContext applicationContext = contextRefreshedEvent.getApplicationContext(); Map<String, Object> beansWithAnnotation = applicationContext.getBeansWithAnnotation(PayCode.class); if (null != beansWithAnnotation) { payMap = new HashMap<>(); beansWithAnnotation.forEach((key, value) -> { String bizType = value.getClass().getAnnotation(PayCode.class).value(); payMap.put(bizType, (IPay) value); }); } } @Override public void toPay(String code) { payMap.get(code).pay(); } }
优化方案二:模板方法
public interface IPay { void pay(); Boolean support(String code); } @Service public class AliPay implements IPay { @Override public void pay() { // 处理支付宝支付逻辑 System.out.println("===支付宝支付==="); } @Override public Boolean support(String code) { return PayCodeEnum.ALI_PAY.getCode().equals(code); } } @Service public class UnionPay implements IPay { @Override public void pay() { // 银联支付逻辑 System.out.println("===银联支付==="); } @Override public Boolean support(String code) { return PayCodeEnum.UNION_PAY.getCode().equals(code); } } @Service public class WechatPay implements IPay { @Override public void pay() { // 微信支付逻辑 System.out.println("===微信支付==="); } @Override public Boolean support(String code) { return PayCodeEnum.WECHAT_PAY.getCode().equals(code); } } @Service public class PayServiceTemplateMethodImpl implements PayService, ApplicationContextAware, InitializingBean { private ApplicationContext applicationContext; private List payList = null; @Override public void afterPropertiesSet() throws Exception { if (null != payList) { payList = new ArrayList<>(); Map beansOfType = applicationContext.getBeansOfType(IPay.class); beansOfType.forEach((key, value) -> payList.add(value)); } } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } @Override public void toPay(String code) { for (IPay iPay : payList) { if (iPay.support(code)) { iPay.pay(); } } } }
优化方案三:工厂+策略模式
最优方案
/** * 支付策略工厂类 */ public class PayStrategyFactory { private static Map payMap = new HashMap<>(); public static void register(String code, IPay iPay) { if (StringUtils.isNotEmpty(code)) { payMap.put(code, iPay); } } public static IPay get(String code) { return payMap.get(code); } } @Service public class AliPay implements IPay { @PostConstruct public void init(){ PayStrategyFactory.register(PayCodeEnum.ALI_PAY.getCode(),this); } @Override public void pay() { // 处理支付宝支付逻辑 System.out.println("===支付宝支付==="); } } @Service public class UnionPay implements IPay { @PostConstruct public void init(){ PayStrategyFactory.register(PayCodeEnum.UNION_PAY.getCode(),this); } @Override public void pay() { // 银联支付逻辑 System.out.println("===银联支付==="); } } @Service public class WechatPay implements IPay { @PostConstruct public void init(){ PayStrategyFactory.register(PayCodeEnum.WECHAT_PAY.getCode(),this); } @Override public void pay() { // 微信支付逻辑 System.out.println("===微信支付==="); } } @Service public class PayServiceStrategyFactoryImpl implements PayService { @Override public void toPay(String code) { PayStrategyFactory.get(code).pay(); } }
优化方案四:责任链模式 (只为解释该设计模式使用)
@Data public abstract class PayHandler { private PayHandler nextPayHandler; public abstract void pay(String code); } @Service public class AliPayHandler extends PayHandler { @Override public void pay(String code) { if (PayCodeEnum.ALI_PAY.getCode().equals(code)) { System.out.println("===支付宝支付==="); } else { getNextPayHandler().pay(code); } } } @Service public class UnionPayHandler extends PayHandler { @Override public void pay(String code) { if (PayCodeEnum.UNION_PAY.getCode().equals(code)) { System.out.println("===银联支付==="); } else { getNextPayHandler().pay(code); } } } @Service public class WechatPayHandler extends PayHandler { @Override public void pay(String code) { if (PayCodeEnum.WECHAT_PAY.getCode().equals(code)) { System.out.println("===微信支付==="); } else { getNextPayHandler().pay(code); } } } @Service public class PayHandlerChainServiceImpl implements PayService, ApplicationContextAware, InitializingBean { private ApplicationContext applicationContext; private PayHandler payHandler; @Override public void afterPropertiesSet() throws Exception { Map beansOfType = applicationContext.getBeansOfType(PayHandler.class); if (beansOfType.isEmpty()) { return; } List handlers = beansOfType.values().stream().collect(Collectors.toList()); for(int i = 0 ; i < handlers.size(); i++ ) { PayHandler handler = handlers.get(i); if (i != handlers.size() - 1) { handler.setNextPayHandler(handlers.get(i + 1)); } } payHandler = handlers.get(0); } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } @Override public void toPay(String code) { payHandler.pay(code); } }
6、思考:如何利用设计模式优化
if ("文本".equals(type)) { // do something } else if ("图片".equals(type)) { // do something } else if ("视频".equals(type)) { // do something } else { // do something }
answer: