继往开来 吐故纳新
日历
网志分类
· 所有网志 (990)
· 个人作品 (62)
· 软件设计 (33)
· 面向对象编程 (22)
· JavaAPI (39)
· Java开源工具 (31)
· Swing (34)
· Java语法细节 (39)
· 样式表CSS (12)
· XML (10)
· J2EE(JavaEE) (23)
· 算法数据结构 (64)
· 正则表达式 (4)
· 软件知识 (6)
· Java线程 (9)
· Web开发.Jsp/Servlet/Struts (20)
· 程序随想录 (7)
· Spring (5)
· Hibernate (7)
· J2SE 高级 (2)
· J2SE 高级 (0)
· Web开发.Ajax (16)
· Web开发.JavaScript (43)
· DB4O (2)
· Web开发.CSS/Html (22)
· C# (20)
· ERP (4)
· JDBC (1)
· 编程资源 (16)
· 编程感悟 (29)
· DB/Sql (13)
· VB (29)
· VC (2)
· 桌面脚本 (3)
· 新兴软件 (3)
· 英语学习 (21)
· 网文转载 (159)
· 职场风云 (39)
· 诗词歌赋 (32)
· 生活感言 (77)
· 奇文共赏 (13)
· 财经纵横 (6)
· 未分类 (11)
站内搜索
友情链接
· 歪酷博客
· 我的歪酷 非非共享界
· 偶要雷锋
· 豆瓣
· nczonline
· 当当网
· easyjf中文站
· Donews
· 天极Java文章列表
· W3CSchool
· taiten的BLOG
· Dojo中国
· Dojo
· Extjs.com
· Lifehack中文网志
· JaveEye的一个AS专题
· Banq's JDon
· Java 中文网址大全
· 梦想Java
· 360Doc个人图书馆
· java开源大全
· 我在硅谷动力的软件下载站
· 站长中国
· 随意贴
· CSS教学素材站
· java 参考中文站
· 面向构件与SOA社区
· 彩字生成
· 派派小说论坛
· 如坐春风
· 英语学习网
· BBC CHina
· www.dlbang.com
· 古文竖排格式在线转化工具
· 免费家谱
· 图片上传基地
· 风景壁纸
· 和风细雨
· MyC#BlogInCsdn

订阅 RSS

0207489

歪酷博客

开此博一为经验积累,二为资料收集,三为同道交流,四为资源共享.
« 上一篇: [Java]对乱序串进行数量整理 下一篇: 赌神出场经典音乐 »
Junglesong @ 2006-12-11 16:15

从一个例子的重构过程到Spring的IoC之使用

IoC (反向控制)是Sping的核心,用以实现系统对象的松耦合,使用IoC ,对象是被动接收依赖类而不是主动去找,下面将结合一个订单,折扣,价格的例子来说明IoC的优势.看看它是怎么降低系统对象的耦合的.

1.关于订单,折扣,价格的说明
  订单是货物的订单,主要货物有计算机,文具,纸张三类,还有其它一些物件.每种货物都有不同的折扣比例,从单价中扣除折扣比例后就是进货时的真实价格.
  下面将使用各种例子实现这一过程.


2.传统的翻译式的处理:

这样的代码新手常些,老手在快速编程中也难以避免,认识到这段代码是否需要重构,如何进行重构是二者的最大区别.

// 订单类
public class Order{
 public static final String Type_Computer="Computer";
 public static final String Type_Stationary="Stationary";
 public static final String Type_Paper="Paper"; 
 private String type;
 
 private float price; 
 
 public Order(float price,String type){
  this.price=price;
  this.type=type;
 }
 
 public Order(){
  this(100.0f,"");
 }

 public float getPrice() {
  float discount=0.0f;
  
                // 真实价格的计算直接写在了订单类的方法中
  if(Type_Computer.equals(type)){
   discount=0.3f;
  }
  else if(Type_Stationary.equals(type)){
   discount=0.2f;
  }
  else if(Type_Paper.equals(type)){
   discount=0.1f;
  }
  
  
  return price*(1.0f-discount);
 } 
}

// 运行
Order computerOrder=new Order(100.0f,Order.Type_Computer);
System.out.println("computerOrder's price is \t"+computerOrder.getPrice());
  
Order stationaryOrder=new Order(100.0f,Order.Type_Stationary);
System.out.println("stationaryOrder's price is \t"+stationaryOrder.getPrice());
  
Order paperOrder=new Order(100.0f,Order.Type_Paper);
System.out.println("paperOrder's price is \t\t"+paperOrder.getPrice());
  
Order otherOrder=new Order();
System.out.println("otherOrder's price is \t\t"+otherOrder.getPrice());

// 输出情况
computerOrder's price is  70.0
stationaryOrder's price is  80.0
paperOrder's price is   90.0
otherOrder's price is   100.0

优势:直白易懂,基本就是把用户的语言描述改写成代码,适用于小规模无变化的项目.
劣势:计算价格的代码属于硬编码,如果规模变大,则判断将会变得冗长难懂,如果产生变化,如增加新的类型的货物,货物折扣发生变化等,将不容易增加新的功能,修改时将容易相互影响.

