Chinaunix首页 | 论坛 | 博客
  • 博客访问: 3252490
  • 博文数量: 530
  • 博客积分: 13360
  • 博客等级: 上将
  • 技术积分: 5473
  • 用 户 组: 普通用户
  • 注册时间: 2006-07-13 13:32
文章分类

全部博文(530)

文章存档

2017年(1)

2015年(2)

2013年(24)

2012年(20)

2011年(97)

2010年(240)

2009年(117)

2008年(12)

2007年(8)

2006年(9)

分类:

2009-11-17 12:06:50

为什么要用IOC,详见

1.通常编程与测试方法

   一个客户对象依赖于一个服务接口。该服务接口可以提供任何服务,我们把它称为Service。
   public interface IService {
      void go();
   }
   public class ServiceImpl implements IService {
      public void go() {
         ...
      }
   }

单元测试的伪服务对象。
   public class MockService implements IService {
      private boolean gone = false;

      public void go() {
         gone = true;
      }
      public boolean isGone() {
         return gone;
      }
   }

   使用IOC之前,最常用的是工厂模式。除了服务接口之外,你还有一个既可以向客户提供服务对象,也可以向测试程序传递伪服务对象的工厂类。在这里我们会将服务实现为一个单件对象,以便让示例尽量简化。
   public class ServiceFactory {
      private ServiceFactory() {}
      private static Service instance = new ServiceImpl();

      public static Service getInstance() {
          return instance;
      }

      public static void setInstance(Service service) {
          instance = service;
      }
   }

   客户程序每次需要服务对象时就直接从工厂获取。
   public class Client {
      public void go() {
         Service service = ServiceFactory.getInstance();
         service.go();
      }
   }

   客户程序的单元测试代码必须将一个伪服务对象传入工厂,同时要记得在测试后清理。在我们这个简单的例子里,这不算什么难事儿。但当你增加了越来越多的客户和服务代码后,所有这些伪代码和清理代码会让单元测试的开发一团糟。此外,如果你忘记在测试后清理,其他测试可能会得到与预期不符的结果。更糟的是,测试的成功与失败可能取决于他们被执行的顺序。

public void testClient() {
   Service previous = ServiceFactory.getInstance();
   try {
      final MockService mock = new MockService();
      ServiceFactory.setInstance(mock);
      Client client = new Client();
      client.go();
      assertTrue(mock.isGone());
   }
   finally {
      ServiceFactory.setInstance(previous);
   }
}
   工厂的API把我们限制在了单件这一种应用模式上。即便 getInstance() 可以返回多个实例, setInstance() 也会束缚我们的手脚。转换到非单件模式也意味着转换到了一套更复杂的API。

2.手工依赖注入
   依赖注入模式的目标之一是使单元测试更简单。我们不需要特殊的框架就可以实践依赖注入模式。依靠手工编写代码,你可以得到该模式大约80%的好处。
   当上例中的客户代码向工厂对象请求一个服务时,根据依赖注入模式,客户代码希望它所依赖的对象实例可以被传入自己。也就是说:不要调用我,我会调用你。
public class Client {
   private final Service service;

   public Client(Service service) {
      this.service = service;
   }
   public void go() {
      service.go();
   }
}
   这让我们的单元测试简化了不少。我们可以只传入一个伪服务对象,在结束后也不需要多做什么。

public void testClient() {
   MockService mock = new MockService();
   Client client = new Client(mock);
   client.go();
   assertTrue(mock.isGone());
}
  
3.用Guice实现依赖注入
   文件名                说明
HelloGuice.java         业务逻辑 接口 定义文件
HelloGuiceImpl.java     业务逻辑 接口实现 文件
HelloGuiceModule.java   该文件必须 实现com.google.inject.Module接口
TestGuice.java          测试文件

HelloGuice.java
    package com.test.guice;  
      //接口HelloGuice
    public interface HelloGuice {  
        public void sayHello();  
    } 

HelloGuiceImpl.java
   package com.test.guice;  
    //接口的实现类HelloGuiceImpl
    public class HelloGuiceImpl implements HelloGuice {  
        public void sayHello() {  
             System.out.println("Hello Guice!");  
         }  
    } 

HelloGuiceModule.java
    package com.test.guice;  
    import com.google.inject.Binder;  
    import com.google.inject.Module;  
    public class HelloGuiceModule implements Module {  
        public void configure(Binder binder) {  
             //用于告诉 Guice 各个接口对应的实现。
             binder.bind(HelloGuice.class).to(HelloGuiceImpl.class);  
        }  
   } 

TestGuice.java
    package com.test.guice;  
    import junit.framework.TestCase;  
    import com.google.inject.Guice;  
    import com.google.inject.Injector;  
    public class TestGuice extends TestCase {  
        public void testHelloGuice() {  
             //使用 Injector 类启动 Guice, 在程序中创建注入器
             Injector injector = Guice.createInjector(new HelloGuiceModule());  
             HelloGuice helloGuice = injector.getInstance(HelloGuice.class);  
             helloGuice.sayHello();  
        }  
   } 
运行TestGuice,打印出:
Hello Guice!

4.使用Java Annotation标注来替代显式地绑定对象
   如果你需要一个接口来简化单元测试,而你又不介意编译时的依赖,你可以直接从你的接口指向一个缺省的实现。
   我们可以直接为HelloGuice加上@ImplementedBy注释,而省略掉对com.google.inject.Module的实现。
HelloGuice.java
    package com.test.guice;  
    import com.google.inject.ImplementedBy;  
     
    @ImplementedBy(HelloGuiceImpl.class)  
    public interface HelloGuice {  
        public void sayHello();  
    } 

TestGuice.java
    package com.test.guice;  
    import junit.framework.TestCase;  
    import com.google.inject.Guice;  
    import com.google.inject.Injector;  
     
    public class TestGuice extends TestCase {  
        public void testHelloGuice() {  
           //Injector injector = Guice.createInjector(new HelloGuiceModule());  
             
            Injector injector = Guice.createInjector();  
            HelloGuice helloGuice = injector.getInstance(HelloGuice.class);  
            helloGuice.sayHello();  
        }  
   } 
   HelloGuiceModule.java不再需要。其余的文件内容不变。
运行TestGuice,打印出:Hello Guice!

   缺省情况下,Guice 每次都注入一个新的实例。如果你想指定不同的作用域规则,你也可以对实现类进行标注。
   @Singleton
   package com.test.guice;  
      //接口的实现类HelloGuiceImpl
      public class HelloGuiceImpl implements HelloGuice {  
          public void sayHello() {  
             System.out.println("Hello Guice!");  
          }  
    } 

参考文献:
1.用 Guice 写 Java. http://hi.baidu.com/banseon/blog/item/e11a7523b6c003519822ed9e.html
2.Google Guice范例解说之使用入门. http://hi.baidu.com/kaisep/blog/item/16d4ce09f0195685d0581b3f.html



阅读(1650) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~