依赖注入(Java Dependency Injection)

2019-05-15  本文已影响0人  Showdy

依赖注入(Java Dependency Injection)

例子: 一个应用程序(Computer),使用服务器(Service)去发送邮件,实现的代码如下:

//属性注入
class Computer {

    //局限: 依赖EmailService,并需要实例化使用,不利于扩展和测试
    private val emailService = EmailService()

    fun processMessages(msg: String, rec: String) {
        //do some msg validation, manipulation logic etc
        emailService.sendEmail(msg, rec)
    }
}

EmailService类处理发送邮件的具体逻辑:

class EmailService {
    fun sendEmail(message: String, receiver: String) {
        println("Email sent to $receiver with Message=$message")
    }
}

用户(User)使用计算机去发送一封邮件(直接使用main函数调用):

fun main() {
    val computer = Computer()
    computer.processMessages("Hi Pankaj", "pankaj@abc.com")
}

乍一看好像没啥问题,不过这样子实现,有几点局限性:

那换一种做法: 我们移除Computer类直接实例化EmailService的代码,改为使用构造函数传入代替:

/构造函数注入
class Computer(private val emailService: EmailService){
    fun processMessages(msg: String, rec: String) {
        emailService.sendEmail(msg, rec)
    }
}

但这样一来, 需要``User在使用Computers时去实例化EmailService`, 这种做法也不好.

现在可以使用Java依赖注入来解决上面的问题, DI主要包括下列几个方法:

Service Component

继续使用上面的例子, 定义个MessageService接口

interface MessageService{
    fun sendMessage(msg: String, rec: String)
}

然后有两个实现类:

class EmailServiceImpl : MessageService {
    override fun sendMessage(msg: String, rec: String) {
        println("Email sent to $rec with Message=$msg")
    }
}

class SMSServiceImpl : MessageService {
    override fun sendMessage(msg: String, rec: String) {
        println("SMS sent to $rec with Message=$msg")
    }
}

Service Consumer

定义一个接口, 提供一个统一处理消息的方法

interface Consumer {

    fun processMessages(msg: String, rec: String)
}

Consumer接口实现类如下:

class DIConsumerImpl(private val service: MessageService) : Consumer {

    override fun processMessages(msg: String, rec: String) {
        this.service.sendMessage(msg, rec)
    }
}

Consumer类仅仅是使用类Service并没有去实例化他, 比较符合separation of concerns原则

Injectors Class

定义一个方法或者属性来获取Consumer对象

interface MessageServiceInjector {

    val consumer: Consumer
}

对于每个Service类,去创建一个Injector

class EmailServiceInjector : MessageServiceInjector {

    override val consumer: Consumer
        get() = DIConsumerImpl(EmailServiceImpl())

}

class SMSServiceInjector : MessageServiceInjector {

    override val consumer: Consumer
        get() = DIConsumerImpl(SMSServiceImpl())

}

这样子User就可以使用了:

fun main(){
    val msg = "Hi Pankaj"
    val email = "pankaj@abc.com"
    val phone = "4088888888"
    var injector: MessageServiceInjector
    var app: Consumer

    //Send email
    injector = EmailServiceInjector()
    app = injector.consumer
    app.processMessages(msg, email)

    //Send SMS
    injector = SMSServiceInjector()
    app = injector.consumer
    app.processMessages(msg, phone)
}

这样,User类仅仅是使用了Service, Service类的实例化交给了Injector去实现. 注入使得代码耦合度降低,并且增加程序的扩展性.

上面使用了构造函数的注入方式, 其实还有一种注入方式为方法注入:

class DIMethodConsumer : Consumer {

    private lateinit var service: MessageService

    //setter dependency injection
    fun setService(service: MessageService) {
        this.service = service
    }

    override fun processMessages(msg: String, rec: String) {
        //do some msg validation, manipulation logic etc
        this.service.sendMessage(msg, rec)
    }
}

class EmailServiceInjector2 : MessageServiceInjector {

    override val consumer: Consumer
        get() {
            val app = DIMethodConsumer()
            app.setService(EmailServiceImpl())
            return app
        }
}
fun main() {
    val msg = "Hi Pankaj"
    val email = "pankaj@abc.com"
    val phone = "4088888888"
    val injector: MessageServiceInjector
    val app: Consumer

    //Send email
    injector = EmailServiceInjector2()
    app = injector.consumer
    app.processMessages(msg, email)
}

到底是使用构造函数注入还是方法注入取决于开发需求.

依赖注入有着如下的优点:

同时有着如下的缺点:

上一篇下一篇

猜你喜欢

热点阅读