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

如何写出优雅的代码

我在这里举一个例子,当你选择一个商品时,对应的不同的支付方式,可以选择不同的支付方式,比如你可以选择农业银行支付可以享受打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);

}

下面是它的两个实现类
这个是农业银行的实现类

@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();
	}

}

下面是建设银行实现类

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();
	}

}

下面是自定义注解类

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();
}

我们通过自定义注解类实例化对应的对象

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 sourceMap=new HashMap<>();
	
	static{
		//扫描含有自定义注解的包
		Reflections reflections = new Reflections("cn.haha.ssm.service");
		//将包中含有注解pay的类取出来
		Set> 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);
	}
}

下面计算打完折不同支付方式需要支付的金额

@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);
	}
}

我们需要将自己定义的类交给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);
		}
	}
}

之所以我们能如此方便的使用该工具来获取,正是因为Spring能过为我们自动的执行setApplicationContext方法,显然,这也是因为IOC的缘故,所以这个工具类也是需要在Spring的配置文件中进行配置的。

你可能感兴趣的:(java开发)