优雅的代码之选择不同支付方式

如何写出优雅的代码
我在这里举一个例子,当你选择一个商品时,对应的不同的支付方式,可以选择不同的支付方式,比如你可以选择农业银行支付可以享受打8折的优惠,选择建设银行支付可以享受打9折的优惠。
又比如同样一份文件,客户可以选择以word文件的形式下载,或者以pdf文件的格式下载,或者以Excel的文件格式下载。。。
其实有很对业务场景是这样的,我就不一一举例了,就拿第一个例子来说:
不同的银行都需要一个支付的功能,不同的银行各自的实现。前端页面传入一个银行id,此时我们需要通过这个银行id找到具体的实例对象。

key/value其中key是银行id,value是对应的实现类
实现类
实现类
strategyFactory
context
strategy pay的方法
农业银行
建设银行
这里key/value存的是银行id和对应的实现类,这里我们可以采用xml配置文件的方式,也可以采用自定义注解的方式。
这是银行表
这是对应的商品表
下面展示源码,对应的mapper和pojo这里就不展示了
首先写一个接口,这个接口有一个计算的方法,

public interface Strategy {
//其中goodsId为商品id,bankid为银行id
public double count(int goodsId,int bankId);

}
1
2
3
4
5
下面是它的两个实现类
这个是农业银行的实现类

@Component
@Pay(value=1)
public class NHbank implements Strategy{

@Autowired
private GoodsMapper goodsMapper;

@Autowired
private BankMapper bankMapper;
@Override
public double count(int goodsId, int bankId) {
// TODO Auto-generated method stub
Goods goods = goodsMapper.selectByPrimaryKey(goodsId);
Bank bank = bankMapper.selectByPrimaryKey(bankId);
System.out.println("我选择的是农业银行帮我付款");
return bank.getCount()*goods.getPrice();
}

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
下面是建设银行实现类

package cn.haha.ssm.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import cn.haha.ssm.mapper.BankMapper;
import cn.haha.ssm.mapper.GoodsMapper;
import cn.haha.ssm.po.Bank;
import cn.haha.ssm.po.Goods;
import cn.haha.ssm.utils.Pay;
import cn.haha.ssm.utils.Strategy;

@Component
@Pay(value=2)
public class JSbank2 implements Strategy{

@Autowired
private GoodsMapper goodsMapper;

@Autowired
private BankMapper bankMapper;
@Override
public double count(int goodsId, int bankId) {
// TODO Auto-generated method stub
Goods goods = goodsMapper.selectByPrimaryKey(goodsId);
Bank bank = bankMapper.selectByPrimaryKey(bankId);
System.out.println("我选择的是建设银行帮我付款");
return bank.getCount()*goods.getPrice();
}

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
下面是自定义注解类

package cn.haha.ssm.utils;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Pay {
public int value();
}

1
2
3
4
5
6
7
8
9
10
11
12
13
我们通过自定义注解类实例化对应的对象

package cn.haha.ssm.utils;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import org.reflections.Reflections;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.stereotype.Component;


public class StrategyFactory {
private static StrategyFactory strategyFactory=new StrategyFactory();
private StrategyFactory(){

}
public static StrategyFactory newInstance(){
return strategyFactory;
}
public static Map<Integer, String> sourceMap=new HashMap<>();

static{
//扫描含有自定义注解的包
Reflections reflections = new Reflections("cn.haha.ssm.service");
//将包中含有注解pay的类取出来
Set<Class<?>> typesAnnotatedWith = reflections.getTypesAnnotatedWith(Pay.class);
for (Class<?> class1 : typesAnnotatedWith) {
Pay pay = class1.getAnnotation(Pay.class);
//将银行bankId和对应的实体类放在map集合中
sourceMap.put(pay.value(),class1.getCanonicalName());
}
}

public Strategy create(int bankId) throws Exception{
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
//根据bankId得到对应的实现类。并实例化对象
String clazz = sourceMap.get(bankId);
Class<?> forName = Class.forName(clazz);
return (Strategy) applicationContext.getBean(forName);
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
下面计算打完折不同支付方式需要支付的金额

@Component
public class Context {
public double count(int goodsId,int bankId) throws Exception{
StrategyFactory strategyFactory = StrategyFactory.newInstance();
Strategy strategy = strategyFactory.create(bankId);
return strategy.count(goodsId, bankId);
}
}

1
2
3
4
5
6
7
8
9
我们需要将自己定义的类交给spring容器进行管理,不然会出现注入失败的问题
当然我们也可以实现ApplicationContextAware接口,当一个类实现了这个接口之后,这个类就可以方便的获取ApplicationContext中的所有的bean。使用这类很简单

package cn.haha.ssm.utils;

import java.lang.reflect.Field;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

public class BeanUtils implements ApplicationContextAware{
private static ApplicationContext context;

public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
// TODO Auto-generated method stub
this.context=applicationContext;
}

public static ApplicationContext getContext(){
return context;
}

public static void initBank(Class clazz) throws Exception{
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
//获取成员变量的类名
String simpleName = field.getType().getSimpleName();
//把类名的首字母改为小写
String topString = simpleName.substring(0, 1);
String lowerCase = topString.toLowerCase();
String beanName=lowerCase+simpleName.substring(1);
Object bean = context.getBean(beanName);
field.setAccessible(true);
field.set(this,bean);
}
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
之所以我们能如此方便的使用该工具来获取,正是因为Spring能过为我们自动的执行setApplicationContext方法,显然,这也是因为IOC的缘故,所以这个工具类也是需要在Spring的配置文件中进行配置的。
---------------------

posted on 2019-06-24 11:15  激流勇进1  阅读(434)  评论(0编辑  收藏  举报