3.使用简单类工厂进行重构解耦的方案(工厂模式)
// 折扣类
public interface Discount{
 public float getDiscount();
}

// 计算机货物的折扣类
public class ComputerDiscount implements Discount{
 public float getDiscount(){
  return 0.3f;
 }
}

// 文具折扣类
public class StationaryDiscount implements Discount{
 public float getDiscount(){
  return 0.2f;
 }
}

// 纸张折扣类
public class PaperDiscount implements Discount{
 public float getDiscount(){
  return 0.1f;
 }
}

// 其它货物折扣类
public class OtherDiscount implements Discount{
 public float getDiscount(){
  return 0.0f;
 }
}

// 用于计算折扣的工厂
public class DiscountFactory{
 public static float getDiscount(String type){
  Discount discount=null;  
  
  // 转移过来的判断过程
  if(Order.Type_Computer.equals(type)){
   discount=new ComputerDiscount();
  }
  else if(Order.Type_Stationary.equals(type)){
   discount=new StationaryDiscount();
  }
  else if(Order.Type_Paper.equals(type)){
   discount=new PaperDiscount();
  } 
  else{
   discount=new OtherDiscount();
  }
  
  return discount.getDiscount();
 }
}

// 订单类
public class Order{
 public static final String Type_Computer="Computer";
 public static final String Type_Stationary="Stationary";
 public static final String Type_Paper="Paper"; 
 private String type;
 
 private float price; 
 
 public Order(float price,String type){
  this.price=price;
  this.type=type;
 }
 
 public Order(){
  this(100.0f,"");
 }

 public float getPrice() {
  float discount=DiscountFactory.getDiscount(this.type);  
  
  return price*(1.0f-discount);
 } 
}

输出的结果还是:
computerOrder's price is  70.0
stationaryOrder's price is  80.0
paperOrder's price is   90.0
otherOrder's price is   100.0

优势:归纳出了折扣接口及其四个子类,细分程度比上个方案高,修改折扣时不容易相互影响.
劣势:订单类Order还是和DiscountFactory类耦合在一起,使用IF进行判断的硬编码依然存在,增加新的货物折扣类时还是容易相互影响.

4.利用反射进行解耦的方案(命令模式)
// 折扣类
public interface Discount{
 public float getDiscount();
}

// 计算机货物的折扣类
public class ComputerDiscount implements Discount{
 public float getDiscount(){
  return 0.3f;
 }
}

// 文具折扣类
public class StationaryDiscount implements Discount{
 public float getDiscount(){
  return 0.2f;
 }
}

// 纸张折扣类
public class PaperDiscount implements Discount{
 public float getDiscount(){
  return 0.1f;
 }
}

// 其它货物折扣类
public class OtherDiscount implements Discount{
 public float getDiscount(){
  return 0.0f;
 }
}

// 订单类
public class Order{
 public static final String Type_Computer="Computer";
 public static final String Type_Stationary="Stationary";
 public static final String Type_Paper="Paper"; 
 private String type;
 
 private float price; 
 
 public Order(float price,String type){
  this.price=price;
  this.type=type;
 }
 
 public Order(){
  this(100.0f,"Other");
 }

 public float getPrice() {
  Discount discount = null;
  String className="discount."+type+"Discount";
  
  try{
              Class cls=Class.forName(className);
              discount=(Discount)cls.newInstance();// 由类得到类实例
      }
      catch(Exception ex){
          ex.printStackTrace();
      }      
    
  return price*(1.0f-discount.getDiscount());
 } 
}

// 输出如下:
computerOrder's price is  70.0
stationaryOrder's price is  80.0
paperOrder's price is   90.0
otherOrder's price is   100.0

优势:使用反射替代了IF判断,这有效消除了增加新货物折扣的相互影响.
劣势:如果要修改折扣大小,必须由程序员来修改类文件,这是不适应经常变化的,变化还会带来一系列的测试,编译,发布等事情.

到这里,如果不使用IoC,重构就基本结束了,相对而言,用反射方式较好些.
但是使用了IoC,我们将有更佳的选择.

5.使用Spring的Ioc进行解耦的方案

// 折扣类
public interface Discount{
 public float getDiscount();
}

// 计算机货物的折扣类
public class ComputerDiscount implements Discount{
 public float getDiscount(){
  return 0.3f;
 }
}

// 文具折扣类
public class StationaryDiscount implements Discount{
 public float getDiscount(){
  return 0.2f;
 }
}

// 纸张折扣类
public class PaperDiscount implements Discount{
 public float getDiscount(){
  return 0.1f;
 }
}

// 其它货物折扣类
public class OtherDiscount implements Discount{
 public float getDiscount(){
  return 0.0f;
 }
}

// Order类
public class Order{
 public static final String Type_Computer="Computer";
 public static final String Type_Stationary="Stationary";
 public static final String Type_Paper="Paper"; 
 private String type;
 
