简述:设计一个接口,该接口会根据接收的 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();

直观看类集成关系得知:

Robjectdelete方法.png

一个很重要的问题就是: 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多种数据结构的执行类匹配