spring中利用策略模式+工厂模式消除switch/case语句

357 阅读2分钟

背景

有一个SQL解析的方法,需要每种数据库类型都实现,入参出参和方法名均相同,根据数据库类型enum的不同值调用对应的方法。一开始是用switch/case方案写的,如下:


public class SqlStatementDetailParser {

    /**
    * sql : sql语句
    * dbTypeEnum : 数据库枚举类型
    **/
    public static SqlParserDetail parser(String sql, DbTypeEnum dbTypeEnum) {


        switch (dbTypeEnum) {
            case MYSQL:
                xxx
                break;
            case PGSQL:
                xxx
                break;
            case SQLSERVER:
                xxx
                break;
            default:
                throw new UnsupportedOperationException("Unsupported DB Type");
        }

        return result;
    }

}

上述代码中的xxx可能内容很长,如果有很多类型数据库,这个switch/case结构就会显得很冗长。而且如果case某种类别的处理逻辑太长,也会使得代码可读性很差。

如果是用手撸的工厂模式,很可能需要自行添加成员到工厂类的map里,可否利用spring的特性(注解+自动注入)减少代码量?

简化方案:巧妙利用spring的自动注入+策略模式+简单工厂模式

首先定义一个接口类,作为所有数据库类型解析SQL的父类。


public interface SqlDetailParser {  

// 不同的数据库类型返回,用于初始化map使用
DbTypeEnum getDbType();  
  
// 解析SQL方法
SqlParserDetailDTO parse(String sql);  
  
}

接着实现该SqlDetailParser接口,以mysql和postgresql两种数据库类型为例:


@Component  
public class MysqlStatementDetailParser implements SqlDetailParser {  
  
  
@Override  
public DbTypeEnum getDbType() {  
return DbTypeEnum.MYSQL;  
}  
  
@Override  
public SqlParserDetailDTO parse(String sql) {  
xxx
}  
}


@Component  
public class PostgresqlStatementDetailParser implements SqlDetailParser {  
  
@Override  
public DbTypeEnum getDbType() {  
return DbTypeEnum.PGSQL;  
}  
  
@Override  
public SqlParserDetailDTO parse(String sql) {  
xxx
}  
}

构建工厂类,利用spring的注解,自动注入到list中。


@Service  
public class SqlDetailParserFactory {  
  
// 工厂模式,建立map
private final Map<DbTypeEnum, SqlDetailParser> sqlRelatedMap = Maps.newConcurrentMap();  
  
// 因为SqlDetailParser的实现类添加了component注解,mysql和pg实现类会自动add到该list中
private final List<SqlDetailParser> sqlDetailParserList;  
  
// 构造函数  
public SqlDetailParserFactory(List<SqlDetailParser> sqlDetailParserList) {  
this.sqlDetailParserList = sqlDetailParserList;  
}  
  
// 初始化阶段,初始化map
@PostConstruct  
protected void init() {  
this.sqlDetailParserList.forEach(this::register);  
}  
  

private void register(SqlDetailParser sqlDetailParser) {  
sqlRelatedMap.put(sqlDetailParser.getDbType(), sqlDetailParser);  
}  


public SqlDetailParser createRelated(DbTypeEnum dbTypeEnum) {  
SqlDetailParser sqlDetailParser = sqlRelatedMap.get(dbTypeEnum);  
assertSqlParserAvailable(sqlDetailParser);  
return sqlDetailParser;  
}  
  
private void assertSqlParserAvailable(SqlDetailParser sqlDetailParser) {  
if (Objects.isNull(sqlDetailParser)) {  
// 抛出异常
throw new xxx;  
}  
}  
  
}

在上述的map初始化中,一般的工厂模式是将map定义为static,然后加静态块put元素进去。这里我们是利用了spring的component初始化时自动写入list的特性。

在调用时,只需用@Resource或@Autowired自动初始化factory类

@Resource  
private SqlDetailParserFactory sqlDetailParserFactory;

// 获取不同数据库类型的parser类
SqlDetailParser sqlDetailParser = sqlDetailParserFactory.createRelated(dbTypeEnum);

便可大功告成。