java 内部类使用姿势

大家好,我是入错行的bug猫。(微博:http://blog.csdn.net/qq_41399429,谢绝转载)

问同事,内(nei)部(bu)类(lei)是什么鬼?
隔壁同事猿一脸懵逼:当然累啊!肿么了?
bug猫:……嗯,没事……我也累……

于是bug猫决定以后放弃问隔壁同事猿了,直接抱住无所不知的度娘大粗腿
(度娘:感谢支持,你还是辞职吧……)


不知道内部类是什么含义的同学请先自行百度~
bug猫只讲怎么高效快速撸代码,原理什么的,bug猫遵循会用就行,以后有兴趣再去了解。

现在用得比较多的框架,有SpringMVC、Spring、Hibernate、Mybatis,比较古老点还有struts2、struts1。
在使用这些框架之前,必须要搞清楚,哪些是多例、哪些是单例。

不知道多例、单例是什么含义的同学请先自行百度~
bug猫知道肯定有不少同学是一脸懵逼。bug猫在面试新同学时,有很多都答不上来。

到这一步,bug猫假设你已经知道单例和多例了。
以SpringMVC为例,SpringMVC默认单例。通过Spring管理的组件,比喻Controller是单例,导致Controller层注入的Service层也是单例!
也就是说如果在Service里面有全局私有属性,会存在线程安全!


好吧,假设你已经知道上述的Service是单例。来一个实际案例分析:

现在有一个特别复杂的业务,比喻订单入库:1个订单主表,n个订单子表,n个日志表。
根据前端传入的参数,有多种判断逻辑,多种业务处理,还有查询系统设置的参数,调上游系统等等。

能力和业务差一点的同学,直接吓趴的那种。

嗯嗯,于是bug猫果断把这块扔给隔壁同事猿做……
最后结果是:Service类中,这个处理业务的方法占了数千行……
没错!就是一个方法数千行代码!

后来业务需求增加,需要从其他入口,也能生成订单。
隔壁同事猿吐血中……
因为那个碉堡了的方法,没法复用……

隔壁同事猿加班最终还是搞定了:
把那个碉堡了的方法,拆成n个方法,每个方法执行特定的功能。
那个碉堡了的方法,变成了只拼装好参数,再调用其他方法的方法。
其他入口生成订单,也是像堆积木一样其他方法拼起来。

问题解决了!隔壁同事猿如是说。
bug猫:隔壁发来贺电!可喜可贺可喜可贺~


哼哼,如果真的完事了,就没这篇博文事了。

老板又来搞事了,需求持续发生变化……

前文说了,Service是单例,不能有全局的私有属性,
隔壁同事猿在n个方法之间,是通过方法上传递的参数耦合在一起:方法dom处理参数ab之后,然后传到方法don中,
伪代码如下:

public void creartOrder(){//创建订单入口
    ...
    dom(a,b,c);    //模块m
    don(a,c,d);    //模块n
    ...
}
private void dom(A a, B b, C c){
    ...
    exm(a,c, x);   //再调用另外一个方法
    ...
}
private void don(A a, C c, D d){
    ...
    exn(a,w); //还调用另外一个方法
    ...
}

private void exm(A a,C c, X x){
    ...
    exn(a, null); //继续调用另外一个方法
    ...
}
private void exn(A a, W w){
    ...
    ...
}

需求发生变化,导致方法上的参数有增减,进一步影响外层方法上的参数,从而整个生成订单的业务代码全崩了!
隔壁同事猿继续吐血中……

老板:这个需求很简单啊,怎么还要那么久?!
隔壁同事猿持续吐血中……

老板:bug猫,你上!
bug猫吐血中……


现在代码改不动,原因在于方法与方法之间有强耦合:参数类型、参数数量、参数顺序
如果把这些很多方法都用到的参数,提到全局上去,
比喻ac,作为全局属性,那么方法就直接可以使用,无需通过参数传递。
或者再创建一个对象P,把abc等参数作为P的属性,方法与方法直间,通过一个对象耦合在一起。方法增减参数,体现为对象P增加属性。

通过权衡各种利弊,bug猫决定综合上述两个方案:
创建一个OrderFactory类,把abc等参数作为OrderFactory的属性,创建订单的方法,全部转移到OrderFactory类中。也就说,OrderFactory是专门用来创建订单的(单一职能)
OrderFactory类中,abc等参数是全局私有属性,创建订单的方法可以直接使用这些属性,不必像丢锅一样,把这些参数丢来丢去。

在主方法中,new 一个OrderFactory的对象,把abc等参数,赋值给OrderFactory的对应属性。然后再执行OrderFactory对外暴露的execute方法,就直接得到结果。OrderFactory对外而言,体现为一个黑匣子,放入原料abc,执行execute就会产出结果。

看到这里,各位可能会说,“我早就知道要这么玩了!浪费我时间!”

等等!少侠请留步!
写订单、操作数据库还没解决呢!


既然OrderFactory是专门用来创建订单的,如果它把订单对象做好,再扔出来,由主方法来写表,
这就不符合bug猫的美学了。
而且,在创建订单对象同时,还要查询数据库,取各种参数。如果这些也有主方法查询出来后,再传给OrderFactory
就像隔壁同事猿把这个锅甩给我一样,主方法说OrderFactory你怎么这么娇贵?什么东西都要帮你准备好,还要我帮你擦屁股,别乱扔锅!!老朽不干了!

好吧,OrderFactory还要兼具操作数据库的功能,需要dao层,怎么破?

“这还不简单,使用@Autowired或者@Resource自动注入!”
如果这样想,我只能说同学,太甜了!

OrderFactory没有被Spring容器管理,而且是使用new创建的,Spring的注解对它无效!
方法一:你必须把它改造一下,把创建OrderFactory的职能委托给Spring管理(略)
方法二:你可以在主方法中,将dao层作为参数传给OrderFactory(略)
方法三:使用内部类

非静态内部类的特性是,可以无条件使用外部的属性、方法。
就算这些属性、方法,是使用private修饰!

回到主方法所在的Servicedao层是可以使用@Autowired或者@Resource自动注入
=>这些dao层都是Service的私有属性
=>如果Service中有一个非静态内部类,是可以直接使用这些dao层!
=>OrderFactory如果是Service中的非静态内部类,可以直接使用dao层!
于是就可以避免在主方法中,将dao层作为参数一个一个传给OrderFactory

“等等,画风变得太快,我没反应过来!”


我们要优雅解决OrderFactory类中,如何获取数据源,操作数据库。
借用非静态内部类,可以无条件使用外部的属性、方法这个特性,
OrderFactory作为Service的一个非静态内部类!
在主方法中,使用OrderFactory这个内部类时,和使用一个普通的类一样,
直接用 new OrderFactory() 创建。
不过这个类有特殊,在Service类的里面!在创建完毕之后,就可以直接使用外部内的属性、方法。
就像A属于B,现在A存在,那么B一定存在。同样,如果内部类对象存在,那么外部类的对象断言可以存在!

假设外部类叫Out,内部类叫In,那么我们可以使用Out.In in = new Out().new In()来实例化内部类的对象
OrderFactory内部类里面,就可以直接使用,Service中自动注入的dao层了。

再来看一下线程问题,
Service是单例,导致dao层也是单例。
在主方法中,使用 new创建了OrderFactory对象,那么OrderFactory就是多例,多例是容许有全局私有属性(类似struts2的action中,有各种全局属性),没有线程问题。

伪代码:

@Autowired
private Dao dao;

public void creartOrder(){//创建订单入口
    OrderFactory ofy = new OrderFactory(a,b,c);
    ofy.execute();
}

private class OrderFactory{
    private A a;
    private B b;
    private C c;

    public OrderFactory(A a,B b,C c){
        this.a = a;
        this.b = b;
        this.c = c;
    }

    public void execute(){
        ...
        dom();    //模块m
        don(d);    //模块n
        ...
    }

    private void dom(){
        ...
        exm(x);   //再调用另外一个方法
        ...
    }
    private void don(D d){
        ...
        exn(w); //再调用另外一个方法
        ...
    }

    private void exm(X x){
        ...
        exn(null); //继续调用另外一个方法
        ...
    }
    private void exn(W w){
        ...
        dao.insert(a);
        ...
    }
}


最后总结一下,非静态内部类非常使用于,像Service这种单例中,又需要创建多例对象、又需要共享一些单例资源的场景。


bug猫又一次漂亮地解决完问题~鼓掌~



~THE END~

隔壁同事猿因为被不明人士袭击,重伤请假中…… (づ。◕‿‿◕。)づ





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值