问题
在设计账户系统的过程中遇到这样一个问题,就是当第三方账户需要根据第三方账户的类型来当前的用户id和第三方的unionId进行一个绑定操作。
如果一般的做法呢就是写很多的if else通过判断不同的账户类型,来决定进行哪种第三方绑定的操作。
解决方案
下面是使用策略模式的做法,下面代码演示,只需关注策略模式的流程和实现,语法上不必深究
首先定义一个 策略接口
package com.third;
public interface ThirdInfoInterfaceStrategy {
void updateUserInfo(String unionId, TblUserInfo userInfo);
}
接着实现具体的三方绑定操作
可能有微信的方式
package com.third;
public class WechatUserInfoStrategy implements ThirdInfoInterfaceStrategy {
@Override
public void updateUserInfo(String unionId, TblUserInfo userInfo) {
userInfo.setWechatBind(1);
userInfo.setWechatUnionid(unionId);
userInfo.setLoginMethod(ThirdAccountOperator.WECHAT_LOGIN_METHOD);
}
}
可能有苹果的方式
package com.third;
public class AppleUserInfoStrategy implements ThirdInfoInterfaceStrategy {
@Override
public void updateUserInfo(String unionId, TblUserInfo userInfo) {
userInfo.setAppleBind(1);
userInfo.setAppleUserId(unionId);
userInfo.setLoginMethod(ThirdAccountOperator.APPLE_LOGIN_METHOD);
}
}
可能有淘宝的方式
package com.third;
public class TaobaoUserInfoStrategy implements ThirdInfoInterfaceStrategy {
@Override
public void updateUserInfo(String unionId, TblUserInfo userInfo) {
userInfo.setTaobaoBind(1);
userInfo.setTaobaoUserId(unionId);
userInfo.setLoginMethod(ThirdAccountOperator.TAOBAO_LOGIN_METHOD);
}
}
最后定义一个上下文类,统一执行所有策略绑定操作
Context类意义,使得策略的行为对外操作起来更为统一,注意策略模式是一个行为行的设计模式,
关注点在执行行为的那个方法,而不用关心,具体的策略方法,
当然了我觉得如果策略的行为特别简单,在决定要使用哪种策略的时候也可以直接用。
package com.third;
public class ThirdInfoBindContext{
// 使用组合的方式, 降低对行为实现类型的依赖
private ThirdInfoInterfaceStrategy thirdInfoInterfaceStrategy;
// 也可以通过构造方法的方式将具体策略传入
public void setThirdInfoInterfaceStrategy(ThirdInfoInterfaceStrategy thirdInfoInterfaceStrategy){
this.thirdInfoInterfaceStrategy = thirdInfoInterfaceStrategy;
}
//
public void bind(String unionId,TblUserInfo userInfo){
// 策略执行前做
。。。。
thirdInfoInterfaceStrategy.updateUserInfo(unionId,userInfo);
// 策略执行后
。。。。。。
}
}
// 简单调用,版本一
public class Client{
public static void main(String[] args) {
// 调用时才知道要使用具体哪个策略类,把变化延时到了使用的地方
// 在使用的时候,对于有没有这个类,其实是不关心的,
ThirdInfoInterfaceStrategy userInfoStrategy = new WechatUserInfoStrategy();
// 下面这部分代码都可以保持稳定,不变
ThirdInfoBindContext context = new ThirdInfoBindContext(userInfoStrategy);
String unionId = "123";
TblUserInfo userInfo = new TblUserInfo();
// 绑定的动作不会随着策略的变化而改变
context.bind(unionId,userInfo);
}
}
// 通过反射获取具体策略对象,版本二
public class Client{
public static void main(String[] args) {
// 这个可以实际策略类的名称配置文件来进行定义,这样就可以动态的运行想要调用的策略了
String strategyName = "com.third.WechatUserInfoStrategy";
Class<?> aClass = Class.forName(strategyName);
ThirdInfoInterfaceStrategy userInfoStrategy = (ThirdInfoInterfaceStrategy) aClass.newInstance();
// 下面这部分代码都可以保持稳定,不变
ThirdInfoBindContext context = new ThirdInfoBindContext(userInfoStrategy);
String unionId = "123";
TblUserInfo userInfo = new TblUserInfo();
// 绑定的动作不会随着策略的变化而改变
context.bind(unionId,userInfo);
}
}
// 配置文件+反射调用,版本三
public class Client{
public static void main(String[] args) {
// 用户登录的时候App端会将当前登录的第三方类型的参数传入type
// 我们可以根据参数的类型,来选择具体的策略,并且将type对应的策略名称写在配置文件中
// 实际使用起来就变成了下面这样
// 。。。。 省略前面的步骤
// 读取配置文件,将type和strategyName用一个Map存储
Map<String,String> config;
String strategyName = config.get(type);
Class<?> aClass = Class.forName(strategyName);
ThirdInfoInterfaceStrategy userInfoStrategy = (ThirdInfoInterfaceStrategy) aClass.newInstance();
// 下面这部分代码都可以保持稳定,不变
ThirdInfoBindContext context = new ThirdInfoBindContext(userInfoStrategy);
String unionId = "123";
TblUserInfo userInfo = new TblUserInfo();
// 绑定的动作不会随着策略的变化而改变
context.bind(unionId,userInfo);
}
}
配置
key[0] = weixin
value[0] = com.third.WechatUserInfoStrategy
key[1] = apple
value[1] = com.third.AppleUserInfoStrategy
.....
在版本三中实现了全流程代码不改动,完全适应业务,当新的第三方登录方式增加时,只需要增加代码的具体策略,和配置文件中的类型和策略名称即可。
这种做法可能在现如今这个jar包部署整个服务时候体现不出优势,因为我们不能向正在执行的jar包中添加其他的.class文件。
但在我接触的C# 项目中,这种反射+配置的做法,好处却时十分的明显,当新加一个功能时往往只需要在项目目录中加一个写好的.dll文件,
再将当前项目中的配置文件添加相关配置,即可完成功能的添加,使用起来十分的方便。
总结一下
- 1.当有许多if-else出现时,可以考虑使用策略模式
- 2.在Context中,使用组合的方式来获取具体的策略类,是一个值得学习的地方,即组合优于继承。
- 3.很多设计模式的出现,是因为变化太多,但是设计模式的意义并不是完全消灭变化,而是把变化像贴屏保的时候挤气泡一样,
把变化都集中在一个地方,这样更容易被管理,比如配置中心就应对变化的一个好去处。 - 4.策略模式虽然做到了很好的隔离性和拓展性,但是随着策略的增多,类会变的越来越多,这个问题我也遇到了,下一次来谈一下我面对的策略膨胀和解决方案。