大家好,我是入错行的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
处理参数a、b之后,然后传到方法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猫吐血中……
现在代码改不动,原因在于方法与方法之间有强耦合:参数类型、参数数量、参数顺序。
如果把这些很多方法都用到的参数,提到全局上去,
比喻a、c,作为全局属性,那么方法就直接可以使用,无需通过参数传递。
或者再创建一个对象P,把a、b、c等参数作为P的属性,方法与方法直间,通过一个对象耦合在一起。方法增减参数,体现为对象P增加属性。
通过权衡各种利弊,bug猫决定综合上述两个方案:
创建一个OrderFactory类,把a、b、c等参数作为OrderFactory的属性,创建订单的方法,全部转移到OrderFactory类中。也就说,OrderFactory是专门用来创建订单的(单一职能)。
在OrderFactory类中,a、b、c等参数是全局私有属性,创建订单的方法可以直接使用这些属性,不必像丢锅一样,把这些参数丢来丢去。
在主方法中,new 一个OrderFactory的对象,把a、b、c等参数,赋值给OrderFactory的对应属性。然后再执行OrderFactory对外暴露的execute方法,就直接得到结果。OrderFactory对外而言,体现为一个黑匣子,放入原料a、b、c,执行execute就会产出结果。
看到这里,各位可能会说,“我早就知道要这么玩了!浪费我时间!”
等等!少侠请留步!
写订单、操作数据库还没解决呢!
既然OrderFactory是专门用来创建订单的,如果它把订单对象做好,再扔出来,由主方法来写表,
这就不符合bug猫的美学了。
而且,在创建订单对象同时,还要查询数据库,取各种参数。如果这些也有主方法查询出来后,再传给OrderFactory,
就像隔壁同事猿把这个锅甩给我一样,主方法说OrderFactory你怎么这么娇贵?什么东西都要帮你准备好,还要我帮你擦屁股,别乱扔锅!!老朽不干了!
好吧,OrderFactory还要兼具操作数据库的功能,需要dao层,怎么破?
“这还不简单,使用@Autowired
或者@Resource
自动注入!”
如果这样想,我只能说同学,太甜了!
OrderFactory没有被Spring容器管理,而且是使用new创建的,Spring的注解对它无效!
方法一:你必须把它改造一下,把创建OrderFactory的职能委托给Spring管理(略)
方法二:你可以在主方法中,将dao
层作为参数传给OrderFactory(略)
方法三:使用内部类
非静态内部类的特性是,可以无条件使用外部的属性、方法。
就算这些属性、方法,是使用private
修饰!
回到主方法所在的Service
,dao
层是可以使用@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~
隔壁同事猿因为被不明人士袭击,重伤请假中…… (づ。◕‿‿◕。)づ