利用策略模式+单例模式+反射 替换if-else

2023年6月30日13:09:53

背景啥的就不讲了哈。有这方面需求或者感兴趣的阔以看看。

假定有这样的一种情况,需要根据用户传入的参数,选择不同的数据库来进行相应的操作。

普通的if-else来实现的话,就类似如下代码:

public static void main(String[] args) {
        String type="hbase";
        if (type.equals(DbTypeEnum.MYSQL_DRIVER.type())){
            MySQL mySQL=new MySQL();
            mySQL.getConnect();
            mySQL.excute();
            mySQL.disconnect();
        }else if (type.equals(DbTypeEnum.HBASE_DRIVER.type())){
            Hbase hbase=new Hbase();
            hbase.getConnect();
            hbase.excute();
            hbase.disconnect();
        }else if (type.equals(DbTypeEnum.MONGO_DRIVER.type())){
            Mongo mongo=new Mongo();
            mongo.getConnect();
            mongo.excute();
            mongo.disconnect();
        }else if (type.equals(DbTypeEnum.ORCAL_DRIVER.type())){
            Orcal orcal=new Orcal();
            orcal.getConnect();
            orcal.excute();
            orcal.disconnect();
        }else if (type.equals(DbTypeEnum.SQLSERVER_DRIVER.type())){
            SqlServer sqlServer=new SqlServer();
            sqlServer.getConnect();
            sqlServer.excute();
            sqlServer.disconnect();
        }else{
            System.out.println("did not have this database");
        }//逻辑异常 可以提前
    }

这里的DbTypeEnum是一个枚举类,没啥多讲的,有兴趣的自行baidu。

public enum DbTypeEnum {

    MYSQL_DRIVER("mysql"),

    ORCAL_DRIVER("orcal"),

    SQLSERVER_DRIVER("sqlserver"),

    HBASE_DRIVER("hbase"),

    MONGO_DRIVER("mogon")
    ;

    private String type;

    private DbTypeEnum(String type){
        this.type=type;
    }

    public String type() {
        return type;
    }
}

至于Mysql,Orcal等等,单纯就是一个普通的类。

public class MySQL {

    public void getConnect(){
        System.out.println("mysql connect");
    }

    public void excute(){
        System.out.println("mysql excute sql");
    }

    public void disconnect(){
        System.out.println("mysql disconnect");
    }

}

其他就不贴了。

以上就是普通做法,用if-else来进行判定,可以看出来这里臃肿。

如果要用其他方式替换掉,怎么办呢?

请让老夫慢慢道来。

首先创建一个DbStrategy接口。

public interface DbStrategy {

    public void excute();

}

然后各个数据库操作类实现它。

public class MySqlStrategy implements DbStrategy{
    @Override
    public void excute() {
        MySQL mySQL=new MySQL();
        mySQL.getConnect();
        mySQL.excute();
        mySQL.disconnect();
    }
}

其他HbaseStrategy啥的就不贴了哈。

到这里其实没有做太多的改变。无非就是把数据库的三个操作connect excute disconnect合在了一个excute类里面。

接下来就是重点了。

我们改造下DbTypeEnum将他改造成RefelDbTypeEnum。

public enum RefelDbTypeEnum {

    MYSQL_DRIVER("mysql","optimization.ifelse.strategy.MySqlStrategy"),

    ORCAL_DRIVER("orcal","optimization.ifelse.strategy.OrcalStrategy"),

    SQLSERVER_DRIVER("sqlserver","optimization.ifelse.strategy.SqlServerStrategy"),

    HBASE_DRIVER("hbase","optimization.ifelse.strategy.HbaseStrategy"),

    MONGO_DRIVER("mogon","optimization.ifelse.strategy.MongoStrategy"),
    ;

    private String type;
    private String clazz;

    private RefelDbTypeEnum(String type,String clazz){
        this.type=type;
        this.clazz=clazz;
    }

    public String type() {
        return type;
    }

    public String clazz(){
        return  clazz;
    }
}

