本栏是博主根据如题教材进行Java进阶时所记的笔记,包括对原著的概括、理解,教材代码的报错和运行情况。十分建议看过原著遇到费解地方再来参考或与博主讨论。致敬作者Joshua Bloch跟以杨春花为首的译者团队,以及为我提供可参考博文的博主们。另外,本条目是《Effective Java 3rd Edition》中的新条目,此后笔记多基于新一些的第三版。
用依赖注入代替硬编码
硬编码
首先说明啥叫硬编码。
硬编码是将数据直接嵌入到程序或其他可执行对象的源代码中的软件开发实践,与从外部获取数据或在运行时生成数据不同。 硬编码数据通常只能通过编辑源代码和重新编译可执行文件来修改,尽管可以使用调试器或十六进制编辑器在内存或磁盘上进行更改。 硬编码的数据通常表示不变的信息,例如物理常量,版本号和静态文本元素。 另一方面,软编码数据对用户输入,HTTP服务器响应或配置文件等任意信息进行编码,并在运行时确定。
硬编码几乎是不灵活性的代名词,一旦使用,很难修改。下面这个类用来说明为什么不要用硬编码风格的设计。
/**
* 被当作资源以供使用
*/
public class Lexicon {
public static Supplier<Lexicon> supplier = () ->new Lexicon();
}
/**
* 这段代码看上去完全o**k,但实际上它隐含了“只有唯一一本字典”的前提条件。假设需要换一本完全不同的字典,
* 比如字典从牛津换成柯林斯,甚至从英语字典换成德语字典,这时只能将{@link #dictionary}
* 的final关键字去掉,然后编一个换字典方法。但这么干容易出错,而且对于并发很不友好。
*
* 对于行为被底层资源参数化的类(翻译过来感觉应该是行为参数化,可以通过策略模式和lambda表达式灵活实现),
* 把它作为工具类或者作为单例都不是很好,这时候可以通过依赖注入,在类被创建时告知需要何种字典
*/
public class BadSpellChecker {
private static final Lexicon dictionary = new Lexicon();
/** Non-instantiable*/
private BadSpellChecker() {}
public static BadSpellChecker INSTANCE = new BadSpellChecker();
public boolean isValid(String word) {
//...
return false;
}
public List<String> suggestions(String typo){
//...
return new ArrayList<>();
}
}
顺便,关于行为参数化的理解可以参见这篇博客,关键词:策略模式,灵活,Lambda表达式。感谢该博客的博主!
依赖注入
这种技术在MVC,MVP等设计模式中被广泛应用,原因就在于它对资源管理的灵活性。
/**
* 改进后的spellchecker,将需要的资源作为参数传入到构造方法中(本例中是构造方法,也适用于Builder,
* 工厂方法等)。另外,将资源的工厂类传进来可以方便地根据需求创建多个资源对象,创建特定的子类对象,
* 这也是依赖注入的有用变体之一。Java8中引入的{@link java.util.function.Supplier}接口,
* 就非常适合这种依赖注入形式,只要在泛型中规定资源父类的类型,就可以根据需要选择该资源的具体子类实现。
* 例如这一句Java类库中马赛克的例子:Mosaic create(Supplier<? extends Tile> tileFactory) { ... }
*/
public class SpellChecker {
private final Lexicon dictionary;
public SpellChecker(Lexicon dictionary) {
this.dictionary = Objects.requireNonNull(dictionary);
}
public boolean isValid(String word) {
//...
return false;
}
public List<String> suggestions(String typo){
//...
return new ArrayList<>();
}
}
全代码git地址:点我点我