日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

用重构指导Clean Code(二):依恋情结和switch语句

發(fā)布時間:2023/12/4 编程问答 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 用重构指导Clean Code(二):依恋情结和switch语句 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

書接上回,我們繼續(xù)聊如何用重構(gòu)指導Clean Code。

在Clean Code的3.4節(jié)中有這樣一段代碼(代碼清單3-4)。(第3章主要講的是函數(shù),而3.4節(jié)討論的是switch語句。)

public Money calculatePay(Employee e) throws InvalidEmployeeType {switch (e.type) {case COMMISSIONED:return calculateCommissionedPay(e);case HOURLY:return calculateHourlyPay(e);case SALARIED:return calculateSalariedPay(e);default:throw new InvalidEmployeeType(e.type);} }

Bob大叔細數(shù)了這段代碼的幾處問題:

  • 太長,當出現(xiàn)新的Employee類型時,還會變得更長

  • 明顯做了不止一件事

  • 違反了單一權(quán)責原則,因為有好幾個修改它的理由

  • 違反了開放閉合原則,因為每當添加新類型時,就必須修改之

  • 最麻煩的是到處皆有類似結(jié)構(gòu)的函數(shù)

  • 其中1和4說的是一回事兒,每當新增加一種類型的Employee,就必須在這個switch里添加新的case。2和3說的是一回事兒,這個方法為三種類型的Employee計算費用,任何一種類型的計算邏輯發(fā)生變化,都要修改這個計算方法(或者對應的計算方法)。

    對于第5點,Bob大叔的意思是說,calculatePay(Employee e)這個方法所在的類中還會有類似isPayDay(Employee e, Date date)或deliverPay(Employee e, Money pay)這樣的方法,由于Employee具有不同的類型(type),那么這些方法中肯定也會出現(xiàn)類似的switch語句。

    講到這里還是非常清晰的,然而Bob大叔接下來又開始魔幻操作,直接給出了解決方案:

    將switch語句埋到抽象工廠底下,不讓任何人看到。該工廠使用switch語句為Employee的派生物創(chuàng)建適當?shù)膶嶓w,而不同的函數(shù),如calculatePay、isPayday和deliverPay等,則藉由Employee接口多態(tài)地接受派遣。

    哪里來的抽象工廠?Employee怎么就有了派生類?再一看代碼

    public abstract class Employee {public abstract boolean isPayday();public abstract Money calculatePay();public abstract void deliverPay(Money pay); } ----------------- public interface EmployeeFactory { public Employee makeEmployee(EmployeeRecord r) throws InvalidEmployeeType; } ----------------- public class EmployeeFactoryImpl implements EmployeeFactory {public Employee makeEmployee(EmployeeRecord r) throws InvalidEmployeeType {switch (r.type) {case COMMISSIONED:return new CommissionedEmployee(r) ;case HOURLY:return new HourlyEmployee(r);case SALARIED:return new SalariedEmploye(r);default:throw new InvalidEmployeeType(r.type);}} }

    妙啊,6啊,Bob大叔簡直是神啊!

    然而,面對同樣的問題代碼,我們能做出這樣的修改嗎?我們能構(gòu)造出Employee的繼承體系嗎?能抽象出EmployeeFactory嗎?反正對我來說,僅看Clean Code是做不到的。Bob大叔的技炫得神乎其神,但無奈只有結(jié)果,沒有過程,讓初學者不明所以。所以,這個時候就要祭出神器《重構(gòu)》了。

    《重構(gòu)》要求我們在重構(gòu)代碼之前,先識別壞味道。然后針對你想去消除的那個壞味道,按照相應的重構(gòu)手法,一步一步讓這個壞味道消失。我曾經(jīng)在真實的遺留系統(tǒng)中演練過“壞味道驅(qū)動的重構(gòu)“(SDR,Smell Driven Refactoring,這是我發(fā)明的一個詞兒),效果還是不錯的。

    還是上面有問題的代碼,我們看看按照《重構(gòu)》應該怎么重構(gòu):

    public Money calculatePay(Employee e) throws InvalidEmployeeType {switch (e.type) {case COMMISSIONED:return calculateCommissionedPay(e);case HOURLY:return calculateHourlyPay(e);case SALARIED:return calculateSalariedPay(e);default:throw new InvalidEmployeeType(e.type);} }

    這里的壞味道很多,最一目了然的可能就是”Switch Statements(Switch驚悚現(xiàn)身)“,當然還有Bob大叔指出的違反SRP和OCP,但在壞味道中,我們管它叫做”Divergent Change(發(fā)散式變化)“。然而要消除這兩個壞味道都不是那么容易,我只想從最簡單的開始。什么是最簡單的呢?

    首先calculatePay方法有一個Employee類型的參數(shù),除此之外,它沒有使用該方法所在類的任何成員。同理可以推斷該方法所使用的其他三個方法應該也只是使用Employee,不會使用該方法所在類的成員。這時,”Feature Envy(依戀情結(jié))“的壞味道就顯現(xiàn)出來了。

    有一種經(jīng)典氣味是:函數(shù)對某個類的興趣高過對自己所處類的興趣。這種孺慕之情最通常的焦點便是數(shù)據(jù)。無數(shù)次經(jīng)驗里,我們看到某個函數(shù)為了計算某個值,從另一個對象那兒調(diào)用幾乎半打的取值函數(shù)。

    對于該方法,豈止是半打,簡直就是全部。老馬不但指出了問題,還給出了方案:

    療法顯而易見:把這個函數(shù)移至另一個地點。你應該使用"Move Method(搬移函數(shù))把它移到它該去的地方”

    沒什么好說的,挪唄。將calculateCommisstionPay、calculateHourlyPay、calculateSalariedPay方法,以及它自己全部挪到Employee中(常用IDE都支持重構(gòu),這里不再贅述):

    public Money calculatePay() throws InvalidEmployeeType {switch (this.type) {case COMMISSIONED:return this.calculateCommissionedPay();case HOURLY:return this.calculateHourlyPay();case SALARIED:return this.calculateSalariedPay();default:throw new InvalidEmployeeType(this.type);} }

    樣一來,該方法只使用Employee自己的數(shù)據(jù),”依戀情結(jié)“的壞味道不見了。

    是禍躲不過,Switch這個壞味道早晚得面對。

    從本質(zhì)上說,switch語句的問題在于重復。你常會發(fā)現(xiàn)同樣的switch語句散布于不同地點。如果要為它添加一個新的case子句,就必須找到所有switch語句并修改它們。

    這也正是Bob大叔所說的”最麻煩的是到處皆有類似結(jié)構(gòu)的函數(shù)“。好在老馬一如既往給出了解決方案:

    面向?qū)ο笾械亩鄳B(tài)概念可為此帶來優(yōu)雅的解決辦法。

    switch語句常常根據(jù)類型碼進行選擇,你要的是“與該類型碼相關(guān)的函數(shù)或類”,……使用Replace Type Code with Subclasses 或Replace Type Code with State/Strategy。一旦這樣完成繼承結(jié)構(gòu)之后,你就可以運用Replace Conditional with Polymorphism了。

    由于我們已經(jīng)通過消除”依戀情結(jié)“將方法移動到了Employee中,所以接下來只需要從類型碼入手就好了。這里我們選擇使用Replace Type Code with Subclass(以子類取代類型碼)這個重構(gòu)手法。打開《重構(gòu)》相關(guān)章節(jié)一看,給出的例子簡直跟我們這里的一模一樣,兩位大師是商量好的吧?

    abstract int getType(); abstract Money calculatePay();static Employee create(int type) {switch (type) {case COMMISSIONED:return new CommissionedEmployee();case HOURLY:return new HourlyEmployee();case SALARIED:return new SalariedEmployee();default:throw new InvalidEmployeeType(type);} }

    怎么樣?是不是與Bob大叔的幻改異曲同工?(EmployeeRecord之類的屬于細枝末節(jié),不再贅述。)

    除此之外,老馬還給出了消除類型碼的其他方案(Replace Type Code with State/Strategy),以及徹底消除switch的辦法(Replace Conditional with Polymorphism)。

    這樣細致入微的教導,是不是比直接告訴你答案要實用得多?

    事實上對于上例這種有很多壞味道的代碼,你完全可以按個人喜好來選擇不同的重構(gòu)順序,得到的結(jié)果可能千差萬別,也可能殊途同歸,但肯定都算是不錯的設計。

    這就是代碼的魅力。

    總結(jié)

    以上是生活随笔為你收集整理的用重构指导Clean Code(二):依恋情结和switch语句的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。