与DbTypeEnum相比多了一个String clazz的属性。至于为什么要这样改,先不急,后面再讲。

最核心的部分来了哈。

新建一个ManagerStrategy管理类。

public class ManagerStrategy {

    private static Map<String,String> strategyMap = new HashMap<>();

    public static void excuteStrategy(String type){
        for (RefelDbTypeEnum t : RefelDbTypeEnum.values())
            strategyMap.put(t.type(), t.clazz());
        String class_path=strategyMap.get(type);
        try {
            /*
             * 通过反射将RefelDbTypeEnum中映射的类实例化
             * */
            Class clazz=Class.forName(class_path);
            Method excute =clazz.getDeclaredMethod("excute");
            excute.invoke(clazz.newInstance());
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }
    }
}

这里先是将RefelDbTypeEnum中的type和clazz属性读到了一个strategyMap的HashMap中。

然后根据用户传入的type判断要实例化哪个类。

如果传入的是mysql则class_path=optimization.ifelse.strategy.MySqlStrategy

通过Class.forName反射出该对象,并且执行其中的excute方法。

这里的class_path就是改造RefelDbTypeEnum中的包路径了。

这样就实现替换if-else了。

接下来是测试类:

 public static void main(String[] args) {
        String type="mysql";
        ManagerStrategy.excuteStrategy(type);
    }

利用策略模式+单例模式+反射 替换if-else

满足需求。

倘若如果是新增了一个ES数据库呢?

同样新建一个EsStrategy类实现DbStrategy接口。

public class EsStrategy implements DbStrategy{
    @Override
    public void excute() {
        System.out.println("ES connect");
        System.out.println("ES excute sql");
        System.out.println("ES disconnect");
    }
}

之后,只需要在RefelDbTypeEnum,多添加一个ES的映射就行了。

ES_DRIVER("es","optimization.ifelse.strategy.EsStrategy")

这里就可以看出来用设计模式的好处了,真正只有一个RefelDbTypeEnum的枚举类被改变了,就可以集成一个新的数据库操作类。

细心的人其实已经发现了。

上面的ManagerStrategy管理类其实有点问题。

因为每次调用excuteStrategy的时候都会重复将RefelDbTypeEnum中的type和clazz属性读到了一个strategyMap的HashMap中,这样是不合理的。

所以下面用了一个单例模式来解决这个问题。

新建一个StrategySingleton。

public class StrategySingleton {

    /*
    * 单例模式
    * */
    private static StrategySingleton instance=null;

    private StrategySingleton(){
    }

    private static synchronized void syncInit() {
        if (instance == null) {
            instance = new StrategySingleton();
        }
    }

    public static StrategySingleton getInstance() {
        if (instance == null) {
            syncInit();
        }
        return instance;
    }

    private static Map<String,String> strategyMap = new HashMap<>();
    static{
        for (RefelDbTypeEnum t : RefelDbTypeEnum.values())
            strategyMap.put(t.type(), t.clazz());
    }
    public String strategy(String type){
        return strategyMap.get(type);
    }
}

单例模式就不具体讲了哈,有兴趣的同学自行去了解。

同时重新改造ManagerStrategy管理类。

public static void excuteStrategy(String type){
        String clz=StrategySingleton.getInstance().strategy(type);
        try {
            /*
            * 通过反射将RefelDbTypeEnum中映射的类实例化
            * */
            Class clazz=Class.forName(clz);
            Method excute =clazz.getDeclaredMethod("excute");
            excute.invoke(clazz.newInstance());
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }
    }

如上,全部完成。

代码托管地址:

https://gitee.com/huangxiaoli/Rem-third-java.git

很久没搞Java了,如果有问题请指正。

如果还有更好的实现方法,请分享出来哈,三人行,必有师焉。

PS:PHP是世界上最好的语言。

  • 作者:lollipop诗
  • 原文链接:https://blog.csdn.net/u012840660/article/details/80484342
    更新时间:2023年6月30日13:09:53 ,共 5199 字。