【长沙研发中心】代码重构之设计模式

简介: 代码重构之设计模式

代码重构之设计模式(一)


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<IPay> payList = null;


   @Override

   public void afterPropertiesSet() throws Exception {

       if (null != payList) {

           payList = new ArrayList<>();

           Map<String, IPay> 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<String, IPay> 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<String, PayHandler> beansOfType = applicationContext.getBeansOfType(PayHandler.class);

       if (beansOfType.isEmpty()) {

           return;

       }

       List<PayHandler> 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:


@Data

public class MessageInfo {


   private Integer type;

   private String content;

}


public interface MessageService {


  void handleMessage(MessageInfo messageInfo);

}


@MessageHandlerAnnotation(value = MsgTypeEnum.TEXT)

@Service

public class TextMessageService implements MessageService{

   @Override

   public void handleMessage(MessageInfo messageInfo) {

       System.out.println("===文本消息===" + messageInfo.getContent());

   }

}


@MessageHandlerAnnotation(value = MsgTypeEnum.IMAGE)

@Service

public class ImageMessageService implements MessageService{

   @Override

   public void handleMessage(MessageInfo messageInfo) {

       System.out.println("===图片消息===" + messageInfo.getContent());

   }

}


@MessageHandlerAnnotation(value = MsgTypeEnum.VIDEO)

@Service

public class VideoMessageService implements MessageService{

   @Override

   public void handleMessage(MessageInfo messageInfo) {

       System.out.println("===视频消息===" + messageInfo.getContent());

   }

}



@Retention(value = RetentionPolicy.RUNTIME)

@Target(value = ElementType.TYPE)

public @interface MessageHandlerAnnotation {

   MsgTypeEnum value();

}



public enum MsgTypeEnum {

   TEXT(1, "文本"),

   IMAGE(2, "图片"),

   VIDEO(3, "视频");


   public final int code;

   public final String name;


   MsgTypeEnum(int code, String name){

       this.code = code;

       this.name = name;

   }

}



@Component

public class MessageServiceContext {


   private final Map<Integer, MessageService> handlerMap = new HashMap<>();


   public MessageService getMessageService(Integer code) {

      return handlerMap.get(code);

   }


   public void putMessageService(Integer code, MessageService messageService) {

       handlerMap.put(code, messageService);

   }

}


@Component

public class MessageServiceListener implements ApplicationListener<ContextRefreshedEvent> {

   @Override

   public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {

       Map<String, Object> beansWithAnnotation = contextRefreshedEvent.getApplicationContext().getBeansWithAnnotation(MessageHandlerAnnotation.class);

       MessageServiceContext messageServiceContext = contextRefreshedEvent.getApplicationContext().getBean(MessageServiceContext.class);

       beansWithAnnotation.forEach((key, value) -> {

           MessageHandlerAnnotation messageHandler = value.getClass().getAnnotation(MessageHandlerAnnotation.class);

           messageServiceContext.putMessageService(messageHandler.value().code, (MessageService) value);

       });

   }

}



@SpringBootTest

class DesignModeApplicationTests {


   @Autowired

   private MessageServiceContext messageServiceContext;


   @Test

   void contextLoads() {

       MessageInfo messageInfo = new MessageInfo();

       messageInfo.setType(1);

       messageInfo.setContent("文本文本");

       MessageService messageService = messageServiceContext.getMessageService(messageInfo.getType());

       messageService.handleMessage(messageInfo);

   }

}

相关文章
|
8月前
|
自然语言处理
|
8月前
|
JSON API 开发者
1688店铺所有商品API接口(1688API系列)
1688店铺所有商品API接口允许开发者通过输入店铺ID,获取指定店铺内的全部商品信息,包括名称、价格、库存、图片和销售数据等。该接口支持排序和分页参数,返回JSON格式数据,便于解析和应用。Python示例展示了如何使用requests库发送GET请求并处理响应,助力电商数据分析与业务拓展。
|
存储 监控 安全
服务器维护是确保服务器稳定运行、数据安全和性能优化的重要过程
【10月更文挑战第4天】服务器维护是确保服务器稳定运行、数据安全和性能优化的重要过程
324 65
|
12月前
|
Kubernetes Cloud Native Devops
云原生技术在现代软件开发中的应用与挑战####
云原生,这一词汇如同云计算浪潮中的灯塔,引领着技术革新的方向。本文旨在探讨云原生技术的核心概念、关键组件及其在现代软件开发中的实践应用,同时剖析面临的挑战与应对策略。通过深入分析Kubernetes、微服务架构、DevOps文化等要素,揭示云原生如何赋能企业实现高效、弹性的IT系统构建,并展望其未来发展趋势。 ####
118 27
|
12月前
|
消息中间件 安全 数据安全/隐私保护
《进程隔离机制:C++多进程编程安全的坚固堡垒》
在数字化时代,C++多进程编程的安全性至关重要。进程隔离机制通过内存隔离、资源访问控制和安全的进程间通信,有效防止数据泄露和恶意攻击,确保各进程独立运行,互不影响,为软件系统的安全稳定提供坚实保障。
184 10
|
Web App开发 Java Python
生成github贡献者前14头像组
生成github贡献者前14头像组
204 0
|
人工智能 搜索推荐 机器人
[AI Mem0] 概览,智能自我改进记忆层
[AI Mem0] 概览,智能自我改进记忆层
|
存储 关系型数据库 数据库
初探PostgreSQL体系结构
初探PostgreSQL体系结构
345 0
|
存储 算法 C++
C++一分钟之-容器概览:vector, list, deque
【6月更文挑战第21天】STL中的`vector`是动态数组,适合随机访问,但插入删除非末尾元素较慢;`list`是双向链表,插入删除快但随机访问效率低;`deque`结合两者优点,支持快速双端操作。选择容器要考虑操作频率、内存占用和性能需求。注意预分配容量以减少`vector`的内存重分配,使用迭代器而非索引操作`list`,并利用`deque`的两端优势。理解容器内部机制和应用场景是优化C++程序的关键。
318 5
|
JavaScript 前端开发 API