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

从一个例子的重构过程到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







// 输出如下
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




 
   0.3
 


 
   0.2
 


 
   0.1
 


 
   0.05
 

输出:
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的运用,某个效果的实现只是旁门左道,对于一个有志于站在时代风口浪尖的程序员应该在大道上多花功夫.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值