1. 前言
我相信很多在刚刚使用Spring的同事会考虑一个问题:
我们为什么要用Spring,Spring虽然给我们带来了一些好处,可是,似乎好处似乎不是那么明显。IOC的作用似乎也很牵强。
所以,冷凝沙漠在此记录了自己的一些Spring开发经验,与各位分享。
2. 一个常见的业务场景
2.1. 场景简介
一个非常常见的业务场景是:程序中会根据某一个特殊的参数,定义一系列不同的执行方式。
流程如下图所示:
2.2. 代码示例
形成代码之后大概会这么写:
publicclass Example1 { publicstaticvoid main(String[] args) { String specialParam = args[0];// 这是一个特定的参数
Object[] param1 = new Object[] {}; Object[] param2 = new Object[] {}; Object[] param3 = new Object[] {}; // ………… 以上是一堆常见、有规则的其他参数
if ("CanShu1".equals(specialParam)) {
// ………… 一堆冗长的程序
} elseif ("CanShu2".equals(specialParam)) { // ………… 又一堆冗长的程序
} elseif ("CanShu3".equals(specialParam)) {
// ………… 再一堆冗长的程序
} else {
// ………… 其他默认的冗长的程序 } } } |
2.3. 优缺点分析
从这个示例的角度,代码挺清晰的,但是到了实际应用中,可能就会有数十套逻辑,可能会有长达数千行代码,可能会绕晕很多人。
3. 一个常见的不好的修改方式
3.1. 场景简介与代码示例
如下是很常见的一个修改方式,将不同的流程提炼出独立的方法,让逻辑判断的位置独立出来。
publicclass Example2 {
publicstaticvoid main(String[] args) { String specialParam = args[0];// 这是一个特定的参数
Object[] param1 = new Object[] {}; Object[] param2 = new Object[] {}; Object[] param3 = new Object[] {}; // ………… 以上是一堆常见、有规则的其他参数
// 提炼独立方法 if ("CanShu1".equals(specialParam)) { handleCanShu1(param1, param2, param3); } elseif ("CanShu2".equals(specialParam)) { handleCanShu2(param1, param2, param3); } elseif ("CanShu3".equals(specialParam)) { handleCanShu3(param1, param2, param3); } else { handleDefault(param1, param2, param3); } }
publicstaticvoid handleCanShu1(Object[] param1, Object[] param2, Object[] param3) { // ………… 一堆冗长的程序 }
publicstaticvoid handleCanShu2(Object[] param1, Object[] param2, Object[] param3) { // ………… 又一堆冗长的程序 }
publicstaticvoid handleCanShu3(Object[] param1, Object[] param2, Object[] param3) { // ………… 再一堆冗长的程序 }
publicstaticvoid handleDefault(Object[] param1, Object[] param2, Object[] param3) { // ………… 其他默认的冗长的程序 } } |
3.2. 优缺点分析
相信各位维护的旧项目里面不会缺乏此类代码。
从维护的角度来看:
1. 如果要继续更新这个代码,也只能不断的在这个类上下功夫。很可能这个类未来会变成一个“只有上帝知道它是干嘛的”的复杂类。
2. 必须要有源码才能更新代码,才能重新编译。相信我,很多维护方没有源代码,这种修改对于程序员而言属于“Mission Impossible”。当然,对于搞IT的,天天玩程序员版的碟中谍,也是家常便饭。
因此,大部分Java培训师、Team Leader、代码走查员都会说:这样不好,这样不好!
4. 中规中矩的工厂模式
4.1. 场景简介
从模式的角度来看,工厂模式自然是这里最适合的模式:
咱们先提炼一个公共的接口,定义一个调用的工厂,再不断实现这个接口,并将接口注册到工厂。
未来如果要增加一种业务,就单独增加一个实现类,并注册到工厂就行了。
4.2. 代码示例
以下是一套简单的实现代码:
publicclass Example3 { publicstaticvoid main(String[] args) { String specialParam = args[0];// 这是一个特定的参数
Object[] param1 = new Object[] {}; Object[] param2 = new Object[] {}; Object[] param3 = new Object[] {}; // ………… 以上是一堆常见、有规则的其他参数 Example3Service service = Example3Factory.getService(specialParam); service.run(param1, param2, param3);
} } |
调用其实是很简单的。
package com.test; /** * 提炼出工厂和接口 */ publicclass Example3Factory { publicstatic Example3Service getService(String specialParam) { if ("CanShu1".equals(specialParam)) { returnnew Example3ServiceCanShu1Impl(); } elseif ("CanShu2".equals(specialParam)) { returnnew Example3ServiceCanShu2Impl(); } elseif ("CanShu3".equals(specialParam)) { returnnew Example3ServiceCanShu3Impl(); } else { returnnew Example3ServiceDefaultImpl(); } } } |
工厂类可以简单的如例子中写死,也可以写成collection形式,然后进行注册。
package com.test; /** * 提炼出工厂和接口 */ publicinterface Example3Service { publicvoid run(Object[] param1, Object[] param2, Object[] param3); }
|
接口神马的最简单了,以下就是接口实现:
package com.test;
publicclass Example3ServiceCanShu1Impl implements Example3Service { publicvoid run(Object[] param1, Object[] param2, Object[] param3) { // ………… 一堆冗长的程序 } } |
package com.test;
publicclass Example3ServiceCanShu2Impl implements Example3Service { publicvoid run(Object[] param1, Object[] param2, Object[] param3) { // ………… 又一堆冗长的程序 } } |
package com.test;
publicclass Example3ServiceCanShu3Impl implements Example3Service { publicvoid run(Object[] param1, Object[] param2, Object[] param3) { // ………… 再一堆冗长的程序 } } |
package com.test;
publicclass Example3ServiceDefaultImpl implements Example3Service { publicvoid run(Object[] param1, Object[] param2, Object[] param3) { // ………… 其他默认的冗长的程序 } } |
4.3. 优点分析
形成工厂模式的优点很明显:
l 提供创建对象的接口. 为系统结构提供了非常灵活强大的动态扩展机制,只要我们更换一下具体的工厂方法,系统其他地方无需一点变换,就有可能将系统功能进行改头换面的变化。
l 除了工厂类以外的程序都不需要去了解具体的实现情况,所以给程序实现带来了很多方便。
l 将程序的一部分复杂度集中在接口的实现上,一部分程序员可以专心于如何通过实现接口来实现业务逻辑,另一部分程序员可以专心于通过更新工厂注册方式来将新的实现对接到整个程序中。
4.4. 缺点分析
形成工厂模式的缺点也很明显:
l 工厂类依旧需要去了解具体的实现类以及其参数,当程序复杂度到一定程度时,工厂类依旧可能很复杂。
l 还是必须要有源码才能更新工厂类的代码,才能重新编译。
5. 开始引入Spring
5.1. 场景简介
既然工厂模式的缺点集中在工厂上,那么就优化工厂好了。我们可以把工厂优化为抽象厂,也可拆掉这个工厂,用Spring来替代这个工厂。
可以这样想,工厂类就是提供了一个Map,根据一个特定的key值,找一个特定的Bean。如果仅仅是这样,Spring自身是不是就能作为这个工厂了?
5.2. 代码示例
调用方的代码依旧很简单:
package com.test; import java.util.Map; import javax.annotation.Resource; publicclass Example4 { @Resource private Map example4ServiceMap; @Resource private Example4Service example4ServiceDefaultImpl;
publicvoid runMain(String specialParam) { Object[] param1 = new Object[] {}; Object[] param2 = new Object[] {}; Object[] param3 = new Object[] {}; // ………… 以上是一堆常见、有规则的其他参数
Example4Service service = example4ServiceMap.get(specialParam); if (service == null) { service = example4ServiceDefaultImpl; } service.run(param1, param2, param3); } } |
工厂的内容直接用Spring配置完成:
|
|
返回顶楼 | |
发表时间:2013-03-26
不知为什么,编辑几次都显示不完整,到我的博客看吧:
Spring与工厂模式 |
|
返回顶楼 | |
发表时间:2013-03-28
这个写得太好,思路太正确,帮助好大。
|
|
返回顶楼 | |
发表时间:2013-04-02
按照你的业务需求,个人认为最合适的是使用command模式!
|
|
返回顶楼 | |