读大话设计模式有感-记一次代码重构历程

问题:为什么要代码重构?

最近,项目底层数据库升级,用的库呢是当下最热门的hadoop生态系统。数据库部门丢了两差异文档给我们写应用的,心想语法不会改动太大,大不了就是ctrl+r替换点关键字啥的。在制定mmp时,自信满满的跟经理报了一个很短的时间。

开始时,一切还挺顺利的,建表语句啥的改个关键字就完事了。

-- 旧
CREATE TABLE A (f_1 string,f_2 long....);
-- 新
CREATE SYSTABLE A (f_1 string,f_2 long...);

一看,这时间不绰绰有余嘛,喝喝茶,写一写自测用例完全够用。应届生来问问题,还能抽身去指点一下,俨然一位大佬模样。前前后后改了几个语法,不是很难,默认分区换个名字、函数换个名字啥的,测起来很快。

直到一个问题出现,执行下面这条sql时报错了,我也没多想,把问题发给了研究数据库的同事,然后就去睡午觉了。

SELECT f_1,f_2,f_3 FROM XXX WHERE f_3 < '2018-11-05 12:15:00'

后来得知,这次升级后底层scala解析sql的时候,完全不支持带空格的字符串比较。(内心os:fk)简单介绍下,目前用的这款数据库不支持Date类型,所以把日期数据以字符串存储。另外中存法就是存成数值类型,其实后者是比较好的方式,方便比较,但是当初因为某些原因没有选择数值型。

后续为了解决问题花了不少时间,但是这就不是本文的重点,不再这里详述。

总之,sql需要大改o(╥﹏╥)o,好在项目不是很大,利用ctrl+shift+f搜索出总共有三处都需要替换成新的sql,需要修改的部分如下:


//需要查询的字段
String name = (String) queryParams.get("name");
//需查询的字段类型,1:字符型、2:数值型、4:日期型
Integer type = (Integer) queryParams.get("type");
//关键字
Object query ;
switch(type){
    case 1:
        if(null == (query = queryParams.get("queryText") ){
            break;
        }
        //拼接字符串类型的条件
        if(oracle库 || gp库){//支持不区分大小写
            sql = " and lowercase(x) like '%" + a.toLowerCase() + "%'";
        }
        if(hive库){//严格区分大小写
            sql = " and x like '%a%'";
        }
        break;
    case 2:
        if(null == (query = queryParams.get("queryNumber") ){
            break;
        }
        ...
        break;
}

以上代码在三个地方实现了三次,刚好最近读到了部分代码重构的博客,当即决定把这部分代码提取出来。这样以后再改版数据库就不用再改三个地方啦。

同时每个case分支里还有针对不同数据库的判断逻辑:

if(dataBaseType == 1) {
    xxxx....
}
if(dataBaseType == 2) {
    yyyy....
}

由于if、for、switch嵌套太多,sonar大哥早报警了哈哈哈。所以决定把这段代码好好重构下。

好在之前打下了基础,在收到产品需要运行在多个版本的数据库的需求时,经理就安排我使用抽象工厂模式对底层基础sql进行封装。

public class Factory{
    public static ISql getBizSql() throws Exception{
        //从配置文件中获取
        int dataBaseType = ...;
        switch(dataBaseType){
            case 1:
                return new OracleSqlImpl();
            case 2:
                return new HiveSqlImpl();
            case 3:
            default:
                return new GpSqlImpl();
        }
    }
}

public interface ISql{

    //create
    public String createTable();

    //drop
    public String dropTable();

    //getCount
    public String getCount();
}

//抽象父类,提供公用方法,子类与父类写法一致时无需单独实现。
public abstract class AbsSql implements ISql{
    //针对部分sql所有数据库的写法一样时,可以不用在具体子类中实现
    @Override
    public String getCountSql(){
        return "select count(1) from xxx";
    }
}

//oracle实现
public class OracleSqlImpl extends AbsSql{
    @Override
    public String createTable(){
        return "create table xxx (f_1 number,f_2 varchar(255))";
    }
}

//hive实现
public class HiveSqlImpl extends AbsSql{
    @Override
    public String createTable(){
        return "create table xxx (f_1 number,f_2 string)";
    }

    //针对父类的写法无法使用的情况,子类可以单独再次重写
    @Override
    public String getCountSql(){
        return "select count(*) from xxx";
    }
}

//gp实现
public class GpSqlImpl extends AbsSql{
    @Override
    public String createTable(){
        return "create table xxx (f_1 numeric,f_2 varchar(255))";
    }
}

以上拼接条件的sql是个漏网之鱼,改版不会太难,只需要在上述代码中添加拼接条件的实现即可。

public interface ISql{

    //组装拼接查询条件
    public String buildSqlWithCondition();
}

//抽象父类,提供公用方法,子类与父类写法一致时无需单独实现。
public abstract class AbsSql implements ISql{
    //组装拼接查询条件
    @Override
    public String buildSqlWithCondition(){
        //需要查询的字段
        String name = (String) queryParams.get("name");
        //需查询的字段类型,1:字符型、2:数值型、4:日期型
        Integer type = (Integer) queryParams.get("type");
        //关键字
        Object query ;
        switch(type){
            case 1:
                if(null == (query = queryParams.get("queryText") ){
                    break;
                }
                ...
                //调用拼接字符串比较的逻辑
                appendStringCondition();
                break;
            case 2:
                if(null == (query = queryParams.get("queryNumber") ){
                    break;
                }
                ...
                //调用拼接数值等于比较的逻辑
                appendNumberCondition();
                break;
        }
    
    }
    
    //拼接字符串全模糊匹配抽象方法,由子类具体实现
    abstract StringBuilder appendStringCondition();
    
    //拼接数值等于比较抽象方法,由子类具体实现
    abstract StringBuilder appendNumberCondition();
}

//oracle实现
public class OracleSqlImpl extends AbsSql{
    @Override
    public StringBuilder appendStringCondition(){
        return "xxx";
    }

    @Override
    public StringBuilder appendNumberCondition(){
        return "xxx";
    }
}

//hive实现
public class HiveSqlImpl extends AbsSql{
    @Override
    public StringBuilder appendStringCondition(){
        return "xxx";
    }

    @Override
    public StringBuilder appendNumberCondition(){
        return "xxx";
    }
}

//gp实现
public class GpSqlImpl extends AbsSql{
    @Override
    public StringBuilder appendStringCondition(){
        return "xxx";
    }

    @Override
    public StringBuilder appendNumberCondition(){
        return "xxx";
    }
}

这次代码重构的灵感部分来自于“大话设计模式”,第一章就很有意思,后来翻阅第15章,标题叫做《就不能不换DB吗?》,讲述的就是抽象工厂模式,结合这次代码重构,我对这种设计模式的印象更加深刻了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值