简述:设计一个接口,该接口会根据接收的 Type 类型,再去分发到对应的实现方法上。例如:用户选择不同的支付渠道:微信、支付宝、网银等等,支付过程中调用对应的 SDK 进行结算。
具体场景是:在之前的 缓存方案:单机 Redis 使用 Java 客户端:Jedis + 豌豆荚开源项目 Codis ,向 集群方案 Redis Cluster 使用 Java 客户端 :Redisson,进行迁移 存在 API 差异:
# 删除 key ,在 Jedis 中的实现:
public Long del(String key) {
client.del(key);
return client.getIntegerReply();
}
# Java基本类型 删除 key, 在 Redisson 中的实现
RedissonClient client = Redisson.create(config);
RBucket<Object> rBucket = client.getBucket(Key);
rBucket.delete();
# Java基本类型 删除 key 都沿用或者继承该接口:
boolean delete();
# collection 类型 删除 key
RedissonClient client = Redisson.create(config);
RSet<String> rSet = client.getSet(Key);
rSet.clear();
rSet.delete();
# collection 类型 删除 key 都沿用或者继承该接口:
boolean delete();
void clear();
直观看类集成关系得知:
一个很重要的问题就是: Jedis 里面 直接 根据 key 就可以删除对应的 任何类型,而 Redisson 是 要先得知 Type 类型,再去 getType(), 执行删除。这些设计由点带面,影响到了整个 迁移方案
ps 实在没看懂 我指出的具体场景问题也没关系。最开始的简述也足够通用理解。
道理已经讲完了,开始写代码吧,敲黑板!
一般解决思路
1:构建一个 GeneralChannelRule 基础规则抽象类,定义一个抽象方法process(),不同的渠道都需要实现该抽象方法。
public abstract class GeneralChannelRule {
public abstract void process();
}
2:编写一个支付宝的规则类,定义具体对于支付宝渠道数据的处理逻辑
public class AliPayChannelRule extends GeneralChannelRule
@Override
public void process() {
// 支付宝处理逻辑
}
}
# 当然还有其他渠道的,不一一表出
public class WeChatPayChannelRule extends GeneralChannelRule
@Override
public void process() {
// 微信支付处理逻辑
}
}
....
3:建立一个简单的枚举类
public enum ChannelRuleEnum {
/**
* 支付宝支付
*/
ALIPAY("ALIPAY"),
/**
* 微信支付
*/
WECHATPAY("WECHATPAY"),
;
....
}
4:使用规则对数据进行处理
public static void main(String[] args) {
//这里我们模拟接收到的数据,其渠道为ALIPAY,需要调用支付宝接口
String sign = "ALIPAY";
GeneralChannelRule rule;
//根据对应渠道获取对应的具体规则实现类
if (ChannelRuleEnum.ALIPAY.code.equals(sign)) {
rule = new AliPayChannelRule();
} else if (ChannelRuleEnum.TOUTIAO.code.equals(sign)) {
rule = new WeChatPayChannelRule();
} else {
//匹配不到
}
//执行
rule.process();
}
解析:如果通过上面的方式,则存在则两个缺点。
当我们需要新增新的渠道的时候,需要对main方法中的逻辑进行修改调整。这违背了设计模式中的开放封闭规则。
开放封闭原则的核心的思想是软件实体是可扩展,而非不可修改的。
也就是说,对扩展是开放的,而对修改是封闭的。
新增渠道后,修改代码会产生大量的if else,不太优雅。为了解决以上的两个问题,我们可以借助枚举类来巧妙优化。
思路调整
1、调整一下枚举类,增加一个GeneralChannelRule属性,并且给对应渠道构建对应的GeneralChannelRule实现类,新增一个match() 匹配方法。
public enum ChannelRuleEnum {
/**
* 支付宝支付
*/
ALIPAY("ALIPAY",new AliPayChannelRule()),
/**
* 微信支付
*/
WECHATPAY("WECHATPAY",new WechatPayChannelRule()),
;
public String name;
public GeneralChannelRule channel;
ChannelRuleEnum(String name, GeneralChannelRule channel) {
this.name = name;
this.channel = channel;
}
//匹配
public static ChannelRuleEnum match(String name){
ChannelRuleEnum[] values = ChannelRuleEnum.values();
for (ChannelRuleEnum value : values) {
if(value.name.equals(name)){
return value;
}
}
return null;
}
public String getName() {
return name;
}
public GeneralChannelRule getChannel() {
return channel;
}
}
# 两个业务处理实现类
# 支付宝支付
public class AliPayChannelRule extends GeneralChannelRule {
@Override
public void process(String sign) {
System.out.println("支付宝支付");
}
}
# 微信支付
public class WechatPayChannelRule extends GeneralChannelRule {
@Override
public void process(String sign) {
System.out.println("微信支付");
}
}
2:改写程序
public static void main(String[] args) {
String sign = "ALIPAY";
ChannelRuleEnum channelRule = ChannelRuleEnum.match(sign);
GeneralChannelRule rule = channelRule.channel;
rule.process(sign);
}
解析:通过使用枚举类,在枚举中将 key 与 规则具体实现进行绑定。通过改变:
可以减少if -else使得代码更加优雅 如果需要新增渠道,我们只需要在编写具体规则实现类并继承GeneralChannelRule抽象类,并在枚举类中新增的枚举,而不需要改动到原先的任何代码。这符合了开发封闭原则
最后
基于此改写,并结合 Redisson 的 API 特点,我最终的逻辑代码如下:
/**
* 通用方法
* @param dataStructure
* @return target 返回具体执行类
*/
private GeneralRedissonDataStructureEnum getGeneralRedissonDateStructureEnum(DataStructure dataStructure) {
String dateStructureType = dataStructure.getName();
return RedissonDateStructureEnum.match(dateStructureType).redissonDateStructure;
}
# 如果是需要删除某个指定的 key
public void remove(String key, final DataStructure dataStructure) {
this.getGeneralRedissonDateStructureEnum(dataStructure).remove(key);
}
# 具体类的实现方法
@Override
public void remove(String key) {
redissonClient.getSet(key).clear();
}
基于此,最终实现了10多种数据结构的执行类匹配