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