从一个任务开始讲
某天,公司领导找到开发人员,说要开发一个微信支付宝的收款明细获取功能,我们把这个任务作为一个案例进行说明。
第一步:设计
案例精简:把任务指派给开发人员完成。本句话中,有两个名词:“任务”和“开发人员”,所以我们考虑设计两个对象(任务和开发人员)。
开发人员对象:
package DependencyInjectionDemo;
public class Javaer {
private String name;
public Javaer(String name) {
this.name = name;
}
public void setName(String name) {
this.name = name;
}
public void WriteCode() {
System.out.println(this.name + " writting java code...");
}
}
任务对象:
package DependencyInjectionDemo;
public class NewTask {
private String name;
private Javaer javaer;
public NewTask(String name) {
this.name = name;
this.javaer = new Javaer("张三");
}
public void Start() {
System.out.println(this.name + " started ..");
this.javaer.WriteCode();
}
}
场景类:
package DependencyInjectionDemo;
public class DependencyInjectionDemo {
public static void main(String[] args) {
NewTask task = new NewTask("开发微信支付宝收款明细获取工具");
task.Start();
}
}
运行结果:
开发微信支付宝收款明细获取工具 started ..
张三 writting java code...
现在让我们来分析一下这个设计存在的问题。
- 如果不追求复用和耦合,只是临时完成任务,这么写倒也无可厚非;
- 如果再有别的任务指派给其他开发人员,我们需要去代码内部修改编码;
- 如果有很仰慕你的同事需要复用你的实现,你不能打包成jar文件给他直接用,因为他不能从jar文件外部修改任务和开发人员;
所以,我们应当让用户来指派开发人员,改进一下:
package DependencyInjectionDemo;
public class NewTask {
private String name;
private Javaer javaer;
public NewTask(String name) {
this.name = name;
//this.javaer = new Javaer("张三"); 删了啦
}
public void SetJavaer(Javaer javaer) {
this.Javaer = javaer;
}
public void Start() {
System.out.println(this.name + " started ..");
this.javaer.WriteCode();
}
}
场景类也要做一下修改:
package DependencyInjectionDemo;
public class DependencyInjectionDemo {
public static void main(String[] args) {
NewTask task = new NewTask("开发微信支付宝收款明细获取工具");
task.SetJavaer(new Javaer("张三")); //加入这句
task.Start();
}
}
输出和前面的Demo是一样的:
开发微信支付宝收款明细获取工具 started ..
张三 writting java code...
现在,我们知道了一个事实,完成任务需要依赖特定的开发人员(NewTask类依赖Javaer类),开始时,NewTask类在构造时绑定开发人员,现在这种依赖可以在使用时按需要进行绑定。
这就是依赖注入
在上面的案例中,我们是通过Setter进行注入的,另外一种常用的注入方式是通过构造方法进行注入:
public NewTask(String name, Javaer javaer) {
this.name = name;
this.javaer = javaer; //构造方法中进行注入
}
这里联想一下,任务执行期间,任务执行者(本例中是张三)生病了,那么就需要另外安排一名开发人员继续任务的执行,怎么办呢?这个时候应该考虑的是Javaer这个对象的稳定性,如果开发人员这个对象稳定性非常高,我们可以考虑在NewTask的构造方法中进行注入,因为开发人员这个对象非常稳定,不会出现中途换帅的情况,但事实并非如此,张三生病了,就得允许不中断任务的情况下,重新指派另一名开发人员继续进行开发,很显然,在这个场景中,我们应该使用Setter注入,不需要重新New一个NewTask(也就是任务重新开始),直接使用Setter更换开发人员即可。
这里还有一种注入方式是配置文件注入,这就要求注入的对象稳定性非常高,甚至高到大于服务的生命周期(比如数据库连接)。
第二步:需求挖掘
我们知道,一个开发团队往往是多种开发语言并存的,有些任务适合用Java来完成,有些适合用C#,还有些任务适合用Python,现在问题来了,这个NewTask类库的使用者发现:任务只能指派给Javaer。
所以为了更好的复用,我们的需求应该变成:任务既能指派给Javaer,也能指派给Pythoner和CSharper,以及其他任何以后可能加入的开发语言。
很自然的,我想到了使用接口:
package DependencyInjectionDemo;
public interface Coder {
void WriteCode();
}
修改原来的Javaer,实现Coder接口:
package DependencyInjectionDemo;
public class Javaer implements Coder {
private String name;
public Javaer(String name) {
this.name = name;
}
public void setName(String name) {
this.name = name;
}
public void WriteCode() {
System.out.println(this.name + " writting java code...");
}
}
Python开发人员实现Coder接口:
package DependencyInjectionDemo;
public class Pythoner implements Coder{
private String name;
public Python