在项目中应用“观察者模式”

在项目中应用“观察者模式”清除层层if...else if...的臃肿代码

一、前言

最近在项目用到了二十三种设计模式中的“观察者模式”,改善了代码结构,消除了层层if...else if...else if...的臃肿代码。深刻体会了这种“高明”的经验所带来的好处,感觉很开心,在此记录一下。

二、项目中遇到的问题

在项目开发中遇到这样一个场景:

客户将多个业务场景下的数据以excel文件的形式整理出来,每种业务场景下整理出的excel内容排版格式都不一样。现在需要开发程序将这些excel解析出来,存到关系库中,后续对这些数据进行其他的处理。

因为每种业务场景下的excel文件内容排版都不一样,所以要根据业务场景写特定的解析程序。最初的设计如下:

public void parseExcelFileBySceneType(String scenetype,File excelFile){
  if (StringUtils.equals(scenetype,"static_type")){
    parseStaticTypeExcel(excelFile);
  }else if(StringUtils.equals(scenetype,"supper_type")){
    parseSupperTypeExcel(excelFile);
  }else if(StringUtils.equals(scenetype,"run_type")){
    parseRunTypeExcel(excelFile);
  }else if(StringUtils.equals(scenetype,"bench_type")){
    parseBenchTypeExcel(excelFile);
  }else{
    throw new RuntimeException("该功能不存在");
  }
}

可以看上,每多一个业务场景,上面的代码就要多一层else...if,因此代码变得越来越臃肿。

三、用观察者模式解决

现在使用“观察者模式”设计出更优化的解决方案。

3.1 观察者模式回顾

在观察者模式中,主要有两类角色:主题(subject)、观察者(observer)。

可以看我之前写的内容:观察者模式类图代码示例

3.2 项目中使用观察者模式的思路

  • 主题

    带解析的excel文件,根据业务场景的不同,需要让特定的观察者去解析处理。

  • 观察者

    一个观察者只解析特定业务场景下的excel文件。

3.3 代码示例

  • 观察者接口:

    public interface ParseFileObserver{
      String getSceneType();
      /**
       * 解析文件
       */
      void parseFile(File excelFile);
    }
    
  • 主题接口:

    public interface ParseFileSubject{
      /**
       * 注册观察者
       */
      void registerObserver(ParseFileObserver observer);
      
      /**
       * 通知特定的观察者对excel解析
       */
      void notifyObserver(String sceneType,excelFile);
    }
    
  • 主题接口的实现类

    public class ParseFileSubjectImpl implement ParseFileSubject{
      /** 存放业务场景与观察者的一一映射 */
      private Map<String,ParseFileObserver> observerMap = new HashMap<>();
      
      /** 将业务场景和对应的观察者存放到映射表中 */
      @Override
      public void registerObserver(ParseFileObserver observer){
        observerMap.put(observer.getSceneType(),observer);
      }
      /** 让特定的观察者处理excel的解析 */
      @Override
      public void notifyObserver(String sceneType,excelFile){
        if(StringUtils.isBlank(sceneType)){
          throw new IllegalArgumentException("sceneType cannot empty");
        }else if(observerMap.containsKey(sceneType)){
          observerMap.get(sceneType).parseFile(excelFile);
        }else{
          throw new RuntimeException(String.format("%s业务场景下的解析功能还未开发",sceneType))
        }
      }
    }
    
  • 观察者接口的实现类(多个):

    第一个观察者StaticTypeObserver:

    public class StaticTypeObserver implement ParseFileObserver{
      private String sceneType;
      public StaticTypeObserver(ParseFileSubject parseFileSubject){
        // 可以定义一个枚举来赋值
        sceneType="staticType";
        parseFileSubject.registerObserver(this);
      }
      
      @Override
      public String getSceneType(){
        return this.sceneType;
      }
      /** 解析excel业务的流畅 */
      @Override
      public void parseFile(File excelFile){
        // 解析该场景下excel文件的逻辑
      }
    }
    

    第二个观察者SupperTypeObserver:

    public class SupperTypeObserver implement ParseFileObserver{
      private String sceneType;
      public SupperTypeObserver(ParseFileSubject parseFileSubject){
        // 可以定义一个枚举来赋值
        sceneType="supperType";
        parseFileSubject.registerObserver(this);
      }
      
      @Override
      public String getSceneType(){
        return this.sceneType;
      }
      /** 解析excel业务的流畅 */
      @Override
      public void parseFile(File excelFile){
        // 解析该场景下excel文件的逻辑
      }
    }
    

    其他观察者:

    ……

原创文章 161 获赞 19 访问量 6万+

猜你喜欢

转载自blog.csdn.net/hefrankeleyn/article/details/105446441