背景啥的就不讲了哈。有这方面需求或者感兴趣的阔以看看。
假定有这样的一种情况,需要根据用户传入的参数,选择不同的数据库来进行相应的操作。
普通的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); }
满足需求。
倘若如果是新增了一个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是世界上最好的语言。