用Java中的例子反转控制,依赖注入和策略模式

时间:2012-09-22 05:26:17

标签: java spring dependency-injection inversion-of-control strategy-pattern

我经常对这三个术语感到困惑。这三个看起来与我相似。有人可以通过示例清楚地向我解释。

我看过类似的帖子,完全不明白。

3 个答案:

答案 0 :(得分:20)

依赖注入是指告诉类其依赖性是什么的模式,而不是要求类知道在哪里找到它的所有依赖项。

所以,例如,你可以从这里开始:

public class UserFetcher {
   private final DbConnection conn = 
      new DbConnection("10.167.1.25", "username", "password");

   public List<User> getUsers() {
      return conn.fetch(...);
   }
}

这样的事情:

public class UserFetcher {
   private final DbConnection conn;

   public UserFetcher(DbConnection conn) { 
      this.conn = conn;
   }

   public List<User> getUsers() {
      return conn.fetch(...);
   }
}

这减少了代码中的耦合,如果您想要单元测试UserFetcher,这尤其有用。现在,您可以将UserFetcher传递到测试数据库,而不是10.167.1.25 始终DbConnection处找到的数据库运行。或者,在快速测试中更有用,您可以传入DbConnection的实现或子类,它甚至连接到数据库,它只是丢弃请求!

然而,这种原始依赖注入使连接(提供具有依赖性的对象)更加困难,因为您已经使用全局变量(或本地实例化)替换了访问依赖性通过整个对象图传递依赖关系。

考虑UserFetcherAccountManager的依赖关系的情况,AdminConsoleAdminConsole的依赖关系。然后DbConnection需要将AccountManager个实例传递给AccountManager,而UserFetcher需要将其传递给AdminConsole ... ,即使它们都不是{{} 1}}或AccountManager需要直接使用DbConnection

反转控件容器(Spring,Guice等)旨在通过自动连接(提供)依赖项来使依赖注入更容易。要做到这一点,你告诉你的IoC容器一旦如何提供一个对象(在Spring中,这被称为 bean ),每当另一个对象要求该依赖时,它将由容器提供。

因此,如果使用构造函数注入,我们的最后一个示例可能与Guice一样:

public class UserFetcher {
   private final DbConnection conn;

   @Inject //or @Autowired for Spring
   public UserFetcher(DbConnection conn) { 
      this.conn = conn;
   }

   public List<User> getUsers() {
      return conn.fetch(...);
   }
}

我们必须配置IoC容器。在Guice中,这是通过Module的实现完成的;在Spring中,您通常通过XML配置应用程序上下文

public class MyGuiceModule extends AbstractModule {    
    @Override
    public void configure() {
       bind(DbConnection.class).toInstance(
           new DbConnection("localhost", "username", "password"));
    }
}

现在,当UserFetcher由Guice或Spring构建时,DbConnection会自动提供。

Guice有a really good Wiki article关于依赖注入的动机,并进一步使用IoC容器。它一直值得一读。

策略模式只是依赖注入的一个特例,您可以在其中注入 logic 而不是对象(即使在Java中,逻辑将封装在一个对象中)。它是解耦独立业务逻辑的一种方式。

例如,您可能有这样的代码:

public Currency computeTotal(List<Product> products) {
   Currency beforeTax = computeBeforeTax(products);
   Currency afterTax = beforeTax.times(1.10);
}

但是,如果您想将此代码扩展到新的司法管辖区,并使用不同的销售税计划,该怎么办?您可以注入计算税的逻辑,如下所示:

public interface TaxScheme {
    public Currency applyTax(Currency beforeTax);
}

public class TenPercentTax implements TaxScheme {
    public Currency applyTax(Currency beforeTax) {
        return beforeTax.times(1.10);
    }
} 

public Currency computeTotal(List<Product> products, TaxScheme taxScheme) {
    Currency beforeTax = computeBeforeTax(products);
    Currency afterTax = taxScheme.applyTax(beforeTax);
    return afterTax;
}

答案 1 :(得分:2)

控制反转意味着运行时框架将所有组件连接在一起(例如Spring)。依赖注入是IoC的一种形式(我不知道是否存在其他形式的IoC)(参见:http://en.wikipedia.org/wiki/Inversion_of_control)。

策略模式是一种设计模式(由GoF定义),其中算法可以被另一个替换(参见:http://en.wikipedia.org/wiki/Strategy_pattern)。这是通过提供相同接口的几个实现来存档的。当使用像Spring这样的IoC时,如果你有多个接口实现,并且你可以通过配置从一个实现切换到另一个实现,那么你就是在使用策略模式。

答案 2 :(得分:0)

我还建议阅读Spring文档的介绍章节,该章节重点讨论这个问题: Introduction to Spring Framework
前几段应该这样做。

这也链接到:Inversion of Control Containers and the Dependency Injection pattern
这也提供了对这些非常重要的核心概念的激励观点。