策略+工厂+注解实战
2018-05-29 本文已影响53人
8813d76fee36
场景
在对接支付接口时,不同的支付渠道需要做不同的处理。后台接口可以要求前端将用户选择的支付渠道以参数的形式传递过来,然后我们就可以通过这个参数来判断究竟该采用处理方式来完成接口对接操作。
假设参数channel
代表支付渠道,可选值为wx
和alipay
,分别代表微信APP支付和支付宝支付。
PayService
类的userPay(String channel)
方法负责具体的对接逻辑。
简单实现
- 现在假设
PayService
类实现如下:
public class PayService {
public void userPay(String channel) {
if ("wx".equals(channel)) {
wxPay();
}
if ("alipay".equals(channel)) {
aliPay();
}
}
private void wxPay() {
System.out.println("微信支付");
}
private void aliPay() {
System.out.println("支付宝支付");
}
}
- 测试类实现如下:
public class Run {
public static void main(String[] args) {
PayService payService = new PayService();
payService.userPay("wx");
}
}
- 测试结果
测试结果
这种方式能够满足我们的需求,但是一旦支付模式有增加(比如引入银联、微信H5、微信公众号等)或者减少,那么就需要不停地维护userPay(String channel)
方法。针对这个问题,我们可以使用策略模式将选择渠道处理方式的过程与负责具体支付逻辑的userPay(String channel)
方法解耦。
引入策略模式

策略模式分为三个主要角色:
- Context持有一个策略的引用,这样它将无需关心策略的具体实现(本例中对应的就是PayService类)。
- Strategy接口是同类策略的一个抽象,同一类策略都将实现这个接口,并完成策略的具体逻辑。
- ConcreteStrateyA等是策略的具体实现。
策略接口
新建一个接口PayMode
代表支付方式的抽象,其中包含一个抽象方法pay()
来代表该方式的具体实现。
public interface PayMode {
void pay();
}
策略实现
新建WxPayMode
和AlipayMode
类,实现PayMode
接口,代表具体的支付实现。
- WxPayMode
public class WxPayMode implements PayMode {
@Override
public void pay() {
System.out.println("微信支付");
}
}
- AlipayMode
public class AlipayMode implements PayMode {
@Override
public void pay() {
System.out.println("支付宝支付");
}
}
改造PayService
public class PayService {
// 持有策略的引用
private PayMode payMode;
public void userPay(String channel) {
if ("wx".equals(channel)) {
payMode = new WxPayMode();
payMode.pay();
}
if ("alipay".equals(channel)) {
payMode = new AlipayMode();
payMode.pay();
}
}
}
测试
public class Run {
public static void main(String[] args) {
PayService payService = new PayService();
payService.userPay("alipay");
}
}

此时具体的渠道实现与
userPay()
方法分离,支付渠道实现逻辑如果有改动则只需要到具体的实现类修改即可,但我们仍需要使用if语句显示地做判断,为了解决这个问题,可以使用工厂+注解。
策略+工厂+注解
项目结构

- 新建@Channel注解,表示支付渠道
@Retention(value = RetentionPolicy.RUNTIME)
@Target(value = {ElementType.TYPE})
public @interface Channel {
String value();
}
- 新建工厂,根据渠道生成相应的策略实现
public class PayModeFactory {
// 获取类加载器
private final ClassLoader classLoader = getClass().getClassLoader();
// 策略实现的缓存 Key: channel(渠道) Value: 策略实现类的Class对象
private final Map<String, Class<?>> payModeMap = new HashMap<>();
// 策略所在包
private static final String BASE_PACKAGE = "dev.wj.pay.strategy";
// 工厂实例
private volatile static PayModeFactory instance;
// 私有化构造方法
private PayModeFactory() {
init(); //初始化操作,完成注解读取、渠道与策略映射
}
// 获取工厂实例
public static PayModeFactory getInstance() {
if (instance == null) {
synchronized (PayModeFactory.class) {
if (instance == null) {
instance = new PayModeFactory();
}
}
}
return instance;
}
private void init() {
try {
// 获取BASE_PACKAGE下所有的类文件
File basePackage
= new File(classLoader.getResource(BASE_PACKAGE.replace('.', File.separatorChar)).toURI());
File[] classFiles = basePackage.listFiles(file -> {
if (file.getName().endsWith(".class")) {
return true;
}
return false;
});
// 获取PayMode的实现类
Class<PayMode> payModeClass = PayMode.class;
for (File classFile : classFiles) {
Class<?> clazz = classLoader
.loadClass(BASE_PACKAGE
+ "."
+ classFile.getName().replace(".class", ""));
if (payModeClass.isAssignableFrom(clazz) && payModeClass != clazz) {
// 获得该类上的注解
Annotation[] annotations = clazz.getDeclaredAnnotations();
// 找到@Channel注解
for (Annotation annotation : annotations) {
if (annotation instanceof Channel) {
// 获得@Channel注解的值
String channel = ((Channel) annotation).value();
// 将该策略的实现类的Class对象和channel值放入Map缓存起来
payModeMap.put(channel, clazz);
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
// 根据渠道获取策略实现
public PayMode getPayMode(String channel) {
try {
Class<?> clazz = payModeMap.get(channel);
return (PayMode) clazz.newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
}
- 为策略的实现类加上表示渠道的注解
@Channel(value = "wx")
public class WxPayMode implements PayMode {
@Override
public void pay() {
System.out.println("微信支付");
}
}
@Channel(value = "alipay")
public class AlipayMode implements PayMode {
@Override
public void pay() {
System.out.println("支付宝支付");
}
}
- PayService改造
public class PayService {
// 持有策略的引用
private PayMode payMode;
public void userPay(String channel) {
payMode = PayModeFactory.getInstance().getPayMode(channel);
payMode.pay();
}
}
- 测试结果
public class Run {
public static void main(String[] args) {
PayService payService = new PayService();
payService.userPay("alipay");
}
}