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

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

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

普通的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是世界上最好的语言。