Day 18: To DI ? Or not DI? 依赖注入的存在意义

Keyword: Dependency Injection
到Day20 使用Koin管理依赖注入显示在Android上 放在这边
KMMDay20


依赖注入(Dependency Injection)

依赖注入(Dependency Injection,简称DI),是一种...物件的使用法,主要的目标是降低物件与物件之间的耦合关系.关於相系确的定义网路上一大堆.

这边就用我自己的想法介绍吧.

依赖注入,分成了"依赖","注入"两块.

所谓的依赖,就是使用到的物件.称为依赖.像是这样:

//Kotlin
class People(var air:Air){
		val heart:Heart
}

这个例子中,人类没有心脏(Heart)或是空气(Air),就无法正常运作.那麽就称 “人,依赖於心脏及空气"

这就是依赖关系

既然需要这些重要的部分才能正常运作,那有两种来源:一种是天生的,另外一个是外来的.

//Kotlin
class People(var air:Air){
		val heart:Heart = UnbreakableHeart()
}

像上面这个例子,人类天生就有了一个心脏的实体,所以能正常运作.

那另外一种,由地球妈妈提供空气:

//Kotlin
class EarthMom(){
	val air:Air = FreshAir()
	val people:People = People(air)
}

class People(var air:Air){
		val heart:Heart
}

有了地球妈妈带来的空气,人类也能正常运作了.

而後面这种由外部提供需要的依赖,就称之为"依赖注入"

其实不难懂对吧?

依赖的问题

实际上在使用时,会有一个问题.就是如果某个部分发生了修改,所有依赖到的地方跟着修改,需要修改很多很多地方.

比如说,有一天,人类除了空气,还需要水.

//Kotlin
class EarthMom(){
	val air:Air = FreshAir()
	val water:Water = CleanWater()
	val people_1:People = People(air,water)
	val people_2:People = People(air,water)
				...
	val people_6Billion:People = People(air,water)
}

class People(var air:Air, var water:Water){
		val heart:Heart
}

所以地球妈妈需要去把六十亿人都加上water的需求.

但是人类的内容修改,跟地球妈妈有什麽关系呢?

这就是依赖所带来的耦合,当被依赖的那方改变时,依赖的这方也需要作出调整.

如果有一个全知全能的神,可以提供所需要的任何东西,那麽人类变化就改成直接跟这个人要空气跟水,不再由地球妈妈提供.那麽地球妈妈与人类的关系就解开了.

透过什麽方法提供呢,就是上面说过的依赖注入.而这个管理一切物件建立与回收的神,我们就称之为"依赖注入框架"

依赖注入的好处

透过依赖注入框架的帮助,我们解耦了物件之间的依赖关系,所带来了什麽好处呢?

  1. 物件之间的建构子不再会直接影响到额外的区块
    由於所有的物件都由框架处理建构的部分,要使用到的物件也是直接跟框架索取.因此当规格修改时,只要修改框架内的生产逻辑即可作用於全局.
  2. 测试更容易撰写了
    由於物件的产生由框架代劳,因此要进行Api或是DB的Mock时,只要修改框架的设定档,就能直接更换成测试使用的Api或DB,而专案内部的程序码,甚至都不用修改,就能够进行测试.
  3. 不用再一路传入不需要的数据
    在App中,常常有这样的使用情境,去确认某资料需不需要更新,如果要更新的话就更新资料,并且把新资料储存进本地的DB.
    Android的DB在使用时需要一个特别的物件 Context,所以问题就变为了为了不一定发生发生的DB存取,在进行流程的一路上都需要携带这个物件以备不时之需.十分恼人.
    而有了注入框架,就能够在正式使用DB的时机,由注入框架提供所需要的物件,从这个情况中解放出来.

依赖注入的痛苦

在Android端,最着名的依赖注入框架,当数Dagger.有名的地方正是超高的学习曲线.有句玩笑话这样说的“Dagger,从入门到放弃”.由於Dagger是设计给Java使用,而不是给Android专用,因此有非常多需要根据Android的情况而进行的特制.

https://github.com/officeyuli/itHome2021/raw/main/day18/learning%20curve.jpeg

上面说的,一个依赖注入框架需要管理物件的建立与回收,但是Android的应用端并不能”直接“接触到Activity或是Fragment的建立与回收.

由於Android App是利用设计模式中的Template模式,仅仅露出了一小部分的开口让Android工程师发挥,其余部分则是由系统代为执行,也不让一般的App有机会修改.例如画面的测量与描绘,手势事件的转换和处理,都是由底层自行处理.这样的好处就是开发很顺畅,不用再去管一些无关紧要的小事,只要告诉系统,在Activity或是Fragment建立的时候顺便做什麽事(onCreate/onDestory),或是发生事件时我想做什麽(onTouch/onClick).被系统掌控最明显的就是,无法掌控App什麽时候被回收,有的时候一退到背景,系统本身就忙着把这个记忆体空间清出来,直接回收了.

也是因为这样,想要在App端上实作一个依赖注入框架,是十分困难的,随时要小心系统的背刺.再加上Dagger发生错误时通常Log所显示的并不是真正的问题所在,还会需要为了不同的实作写许许多的module设定档,新人只要发生问题往往不知所措.连追踪都很困难.这也是Dagger劝退很多人的原因之一

我在进行公司专案重构的时候,也曾经考虑到使用Dagger来进行依赖管理,但是考虑到学习成本与带来的问题,所消耗的时间,最後决定不使用.当然目前也是因为当初的这个决定,依赖上也出现了很多问题,但是如果回到那时候,我仍然会选择放弃Dagger.这也从侧面证明了Dagger到底有多难学和使用.

官方也有注意到这个问题,所以在去年的Jetpack中,加入了Hilt,一个基於Dagger,Android专用的依赖注入框架,Dagger(匕首)加上了Hilt(刀柄),十分轻易好用.如果有新专案,我会推荐Android工程师使用Hilt.

但是这次我们写的是跨平台KMM,那Hilt这些针对Andriod的优化就无法使用,所以我们这次将使用Kotlin官方推出的另外一套注入框架,针对Kotlin专用的,Koin.


<<:  [day-8] 凡事都有第一次,撰写程序前的必要步骤!

>>:  [Day 08] - Spring Boot 实作登入验证(二)(JWT浅析)

Day15:今天我们来聊一下使用Parrot Security的 Armitage来取得远端系统的存取(Gain Access)权

今天我们来使用Armitage这工具对远端系统进行Gain Access测试 这套工具很强,使用前请...

超微x9dri-ln4f+ 安装MacOS Mojave 10.14.6

机器配置: 主板:超微x9dri-ln4f+ cpu:E5-2650v2 x2 内存:64gb 8g...

[Day_29]函式与递回_(8)

函式视为物件 Python中函式视为物件,以函式名称当作物件,函式名称加上()才会执行该函式,范例如...

并行程序的潜在问题 (三)

在介绍 Mutex lock 与 Spinlock 後,本篇文章同样针对并行程序的 Synchron...

Flutter基础介绍与实作-Day29 旅游笔记的实作(10)

今天要写的是隐藏式选单的第三个"登出"的选项,原本这格写的是使用说明,後来想想好...