 private float price; 
 
 public Order(float price,String type){
  this.price=price;
  this.type=type;
 }
 
 public Order(){
  this(100.0f,"Other");
 }

 public float getPrice() {
  ApplicationContext ctx = new FileSystemXmlApplicationContext("bean.xml");
  Discount discount = null;
  discount = (Discount) ctx.getBean(this.type);
    
  return price*(1.0f-discount.getDiscount());
 } 
}

// 配置文件Bean.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
 <bean id="Computer" class="discount.ComputerDiscount"/>
 <bean id="Stationary" class="discount.StationaryDiscount"/>
 <bean id="Paper" class="discount.PaperDiscount"/>
 <bean id="Other" class="discount.OtherDiscount"/>
</beans>

// 输出如下
computerOrder's price is  70.0
stationaryOrder's price is  80.0
paperOrder's price is   90.0
otherOrder's price is   100.0

优势:使用Spring的IoC代替了反射,易懂而不容易出错.
劣势:和使用反射的情况一致.

6.进一步使用Ioc进行解耦的方案
// 折扣接口
public interface Discount{
 public float getDiscount();
 public void  setDiscount(float discount);
}

// 计算机折扣类
public class ComputerDiscount implements Discount{
 private float discount=0.0f;
 
 public float getDiscount(){
  return discount;
 }
 
 public void setDiscount(float discount){
  this.discount=discount;
 }
}

// 文具折扣类
public class StationaryDiscount implements Discount{
 private float discount=0.0f;
 
 public float getDiscount(){
  return discount;
 }
 
 public void setDiscount(float discount){
  this.discount=discount;
 }
}

// 纸张折扣类
public class PaperDiscount implements Discount{
 private float discount=0.0f;
 
 public float getDiscount(){
  return discount;
 }
 
 public void setDiscount(float discount){
  this.discount=discount;
 }
}

// 其它折扣类
public class OtherDiscount implements Discount{
 private float discount=0.0f;
 
 public float getDiscount(){
  return discount;
 }
 
 public void setDiscount(float discount){
  this.discount=discount;
 }
}

// Order类
public class Order{
 public static final String Type_Computer="Computer";
 public static final String Type_Stationary="Stationary";
 public static final String Type_Paper="Paper"; 
 private String type;
 
 private float price; 
 
 public Order(float price,String type){
  this.price=price;
  this.type=type;
 }
 
 public Order(){
  this(100.0f,"Other");
 }

 public float getPrice() {
  ApplicationContext ctx = new FileSystemXmlApplicationContext("bean.xml");
  Discount discount = null;
  discount = (Discount) ctx.getBean(this.type);
    
  return price*(1.0f-discount.getDiscount());
 } 
}

// 配置文件bean.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
 <bean id="Computer" class="discount.ComputerDiscount">
  <property name="discount">
   <value>0.3</value>
  </property>
 </bean>
 <bean id="Stationary" class="discount.StationaryDiscount">
  <property name="discount">
   <value>0.2</value>
  </property>
 </bean>
 <bean id="Paper" class="discount.PaperDiscount">
  <property name="discount">
   <value>0.1</value>
  </property>
 </bean>
 <bean id="Other" class="discount.OtherDiscount">
  <property name="discount">
   <value>0.05</value>
  </property>
 </bean>
</beans>

输出:
computerOrder's price is  70.0
stationaryOrder's price is  80.0
paperOrder's price is   90.0
otherOrder's price is   95.0

优势:对于折扣数额变化不需要修改类文件,也没有修改类文件的带来的系列问题.
劣势:增加新类时仍需配置xml文件,没有达到完全的即插即用.这也许是我没有学到,也许Spring也不能解决,还有一段路要走.

IoC的根在于反射,其它使用XMl文件进行配置的框架如Struts,Hibernate也在反射上做了很多文章,可以说不充分理解和运用反射就难以把握现代框架,永远是一个使用者.
可以说反射,面向接口等是java界的大道,某个API的运用,某个效果的实现只是旁门左道,对于一个有志于站在时代风口浪尖的程序员应该在大道上多花功夫.

 

 





评论 / 个人网页 / 扔小纸条
* 昵称

已经注册过? 请登录

新用户请先注册 以便能显示头像及追踪评论回复

Email
网址
* 评论
表情
 


 

分类小组论坛
杂谈 , 娱乐、八卦 , 文学、艺术 , 体育 , 旅游、同城 , 象牙塔 , 情感 , 时尚、生活 , 星座 , 科技

请注意遵守中华人民共和国法律法规, 如威胁到本站生存, 将依法向有关部门报告, 同时本站的相关记录可能成为对您不利的证据.

相关法律法规
全国人大常委会关于维护互联网安全的决定
中华人民共和国计算机信息系统安全保护条例
中华人民共和国计算机信息网络国际联网管理暂行规定
计算机信息网络国际联网安全保护管理办法
计算机信息系统国际联网保密管理规定