1200字范文,内容丰富有趣,写作的好帮手!
1200字范文 > DI(依赖注入)设计模式

DI(依赖注入)设计模式

时间:2020-12-17 09:19:25

相关推荐

DI(依赖注入)设计模式

一. DI(依赖注入)基本概念

DI—Dependency Injection,即“依赖注入”:

组件之间依赖关系由容器在运行期决定,形象的说,即由容器动态的将某个依赖关系注入到组件之中。

Java Dependency Injection设计模式允许我们删除硬编码的依赖关系,并使我们的应用程序松散耦合,可扩展和可维护。我们可以在Java中实现依赖注入,以将依赖解析从编译时移至运行时。

Java依赖注入似乎很难用理论来理解,因此我将举一个简单的例子,然后我们将看到如何使用依赖注入模式来实现应用程序中的松散耦合和可扩展性。

假设我们有一个消费应用程序EmailService来发送电子邮件。通常我们会像下面这样实现。

public class EmailService {public void sendEmail(String message, String receiver){//发送电子邮件的逻辑System.out.println("Email sent to "+receiver+ " with Message="+message);}}

EmailService类包含将电子邮件消息发送到收件人电子邮件地址的逻辑。我们的应用程序代码如下所示。

public class MyApplication {private EmailService email = new EmailService();public void processMessages(String msg, String rec){//做一些msg验证,操作逻辑等this.email.sendEmail(msg, rec);}}

我们的客户代码将使用MyApplicationclass发送电子邮件,如下所示。

public class MyLegacyTest {public static void main(String[] args) {MyApplication app = new MyApplication();app.processMessages("锦瑟无端五十弦", "李商隐@锦瑟");}}

乍一看,上述实现似乎没有错。但是以上代码逻辑有一定的局限性。

MyApplication类负责初始化电子邮件服务,然后使用它。这导致硬编码的依赖性。如果将来想切换到其他高级电子邮件服务,则需要在MyApplication类中更改代码。这使我们的应用程序难以扩展,如果在多个类中使用电子邮件服务,则将更加困难。

如果我们想扩展应用程序以提供附加的消息传递功能,例如SMS或Facebook消息,那么我们需要为此编写另一个应用程序。这将涉及应用程序类和客户端类中的代码更改。

由于我们的应用程序是直接创建电子邮件服务实例,因此测试该应用程序将非常困难。我们无法在测试类中模拟这些对象。

我们可以将MyApplication通过使用需要电子邮件服务作为参数的构造函数来从类中删除电子邮件服务实例的创建。

public class MyApplication {private EmailService email = null;public MyApplication(EmailService svc) {this.email = svc;}public void processMessages(String msg, String rec){//做一些msg验证,操作逻辑等this.email.sendEmail(msg, rec);}}

但是在这种情况下,我们要求客户端应用程序或测试类初始化电子邮件服务,这不是一个好的设计决定。

现在让我们看看如何应用Java依赖注入模式来解决上述实现的所有问题。Java中的依赖注入至少需要满足以下条件:

服务组件应使用基类或接口进行设计。最好选择为服务定义合同的接口或抽象类。消费者类应根据服务接口编写。先初始化服务的注入器类,然后初始化使用者类。

二. Java依赖注入–服务组件

对于我们的情况,我们可以MessageService声明为服务实现接口。

public interface MessageService {void sendMessage(String msg, String rec);}

现在,我们有实现上述接口的电子邮件和SMS服务。

public class EmailServiceImpl implements MessageService {@Overridepublic void sendMessage(String msg, String rec) {//发送电子邮件的逻辑System.out.println("Email sent to " + rec + " with Message=" + msg);}}

public class SMSServiceImpl implements MessageService {@Overridepublic void sendMessage(String msg, String rec) {//发送电子邮件的逻辑System.out.println("SMS sent to " + rec + " with Message=" + msg);}}

我们的依赖项注入Java服务已准备就绪,现在我们可以编写我们的使用者类。

三. Java依赖注入–服务使用者

我们不需要具有用于消费者类的基本接口,但是我将具有一个Consumer用于声明消费者类的接口。

public interface Consumer {void processMessages(String msg, String rec);}

消费者类实现如下。

public class MyDIApplication implements Consumer{private MessageService service;public MyDIApplication(MessageService svc){this.service=svc;}@Overridepublic void processMessages(String msg, String rec){//做一些msg验证,操作逻辑等this.service.sendMessage(msg, rec);}}

请注意,我们的应用程序类仅在使用服务。它不会初始化导致更好的“关注点分离”的服务。服务接口的使用还使我们能够通过模拟MessageService轻松地测试应用程序,并在运行时而不是在编译时绑定服务。

现在我们准备编写java依赖注入程序类,该类将初始化服务以及使用者类。

四. Java依赖注入–注入器类

让我们有一个MessageServiceInjector带有方法声明的接口,该接口返回Consumer该类。

public interface MessageServiceInjector {public Consumer getConsumer();}

现在,对于每项服务,我们将必须创建如下所示的注入器类。

public class EmailServiceInjector implements MessageServiceInjector {@Overridepublic Consumer getConsumer() {return new MyDIApplication(new EmailServiceImpl());}}

public class SMSServiceInjector implements MessageServiceInjector {@Overridepublic Consumer getConsumer() {return new MyDIApplication(new SMSServiceImpl());}}

现在,让我们看看我们的客户端应用程序将如何通过一个简单的程序使用该应用程序。

public class MyMessageDITest {public static void main(String[] args) {String msg = "锦瑟无端五十弦,一弦一柱思华年。";String email = "李商隐@锦瑟.com";String phone = "4088888888";MessageServiceInjector injector = null;Consumer app = null;//发送电子邮件injector = new EmailServiceInjector();app = injector.getConsumer();app.processMessages(msg, email);//Send SMSinjector = new SMSServiceInjector();app = injector.getConsumer();app.processMessages(msg, phone);}}

如您所见,我们的应用程序类仅负责使用服务。服务类别是在注入器中创建的。同样,如果我们必须进一步扩展我们的应用程序以允许Facebook消息传递,我们将只必须编写Service类和注入器类。

因此,依赖项注入实现解决了具有硬编码依赖项的问题,并帮助我们使应用程序灵活且易于扩展。现在让我们看看通过模拟注入器和服务类来测试应用程序类有多么容易。

五. 带有模拟注入器和服务的JUnit测试用例

public class MyDIApplicationJUnitTest {private MessageServiceInjector injector;@Beforepublic void setUp() {//使用匿名类模拟注入器injector = new MessageServiceInjector() {@Overridepublic Consumer getConsumer() {//模拟消息服务return new MyDIApplication(new MessageService() {@Overridepublic void sendMessage(String msg, String rec) {System.out.println("模拟消息服务实现");}});}};}@Testpublic void test() {Consumer consumer = injector.getConsumer();consumer.processMessages("锦瑟无端五十弦", "李商隐@锦瑟");}@Afterpublic void tear() {injector = null;}}

正如你可以看到,我使用匿名类来模拟注入器和服务类,我可以轻松地测试我的应用程序的方法。我在上述测试类中使用的是JUnit 4,因此,如果您在测试类之上运行,请确保它在您的项目构建路径中。

我们使用构造函数将依赖项注入应用程序类中,另一种方法是使用setter方法将依赖项注入应用程序类中。对于setter方法的依赖注入,我们的应用程序类将如下实现。

public class MyDIApplication implements Consumer {private MessageService service;public MyDIApplication(){}//setter依赖注入public void setService(MessageService service) {this.service = service;}@Overridepublic void processMessages(String msg, String rec){//做一些msg验证,操作逻辑等this.service.sendMessage(msg, rec);}}

public class EmailServiceInjector implements MessageServiceInjector {@Overridepublic Consumer getConsumer() {MyDIApplication app = new MyDIApplication();app.setService(new EmailServiceImpl());return app;}}

使用基于构造函数的依赖项注入还是基于setter是设计决策,取决于您的要求。例如,如果没有服务类我的应用程序根本无法工作,那么我宁愿使用基于构造函数的DI,否则我会选择基于setter方法的DI仅在真正需要时才使用它。

Java中的依赖注入是一种通过将对象绑定从编译时移到运行时来在我们的应用程序中实现控制反转(IoC)的方法。我们也可以通过工厂模式,模板方法设计模式,策略模式和服务定位器模式来实现IoC 。

Spring Dependency Injection, Google Guice和Java EE CDI框架通过使用Java Reflection API和Java注释简化了依赖项注入的过程。我们所需要做的就是注释字段,构造函数或设置方法,并在配置xml文件或类中对其进行配置。

六. Java依赖注入的优缺点

Java依赖注入的好处

在Java中使用依赖注入的一些好处是:

关注点分离减少应用程序类中的代码,因为初始化依赖项的所有工作都由注入器组件处理可配置的组件使应用程序易于扩展使用模拟对象可以轻松进行单元测试

Java依赖注入的缺点

Java依赖注入也有一些缺点:

如果使用过度,则可能导致维护问题,因为在运行时知道更改的效果。Java中的依赖关系注入隐藏了服务类依赖关系,这可能导致运行时错误,而这些错误在编译时就会被捕获。

参考链接: DI设计模式示例教程

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。