利用策略模式+单例模式+反射 替换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);
}


满足需求。

倘若如果是新增了一个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了,如果有问题请指正。

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

猜你喜欢

转载自www.cnblogs.com/magicellia/p/12000962.html