不管在什么软件公司,不管做什么软件业务,当产品不断迭代业务不断变更后,咱们的程序代码也会越来越臃肿越来越冗余。

在一个维护了几年的系统中,有的时候我们会发现一个方法几百行,甚至上千行的都有,方法里面嵌套了数不清的if else,不可思议,无法理解。看到这样的方法,特别是前人都没有留啥注释,真的很蛋疼。

所以在这种情况下,咱们就想到了用设计模式,将一个方法拆分成n个方法,每个方法负责不同的功能模块,然后将所有的方法组合在一起形成一个主要的大功能模块。这也是封装思想的理想模式。

废话不多说咱们先看一个例子:

public Integer  saveOrder(OrderVar var){

     //校验参数
     if(var == null){
         throw new RuntimeException("*******");
     }
     if(var.getMoney() == null || var.getMoney() <= 0){
         throw new RuntimeException("*******");
     }
     if(var.getUserId() == null){
         throw new RuntimeException();
     }
     ……………… //此处省略300行代码
 
     //保存订单
     Order order = new Order();
     order.setOrderId(orderId)
          .setMoney(money)
          .setObject(obj)
          .setStatus(4)
          .setOrderType(5)
          .setCreateAt(new Date);
     orderService.save(order);

     OrderExt orderExt = new OrderExt();
     orderExt.setOrderId(orderId)
          .setUserId(userId)
          .setSellBl(bll)
          .setStatus(4)
          .setType(3)
          .setlocal(local)
          .setbusiness(business)
          .setPerll(perll)
          .setCreateAt(new Date);
     orderExtService.save(orderExt);
}

随便写了一点代码,咱们就姑且把这个方法当成一个无比简化版下单的方法吧,这里简略写下。

       一般情况下这种类型的方法拆分起来很简单,只需要将这三个功能拆分成三个方法,然后在一个方法中调用即可。但是实际上相关的业务却比这复杂的多,下单的业务涉及到用户,账户,各种卷,商家,物流,银行,日志,第三方等等等等,如果将一个下单的全部代码放到一个方法中,估计得要有几万行,即使是spring这种service分类分功能的,也会有各种逻辑业务嵌套,无法避免的一个方法也会出现成百上千行,而且spring的service方法之间调用需要不断的传递参数,偶尔一个参数没有传递,或者没有返回就会导致业务执行不下去,然后咱们有要改方法,改了这个方法,其他n个调用的方法又要改,心都碎了……

       那么怎么办呢,很自然的咱们想到了设计模式,工厂模式,命令模式,责任链模式等等等等。

      一个方法很长,那么我们是不是可以将这个方法封装成一个类,这个类完全就用来实现这个业务,不做其他任何事情。

      就拿上面的下单为例:

如何去查看一个spring cloud项目_封装

咱们调用的时候就可以直接  new Create

如何去查看一个spring cloud项目_设计模式_02

        这样咱们的下单逻辑就完全集中到CreateOrder中了,而且CreateOrder因为是new出来的新的对象,所以这个类中咱们可以随便定义全局变量,这样的话咱们的方法之间就不用为参数犯愁了,反正参数都在CreateOrder对象中哈哈,简直完美。

        不过凡是都不会这么顺利,问题来了!

如何去查看一个spring cloud项目_设计模式_03

哈哈,这个对象是咱们自己new出来的,又不是spring管理的,当然就不能注入对象喽,可是不能注入对象,咱们怎么办呢,这不是坑吗!想了一下,注入不了对象,咱们可以在spring容器中查啊!

不废话继续

如何去查看一个spring cloud项目_设计模式_04

嗯,这样就可以了,非常nice啊,不过哥是个追求完美的人,咱们不能每做一个业务操作,都在spring中获取service或者dao吧,这太尴尬了,完全不能接受嘛!

那么怎么办呢?

对了!spring是肯定不会给我们注入的,但是难道咱们自己不能注入吗,脑袋浆糊啦

接下来才是见证奇迹的时刻哈哈

@Slf4j
public abstract class AbstractCmd<T>{

    /**
     * 获取当前命令对象的所有带 @Autowired注解的属性,然后从spring上下文中获取到注入进去
     */
    public AbstractCmd(Object params){
        String cmdName = this.getClass().getSimpleName();
        log.info("开始执行命令:"+cmdName);
        if(log.isDebugEnabled()){
            log.debug("执行命令["+cmdName+"]参数:"+JsonHelper.toJson(params));
        }
        List<Field> fields = getFields(getClass(),new ArrayList<>());
        if(!ObjectUtils.isEmpty(fields)){
            for(Field field : fields){
                //spring bean
                Autowired autowired = field.getAnnotation(Autowired.class);
                if(autowired != null){
                    Class<?> bean = field.getType();
                    Object springBean = SpringApplicationContext.getBean(bean);
                    Assert.isTrue(!ObjectUtils.isEmpty(springBean),"bean: "+bean.getName()+" not found in spring application!");
                    try {
                        field.setAccessible(true);
                        field.set(this,springBean);
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                        throw new RuntimeException(e.getMessage(),e);
                    }
                }
                //dubbo
                Reference reference = field.getAnnotation(Reference.class);
                if(reference != null){
                    Class<?> type = field.getType();
                    AnnotationInjectedBeanPostProcessor postProcessor = SpringContextUtils.getBean(AnnotationInjectedBeanPostProcessor.class);
                    List<Method> methods = getMethods(postProcessor.getClass(),new ArrayList<>());
                    for(Method method : methods){
                        if(method.getName().equals("doGetInjectedBean")){
                            method.setAccessible(true);
                            try {
                                DubboElement element = new DubboElement(null,null);
                                Object bean = method.invoke(postProcessor,reference,null,null,type,element);
                                field.setAccessible(true);
                                field.set(this,bean);
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                            break;
                        }
                    }
                }
            }
        }
    }


    private List<Field> getFields(Class copy, List<Field> fields){
        fields.addAll(Arrays.asList(copy.getDeclaredFields()));
        if(copy.getSuperclass() != Object.class){
            Class superCopy = copy.getSuperclass();
            getFields(superCopy,fields);
            return fields;
        }else{
            return fields;
        }
    }


    private List<Method> getMethods(Class bean,List<Method> methods){
        methods.addAll(Arrays.asList(bean.getDeclaredMethods()));
        if(bean.getSuperclass() != Object.class){
            Class superCopy = bean.getSuperclass();
            getMethods(superCopy,methods);
            return methods;
        }else{
            return methods;
        }
    }


    protected  class DubboElement extends InjectionMetadata.InjectedElement{


        protected DubboElement(Member member, PropertyDescriptor pd) {
            super(member, pd);
        }
    }


    public abstract T  execute();
}

     咱们先做一个抽象的注入父类,这个父类没啥特殊的,就是通过反射来获取spring中的bean,然后自己手动塞到被Autowired注解注释的属性中,简直lou的不要不要的,然后在创建一个CreateOrder子类,来继承这个父类,额就是这么简单

public class CreateOrder extends AbstractCmd<Integer>{

    private OrderVar var;

    @Autowired
    private OrderService orderService;
   
    public CreateOrder(OrderVar var){
       super(var);
       this.var = var;
    }
      

    @Override
    public Integer execute() {
        orderService.save();
        
        return 1;
    }
}

 

如何去查看一个spring cloud项目_List_05

哈哈,结束啦