如何写出一手优雅的代码?

代码大家都写过,每个人水平有高也有底的,写出来的代码也参吃不齐,往往大神们写出来的代码不仅简洁,而且看起来超级舒服,今天来领略一次框架级的代码,而且场景绝对是开发中经常遇到的。

先来看一下这个场景,需求就是:根据用户选择的不同银行,计算出不同的价格。

刚接到这个份需求时,你会不会觉得很简单,就几个if判断就可以了,比如这样:

是不是很眼熟,是不是都写过,我自己之前写的都是这种的。

这种场景是我们开发中到的,多流程判断。

以上的代码有什么问题呢?

  1. 这代码不利于维护,全部耦合在一起,很臃肿。
  2. 这里有很多的魔法数字,1 2 3可以代表很多的状态

第二个问题好解决,我们可以用常量和枚举来解决。第一个问题呢?思考一下该如何解决。

拿到需求先进行分析:

分析出它们之间的共性和个性。

通过分析我们可以抽离出一个支付的接口,创建Strategy接口

/**
 * @author: LKP
 * @date: 2019/1/2
 */
public interface Strategy {

    /**
     * 根据支付渠道类型去调用银行的优惠信息
     * @param channelId
     * @return
     */
    String calRecharge(Integer channelId);

}

接下来就直接上代码,你看完代码就可以很好的理解了。

创建一个银行的实现类:

**
 * @author: LKP
 * @date: 2019/1/2
 */
@Pay(channelId = 1)
public class ICBCBank implements Strategy {

    @Override
    public String calRecharge(Integer channelId) {
        return "我是ICBC";
    }

}

创建@Pay注解

/**
 * @author: LKP
 * @date: 2019/1/2
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Pay {
    int channelId();
}

创建一个上下文

/**
 * @author: LKP
 * @date: 2019/1/2
 */
public class Context {

    /**
     * 根据上文(渠道ID)得到下文(具体的某个实现类)
     * @param channlId
     * @return
     */
    public String chalRecharge(Integer channlId) throws Exception {

        // 我们需要一个类帮我们创建具体的实现类,怎么创建的,我不关心,我的
        // 条件就是channlid

        StrategyFactory strategyFactory = StrategyFactory.getInstance();
        Strategy strategy = strategyFactory.create(channlId);

        return strategy.calRecharge(channlId);
    }
}

创建一个工厂,这里用到了工厂方法设计模式和单例模式

/**
 * @author: LKP
 * @date: 2019/1/2
 */
public class StrategyFactory {

    private static StrategyFactory strategyFactory = new StrategyFactory();

    private StrategyFactory() {
    }

    public static HashMap<Integer, String> source_map = new HashMap<>();

    static {
        Reflections reflections = new Reflections("com.study.lkp.demoStudy.CleanCode.pay.impl");
        Set<Class<?>> classSet = reflections.getTypesAnnotatedWith(Pay.class);

        for (Class<?> aClass : classSet) {
            Pay pay = aClass.getAnnotation(Pay.class);
            source_map.put(pay.channelId(), aClass.getCanonicalName());
        }
    }

    /**
     * 具体的创建某个银行实现类的方法
     *
     * @param channelId
     * @return
     */
    public Strategy create(int channelId) throws Exception {
        //卡在没有map,现在有map
        String className = source_map.get(channelId);
        Class<?> aClass = Class.forName(className);
        return (Strategy) aClass.newInstance();
    }

    public static StrategyFactory getInstance() {
        return strategyFactory;
    }

}

代码基本就是这样,接下来就是测试了:

/**
 * @author: LKP
 * @date: 2019/1/2
 */
public class TestMain {

    public static void main(String[] args) throws Exception {
        Context context = new Context();
        System.out.println(context.chalRecharge(1));
    }

}

结果是:

整个流程是没有问题的,接下来我们需要增加一个银行,该怎么实现呢?

很简单,只要复制一份ICBCBank类,修改一下类名,还有注解的channel值

/**
 * @author: LKP
 * @date: 2019/1/2
 */
@Pay(channelId = 2)
public class CMBBank implements Strategy {

    @Override
    public String calRecharge(Integer channelId) {
        return "我是CMB";
    }

}

修改一下main方法,再次测试

运行结果为:

看到这里是不是觉得很方便,每次新增一个银行的时候,我们不用修改原有代码,只需要新增一个实现类就可以了。

 

特别注意,如果是在Spring框架下使用该架构,那么要特别注意:就是实现类里面使用注解获取的对象是获取不到的,因为它们不会被Spring管理,我们前面都是自己new context()的对象,还有是通过反射new出来的,它们已经脱离了Spring的管理,运行时会出来 Null 异常。

比如在实现类使用了这样的注解

@Resource

@Resource
private ElegantCodeGoodsMapper mapper;

那么这些类的对象是获取不到的,运行时会报 null 异常。

解决方案:

创建一个BeanUtils,将那些类写入到Spring里面去

/**
 * @author: LKP
 * @date: 2019/1/2
 */
@Service
public class BeanUtils implements ApplicationContextAware {

    private static ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        BeanUtils.applicationContext = applicationContext;
    }

    /**
     * 把继承该类的 类成员变量 (通过spring 管理的bean)注入到继承的类中去
     */
    public BeanUtils() {
        //加载继承该类的类,扫描成员变量
        Reflections reflections = new Reflections(this.getClass(), new
                FieldAnnotationsScanner());

        //将所有含有Resource注解的成员变量 扫描出来
        Set<Field> fields = reflections.getFieldsAnnotatedWith(Resource.class);

        //循环遍历成员变量
        for (Field field : fields) {
            //循环的目的就是把这些为null的不被spring管理的注入对象,手动从applicationContext 取出来,
            //然后设置进去。

            try {
                //获得成员变量的类名
                String simpleName = field.getType().getSimpleName();
                //因为我们Spring里面管理bean 的name 都是首字母小写的,所以我们需要把首字母转为小写
                String beanName = toLowerCaseFirstLetter(simpleName);
                //通过beanname 去applicationContext 去获取 beanName 对象
                Object bean = applicationContext.getBean(beanName);
                if (bean == null) {
                    return;
                }
                //我们必须要打破封装
                field.setAccessible(true);
                //把spring管理的对象  设置到我们反射出来的对象中
                field.set(this, bean);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 将首字母变为小写
     * @param beanName
     * @return
     */
    public String toLowerCaseFirstLetter(String beanName){
        String newBeanName = beanName.substring(0, 1).toLowerCase() +
                beanName.substring(1, beanName.length());
        return newBeanName;
    }
}

然后我们的实现类在继承BeanUtils工具类

/**
 * @author: LKP
 * @date: 2019/1/2
 */
@Pay(channelId = 1)
public class ICBCBank extends BeanUtils implements Strategy {

    @Override
    public String calRecharge(Integer channelId) {
        return "我是ICBC";
    }

}

这样就可以将我们的对象写入,并交由Spring进行管理了。

 

 

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值