<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          邪惡的字段注入

          共 2977字,需瀏覽 6分鐘

           ·

          2021-06-17 12:33

          繼《給 Java gradle 工程添加 git hooks》之后,再次延續(xù)《后端工程圣殿形象的崩塌以及重建》一文,用 JavaScript 工程師的視角,來(lái)改造理論上本該高大上但是現(xiàn)實(shí)中卻大面積倒塌的 java 后端工程。


          今天面試了一個(gè)號(hào)稱(chēng)有著 8 年 java 開(kāi)發(fā)經(jīng)驗(yàn)的工程師(據(jù)說(shuō)所謂的 java 工程師,事實(shí)上就是 Spring 工程師而已)。我問(wèn)她這兩種寫(xiě)法有什么區(qū)別?你更傾向哪一種?


          @Serviceclass ServiceX {...}
          // 寫(xiě)法 1class A1 {    @Autowired private ServiceX service;
              ...}
          // 寫(xiě)法 2class A2 { private final ServiceX service;
              public A2 (@Autowired ServiceX service) {        this.service = service; } ...}


          她看了大驚失色:啊?還有 A2 這種寫(xiě)法?沒(méi)見(jiàn)過(guò)啊,都是像第一種那樣去寫(xiě)啊!

          我不驚訝她的回答,因?yàn)槲覀児镜膶?shí)際項(xiàng)目中也是這樣的,所有人都是像第一種那樣去寫(xiě)的,從來(lái)沒(méi)有人覺(jué)得任何不適。


          但是我很不適,首先 IDE 會(huì)給第一種寫(xiě)法畫(huà)上波浪線(xiàn),給一個(gè)黃色警告。我不明白那些看不起 JavaScript 工程師的 java 工程師們,為什么從來(lái)不去注意這種警告?


          其次是我在寫(xiě)測(cè)試時(shí),覺(jué)得更加不爽。原來(lái)沒(méi)有人覺(jué)得不適,因?yàn)轫?xiàng)目中根本沒(méi)有測(cè)試。通過(guò)寫(xiě)測(cè)試,我切身體會(huì)到了兩種寫(xiě)法的區(qū)別和各自的優(yōu)劣,結(jié)論是強(qiáng)烈建議使用第二種寫(xiě)法,應(yīng)該沒(méi)有不得不使用第一種寫(xiě)法的場(chǎng)景。


          以下是個(gè)人粗淺的理解的總結(jié)


          以上的代碼就是要寫(xiě)一個(gè)類(lèi),該類(lèi)依賴(lài)一個(gè) Service,使用 Spring 框架實(shí)現(xiàn)這個(gè)依賴(lài) Service 的類(lèi),當(dāng)然要使用依賴(lài)注入,這個(gè) @Autowired 注解就是用來(lái)注入依賴(lài)的。但是注入的方式有兩種,以上第一種寫(xiě)法是字段注入,而第二種寫(xiě)法是構(gòu)造器注入。



          為什么不建議采用字段注入?


          一、測(cè)試不僅難寫(xiě),而且難以運(yùn)行


          測(cè)試時(shí)需要控制依賴(lài)項(xiàng),所以往往需要將真正的依賴(lài)項(xiàng)模擬掉。如果使用字段注入,測(cè)試就很難寫(xiě)。因?yàn)橐M這個(gè)依賴(lài)項(xiàng),就要寫(xiě)更多的代碼。測(cè)試也很難運(yùn)行,因?yàn)橐ǜL(zhǎng)時(shí)間運(yùn)行,更耗機(jī)器資源,還讓反饋?zhàn)兟?/span>


          比如對(duì)于字段注入的代碼進(jìn)行測(cè)試,你需要先在測(cè)試類(lèi)上注解上 Spring 相關(guān)的環(huán)境,而且在運(yùn)行測(cè)試時(shí)會(huì)真的啟動(dòng) Spring 容器,所以運(yùn)行起來(lái)很慢:


          @RunWith(SpringRunner.class)@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)class A1Test {    @MockBean    private Service mockService;       @Autowired    private A1 sut;       @Test    void testIt() {        ...    }}



          事實(shí)上,除非是寫(xiě)集成測(cè)試,根本沒(méi)有必要啟動(dòng) Spring 環(huán)境。所以對(duì)于構(gòu)造器注入,測(cè)試代碼不僅更加簡(jiǎn)潔且運(yùn)行更快:

          class A2Test {    private final A2 sut;    private final Service mockService;       {        mockService = Mockito.mock(Service.class);               // 直接 new,避開(kāi)了 Spring 容器的開(kāi)銷(xiāo)        sut = new A2(mockService);    }       @Test    void testIt() {        when(mockService.method(any(Object.class)).thenReturn(0);        ...    }}


          二、字段注入方式違反了不可變性原則


          對(duì)比 A1 的實(shí)現(xiàn),注意在 A2 的實(shí)現(xiàn)中(構(gòu)造器注入),使用了 final 關(guān)鍵字。這帶來(lái)了很大的好處,因?yàn)檫@個(gè)字段內(nèi)容在應(yīng)用的整個(gè)生命周期中不能再被改變,從而可以避免編程錯(cuò)誤(比如忘掉初始化這個(gè)字段會(huì)導(dǎo)致編譯報(bào)錯(cuò))。


          三、字段注入實(shí)現(xiàn)的代碼不夠安全


          當(dāng)構(gòu)造器執(zhí)行完畢,對(duì)象就準(zhǔn)備好被使用了。采用構(gòu)造器注入方式,對(duì)象只有準(zhǔn)備好或者沒(méi)有準(zhǔn)備好的狀態(tài),不存在中間態(tài)。但是采用字段注入,導(dǎo)致對(duì)象存在一個(gè)中間狀態(tài),這個(gè)對(duì)象會(huì)比較脆弱。


          四、字段注入方式對(duì)依賴(lài)的表述不夠清晰


          采用構(gòu)造器注入,使得類(lèi)的必要依賴(lài)一目了然。


          五、逼迫開(kāi)發(fā)者思考類(lèi)的設(shè)計(jì)


          如果你的構(gòu)造器里出現(xiàn)了很多參數(shù),就是非常明顯的一個(gè)壞的設(shè)計(jì),實(shí)際上是一種上帝對(duì)象這樣的反模式。不管類(lèi)通過(guò)構(gòu)造器還是字段的方式依賴(lài)多個(gè)其他服務(wù),這都是錯(cuò)的,但是通過(guò)構(gòu)造器注入更能讓人在依賴(lài)變多時(shí)停下來(lái)思考代碼結(jié)構(gòu)的設(shè)計(jì)。


          總結(jié)


          綜上所述,如果非要說(shuō)構(gòu)造器注入有什么不好的地方,那就是增加了實(shí)現(xiàn)上代碼量,因?yàn)樽侄巫⑷胫恍枰獙?xiě)一個(gè)字段,而構(gòu)造器注入既要寫(xiě)構(gòu)造器,還免不了要寫(xiě)字段。但作為 JavaScript 工程師,不得不說(shuō)這是 java 語(yǔ)言本身的問(wèn)題,在 JavaScript 或者 TypeScript 的世界里,采用構(gòu)造器注入,連這個(gè)缺點(diǎn)都沒(méi)有。


          舉個(gè)例子


          如果你使用 NestJs(https://docs.nestjs.com/fundamentals/injection-scopes),那么可以這樣寫(xiě):

          @Injectable()class Service {}

          class A2 { constructor(@Inject(Service) private service) {}}


          注意到在構(gòu)造器里可以直接寫(xiě)上 private,這樣 A2 類(lèi)就自動(dòng)有了 service 這個(gè)私有字段。


          如果你不用 NestJs,那么使用 InversifyJshttps://doc.inversify.cloud/zh_cn/classes_as_id.html)也類(lèi)似:

          @injectable()class Service {...}

          class A2 { constructor(private readonly service: Service) {}}


          最后再次強(qiáng)調(diào),如果你發(fā)現(xiàn)自己的項(xiàng)目中還在用字段注入,趕緊改成構(gòu)造器注入的方式吧!


          瀏覽 30
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国产剧情一区二区av在线观看 | 欧美性爱超碰 | 欧美色图一区在线视频 | 金先生大韩航空模特 | 91av.|