资讯专栏INFORMATION COLUMN

第5项:固定资源首选使用依赖注入

KnewOne / 2331人阅读

摘要:满足此要求的简单模式是在创建新实例时将资源传递给构造函数。依赖注入同样适用于构造函数静态工厂第项和构建器第项。将资源工厂传递给构造函数就会变成一个有用的模式。这种做法称为依赖注入,将极大地增强类的灵活性,可重用性和可测试性。

  许多类依赖于一个或多个底层资源。 例如,拼写检查器依赖于字典。常见的做法是将这些类实现为静态实用程序类(第4项):

// Inappropriate use of static utility - inflexible & untestable!
public class SpellChecker {
    private static final Lexicon dictionary = ...;
    private SpellChecker() {} // Noninstantiable
    public static boolean isValid(String word) { ... }
    public static List suggestions(String typo) { ... }
}

  同样的,将它们作为单例实现的情况并不少见(第3项):

// Inappropriate use of singleton - inflexible & untestable!
public class SpellChecker {
    private final Lexicon dictionary = ...;
    private SpellChecker(...) {}
    public static INSTANCE = new SpellChecker(...);
    public boolean isValid(String word) { ... }
    public List suggestions(String typo) { ... }
}

  这些方法都不令人满意,因为它们假设只有一本值得使用的字典。 在实践中,每种语言都有自己的字典,特殊字典用于特殊词汇。 而且,可能需要使用特殊字典进行测试。 假设单本字典就足以满足所有情况,这是一厢情愿的想法。

  你可以尝试让SpellChecker支持多个词典,方法是使字典字段为非final域,并添加一个方法来更改现有拼写检查器中的字典,但这在并发时设置会很笨拙,容易出错并且不可行。 静态实用程序类和单例不适用于底层资源作为参数的类(Static utility classes and singletons are inappropriate for classes whose behavior is parameterized by an underlying resource.)。

  所需要的是能够支持类的多个实例(在我们的示例中为SpellChecker),每个实例都使用客户端所需的资源(在我们的示例中为字典)。 满足此要求的简单模式是在创建新实例时将资源传递给构造函数。 这是依赖注入的一种形式:字典是拼写检查器的依赖项,并在创建时注入拼写检查器。

// Dependency injection provides flexibility and testability
public class SpellChecker {
    private final Lexicon dictionary;
    public SpellChecker(Lexicon dictionary) {
        this.dictionary = Objects.requireNonNull(dictionary);
    }
    public boolean isValid(String word) { ... }
    public List suggestions(String typo) { ... }
}

  这种依赖注入很简单,以至于程序猿用了很多年却不知道它有一个名称。虽然我们的拼写检查器只有一个资源(字典),但是依赖注入可以使用任意数量的资源和任意的依赖关系,它保留了不变性(第17项),因此多个客户端可以共享依赖对象(假设客户端需要相同的底层资源)。依赖注入同样适用于构造函数、静态工厂(第1项)和构建器(第2项)。

  将资源工厂传递给构造函数就会变成一个有用的模式。工厂是一个对象,通过重复调用这个工厂可以创建某个类型的实例对象。这些就是工厂方法模式 [Gamma95]。Java 8中引入的Supplier 接口非常适合体现工厂。在输入上采用Supplier 的方法通常应该使用泛型(第31项)约束工厂的类型参数,以允许客户端传入创建指定类型的任何子类型的工厂。例如,这是一种使用客户提供的工厂生成马赛克来生成每个图块的方法:

Mosaic create(Supplier tileFactory) { ... }

  尽管依赖注入极大地提高了灵活性和可测试性,但它可能会使大型项目更加混乱,这些项目通常包含数千个依赖项。通过使用依赖注入框架,例如Dagger [Dagger],Guice [Guice]或Spring [Spring],可以消除这种混乱。这些框架的使用超出了本书的范围,但请注意,为手动依赖注入而设计的API可以轻松地适用于这些框架。

  总之,如果有一个类依赖一个或多个底层资源的类,并且底层资源类影响了类的行为,不要使用单例或静态实用程序类来实现它,并且不要让类直接创建这些资源(do not use a singleton or static utility class to implement a class that depends on one or more underlying resources whose behavior affects that of the class)。相反,将资源或工厂传递给构造函数(或静态工厂或构建器)。这种做法称为依赖注入,将极大地增强类的灵活性,可重用性和可测试性。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/73935.html

相关文章

  • 看起来很长但还是有用的Spring学习笔记

    摘要:关于依赖注入注入的注解提供的注解不仅仅是对象,还有在构造器上,还能用在属性的方法上。与之相反,的限定符能够在所有可选的上进行缩小范围的操作,最终能够达到只有一个满足所规定的限制条件。注解是使用限定符的主要方式。 本文首发于泊浮目的专栏:https://segmentfault.com/blog... Spring致力于提供一种方法管理你的业务对象。在大量Java EE的应用中,随处可...

    DTeam 评论0 收藏0
  • php面试总结

    摘要:由一层函数调用进入下一层函数调用的递推。此时,中的一个称为孤儿的类就会收留这个对象。禁止访问服务器拒绝请求服务器找不到请求的页面服务器内部错误坏的网关一般是网关服务器请求后端服务时,后端服务没有按照协议正确返回结果。 持续更新。。。。 php 1. 简述 php 中的 autoload Autoload 的加载机制,当通过 new 来实例化一个类时,PHP 会通过定义的 autol...

    greatwhole 评论0 收藏0
  • 二章 创建和销毁对象

    摘要:一个类可以提供一个公共静态工厂方法,它仅仅是一第项遇到多个构造器参数时要考虑使用构建器静态工厂和构造器有个共同的局限性他们都不能很好地扩展到大量的可选参数。   本章涉及创建和销毁对象,包括何时以及如何创建它们,何时以及如何避免创建它们,如何确保它们被及时销毁,以及如何管理在销毁之前必须进行的清理操作。 第1项:用静态工厂方法代替构造器   类允许客户端获取实例的传统方法是提供公共构造...

    Jeffrrey 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<