<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>

          25種代碼壞味道總結(jié)+優(yōu)化示例

          共 14465字,需瀏覽 29分鐘

           ·

          2021-10-30 03:57

          前言

          什么樣的代碼是好代碼呢?好的代碼應(yīng)該命名規(guī)范、可讀性強(qiáng)、擴(kuò)展性強(qiáng)、健壯性......而不好的代碼又有哪些典型特征呢?這25種代碼壞味道大家要注意啦

          1. Duplicated Code (重復(fù)代碼)

          重復(fù)代碼就是不同地點(diǎn),有著相同的程序結(jié)構(gòu)。一般是因?yàn)樾枨蟮容^快,開發(fā)小伙伴擔(dān)心影響已有功能,就復(fù)制粘貼造成的。重復(fù)代碼很難維護(hù)的,如果你要修改其中一段的代碼邏輯,就需要修改多次,很可能出現(xiàn)遺漏的情況。

          如何優(yōu)化重復(fù)代碼呢?分三種情況討論:

          1. 同一個(gè)類的兩個(gè)函數(shù)含有相同的表達(dá)式
          class?A?{
          ????public?void?method1()?{
          ????????doSomething1
          ????????doSomething2
          ????????doSomething3
          ????}
          ????public?void?method2()?{
          ????????doSomething1
          ????????doSomething2
          ????????doSomething4
          ????}
          }

          優(yōu)化手段:可以使用Extract Method(提取公共函數(shù)) 抽出重復(fù)的代碼邏輯,組成一個(gè)公用的方法。

          class?A?{
          ????public?void?method1()?{
          ????????commonMethod();
          ????????doSomething3
          ????}
          ????public?void?method2()?{
          ????????commonMethod();
          ????????doSomething4
          ????}
          ????
          ????public?void?commonMethod(){
          ???????doSomething1
          ???????doSomething2
          ????}
          }
          1. 兩個(gè)互為兄弟的子類內(nèi)含相同的表達(dá)式
          class?A?extend?C?{
          ????public?void?method1()?{
          ????????doSomething1
          ????????doSomething2
          ????????doSomething3
          ????}
          }

          class?B?extend?C?{
          ????public?void?method1()?{
          ????????doSomething1
          ????????doSomething2
          ????????doSomething4
          ????}
          }

          優(yōu)化手段:對兩個(gè)類都使用Extract Method(提取公共函數(shù)),然后把抽取出來的函數(shù)放到父類中。

          class?C?{
          ????public?void?commonMethod(){
          ?????doSomething1
          ?????doSomething2
          ???}
          }
          class?A?extend?C?{
          ????public?void?method1()?{
          ????????commonMethod();
          ????????doSomething3
          ????}
          }

          class?B?extend?C?{
          ????public?void?method1()?{
          ????????commonMethod();
          ????????doSomething4
          ????}
          }
          1. 兩個(gè)毫不相關(guān)的類出現(xiàn)重復(fù)代碼

          如果是兩個(gè)毫不相關(guān)的類出現(xiàn)重復(fù)代碼,可以使用Extract Class將重復(fù)代碼提煉到一個(gè)類中。這個(gè)新類可以是一個(gè)普通類,也可以是一個(gè)工具類,看具體業(yè)務(wù)怎么劃分吧。

          2 .Long Method (長函數(shù))

          長函數(shù)是指一個(gè)函數(shù)方法幾百行甚至上千行,可讀性大大降低,不便于理解。反例如下:

          public?class?Test?{
          ????private?String?name;
          ????private?Vector?orders?=?new?Vector();

          ????public?void?printOwing()?{
          ????????//print?banner
          ????????System.out.println("****************");
          ????????System.out.println("*****customer?Owes?*****");
          ????????System.out.println("****************");

          ????????//calculate?totalAmount
          ????????Enumeration?env?=?orders.elements();
          ????????double?totalAmount?=?0.0;
          ????????while?(env.hasMoreElements())?{
          ????????????Order?order?=?(Order)?env.nextElement();
          ????????????totalAmount?+=?order.getAmout();
          ????????}

          ????????//print?details
          ????????System.out.println("name:"?+?name);
          ????????System.out.println("amount:"?+?totalAmount);
          ????????......
          ????}
          }

          可以使用Extract Method,抽取功能單一的代碼段,組成命名清晰的小函數(shù),去解決長函數(shù)問題,正例如下:

          public?class?Test?{
          ????private?String?name;
          ????private?Vector?orders?=?new?Vector();

          ????public?void?printOwing()?{

          ????????//print?banner
          ????????printBanner();
          ????????//calculate?totalAmount
          ????????double?totalAmount?=?getTotalAmount();
          ????????//print?details
          ????????printDetail(totalAmount);
          ????}

          ????void?printBanner(){
          ????????System.out.println("****************");
          ????????System.out.println("*****customer?Owes?*****");
          ????????System.out.println("****************");
          ????}

          ????double?getTotalAmount(){
          ????????Enumeration?env?=?orders.elements();
          ????????double?totalAmount?=?0.0;
          ????????while?(env.hasMoreElements())?{
          ????????????Order?order?=?(Order)?env.nextElement();
          ????????????totalAmount?+=?order.getAmout();
          ????????}
          ????????return?totalAmount;
          ????}

          ????void?printDetail(double?totalAmount){
          ????????System.out.println("name:"?+?name);
          ????????System.out.println("amount:"?+?totalAmount);
          ????}
          ????
          }

          3. ?Large Class (過大的類)

          一個(gè)類做太多事情,維護(hù)了太多功能,可讀性變差,性能也會(huì)下降。舉個(gè)例子,訂單相關(guān)的功能你放到一個(gè)類A里面,商品庫存相關(guān)的也放在類A里面,積分相關(guān)的還放在類A里面...反例如下:

          Class?A{
          ??public?void?printOrder(){
          ???System.out.println("訂單");
          ??}
          ??
          ??public?void?printGoods(){
          ???System.out.println("商品");
          ??}
          ??
          ??public?void?printPoints(){
          ???System.out.println("積分");
          ??}
          }

          試想一下,亂七八糟的代碼塊都往一個(gè)類里面塞,還談啥可讀性。應(yīng)該按單一職責(zé),使用Extract Class把代碼劃分開,正例如下:

          Class?Order{
          ??public?void?printOrder(){
          ???System.out.println("訂單");
          ??}
          }

          Class?Goods{
          ???public?void?printGoods(){
          ???System.out.println("商品");
          ??}
          }
          ?
          Class?Points{???
          ??public?void?printPoints(){
          ???System.out.println("積分");
          ??}
          ?}
          }

          4. Long Parameter List (過長參數(shù)列)

          方法參數(shù)數(shù)量過多的話,可讀性很差。如果有多個(gè)重載方法,參數(shù)很多的話,有時(shí)候你都不知道調(diào)哪個(gè)呢。并且,如果參數(shù)很多,做新老接口兼容處理也比較麻煩。

          public?void?getUserInfo(String?name,String?age,String?sex,String?mobile){
          ??//?do?something?...
          }

          如何解決過長參數(shù)列問題呢?將參數(shù)封裝成結(jié)構(gòu)或者類,比如我們將參數(shù)封裝成一個(gè)DTO類,如下:

          public?void?getUserInfo(UserInfoParamDTO?userInfoParamDTO){
          ??//?do?something?...
          }

          class?UserInfoParamDTO{
          ??private?String?name;
          ??private?String?age;?
          ??private?String?sex;
          ??private?String?mobile;
          }

          5. Divergent Change (發(fā)散式變化)

          對程序進(jìn)行維護(hù)時(shí), 如果添加修改組件, 要同時(shí)修改一個(gè)類中的多個(gè)方法, 那么這就是 Divergent Change。舉個(gè)汽車的例子,某個(gè)汽車廠商生產(chǎn)三種品牌的汽車:BMW、Benz和LaoSiLaiSi,每種品牌又可以選擇燃油、純電和混合動(dòng)力。反例如下

          /**
          ?*??公眾號:撿田螺的小男孩
          ?*/
          public?class?Car?{

          ????private?String?name;

          ????void?start(Engine?engine)?{
          ????????if?("HybridEngine".equals(engine.getName()))?{
          ????????????System.out.println("Start?Hybrid?Engine...");
          ????????}?else?if?("GasolineEngine".equals(engine.getName()))?{
          ????????????System.out.println("Start?Gasoline?Engine...");
          ????????}?else?if?("ElectricEngine".equals(engine.getName()))?{
          ????????????System.out.println("Start?Electric?Engine");
          ????????}
          ????}

          ????void?drive(Engine?engine,Car?car)?{
          ????????this.start(engine);
          ????????System.out.println("Drive?"?+?getBrand(car)?+?"?car...");
          ????}

          ????String?getBrand(Car?car)?{
          ????????if?("Baoma".equals(car.getName()))?{
          ????????????return?"BMW";
          ????????}?else?if?("BenChi".equals(car.getName()))?{
          ????????????return?"Benz";
          ????????}?else?if?("LaoSiLaiSi".equals(car.getName()))?{
          ????????????return?"LaoSiLaiSi";
          ????????}
          ????????return?null;
          ????}
          ?}

          如果新增一種品牌新能源電車,然后它的啟動(dòng)引擎是核動(dòng)力呢,那么就需要修改Car類的startgetBrand方法啦,這就是代碼壞味道:Divergent Change (發(fā)散式變化)。

          如何優(yōu)化呢?一句話總結(jié):拆分類,將總是一起變化的東西放到一塊

          • 運(yùn)用提煉類(Extract Class) 拆分類的行為。
          • 如果不同的類有相同的行為,提煉超類(Extract Superclass) 和 提煉子類(Extract Subclass)。

          正例如下:

          因?yàn)镋ngine是獨(dú)立變化的,所以提取一個(gè)Engine接口,如果新加一個(gè)啟動(dòng)引擎,多一個(gè)實(shí)現(xiàn)類即可。如下:

          //IEngine
          public?interface?IEngine?{
          ????void?start();
          }

          public?class?HybridEngineImpl?implements?IEngine?{?
          ????@Override
          ????public?void?start()?{
          ????????System.out.println("Start?Hybrid?Engine...");
          ????}
          }

          因?yàn)?code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;">drive方法依賴于Car,IEngine,getBand方法;getBand方法是變化的,也跟Car是有關(guān)聯(lián)的,所以可以搞個(gè)抽象Car的類,每個(gè)品牌汽車?yán)^承于它即可,如下

          public?abstract?class?AbstractCar?{

          ????protected?IEngine?engine;

          ????public?AbstractCar(IEngine?engine)?{
          ????????this.engine?=?engine;
          ????}

          ????public?abstract?void?drive();
          }

          //奔馳汽車
          public?class?BenzCar?extends?AbstractCar?{

          ????public?BenzCar(IEngine?engine)?{
          ????????super(engine);
          ????}

          ????@Override
          ????public?void?drive()?{
          ??????this.engine.start();
          ??????System.out.println("Drive?"?+?getBrand()?+?"?car...");
          ????}

          ????private?String?getBrand()?{
          ????????return?"Benz";
          ????}
          }

          //寶馬汽車
          public?class?BaoMaCar?extends?AbstractCar?{

          ????public?BaoMaCar(IEngine?engine)?{
          ????????super(engine);
          ????}

          ????@Override
          ????public?void?drive()?{
          ????????this.engine.start();
          ????????System.out.println("Drive?"?+?getBrand()?+?"?car...");
          ????}

          ????private?String?getBrand()?{
          ????????return?"BMW";
          ????}
          }

          細(xì)心的小伙伴,可以發(fā)現(xiàn)不同子類BaoMaCar和BenzCar的drive方法,還是有相同代碼,所以我們可以再擴(kuò)展一個(gè)抽象子類,把drive方法推進(jìn)去,如下:

          public?abstract?class?AbstractRefinedCar?extends?AbstractCar?{

          ????public?AbstractRefinedCar(IEngine?engine)?{
          ????????super(engine);
          ????}

          ????@Override
          ????public?void?drive()?{
          ????????this.engine.start();
          ????????System.out.println("Drive?"?+?getBrand()?+?"?car...");
          ????}

          ????abstract?String?getBrand();
          }

          //寶馬
          public?class?BaoMaRefinedCar?extends?AbstractRefinedCar?{

          ????public?BaoMaRefinedCar(IEngine?engine)?{
          ????????super(engine);
          ????}

          ????@Override
          ????String?getBrand()?{
          ????????return??"BMW";
          ????}
          }

          如果再添加一個(gè)新品牌,搞個(gè)子類,繼承AbstractRefinedCar即可,如果新增一種啟動(dòng)引擎,也是搞個(gè)類實(shí)現(xiàn)IEngine接口即可

          6. Shotgun Surgery(散彈式修改)

          當(dāng)你實(shí)現(xiàn)某個(gè)小功能時(shí),你需要在很多不同的類做出小修改。這就是Shotgun Surgery(散彈式修改)。它跟發(fā)散式變化(Divergent Change) 的區(qū)別就是,它指的是同時(shí)對多個(gè)類進(jìn)行單一的修改,發(fā)散式變化指在一個(gè)類中修改多處。反例如下

          public?class?DbAUtils?{
          ????@Value("${db.mysql.url}")
          ????private?String?mysqlDbUrl;
          ????...
          }

          public?class?DbBUtils?{
          ????@Value("${db.mysql.url}")
          ????private?String?mysqlDbUrl;
          ????...
          }

          多個(gè)類使用了db.mysql.url這個(gè)變量,如果將來需要切換mysql到別的數(shù)據(jù)庫,如Oracle,那就需要修改多個(gè)類的這個(gè)變量!

          如何優(yōu)化呢?將各個(gè)修改點(diǎn),集中到一起,抽象成一個(gè)新類。

          可以使用 Move Method (搬移函數(shù))和 Move Field (搬移字段)把所有需要修改的代碼放進(jìn)同一個(gè)類,如果沒有合適的類,就去new一個(gè)。

          正例如下:

          public?class?DbUtils?{
          ????@Value("${db.mysql.url}")
          ????private?String?mysqlDbUrl;
          ????...
          }

          7. Feature Envy (依戀情節(jié))

          某個(gè)函數(shù)為了計(jì)算某個(gè)值,從另一個(gè)對象那里調(diào)用幾乎半打的取值函數(shù)。通俗點(diǎn)講,就是一個(gè)函數(shù)使用了大量其他類的成員,有人稱之為紅杏出墻的函數(shù)。反例如下:

          public?class?User{
          ?private?Phone?phone;
          ??public?User(Phone?phone){
          ????????this.phone?=?phone;
          ????}
          ????public?void?getFullPhoneNumber(Phone?phone){
          ????????System.out.println("areaCode:"?+?phone.getAreaCode());
          ????????System.out.println("prefix:"?+?phone.getPrefix());
          ????????System.out.println("number:"?+?phone.getNumber());
          ????}
          }

          如何解決呢?在這種情況下,你可以考慮將這個(gè)方法移動(dòng)到它使用的那個(gè)類中。例如,要將 getFullPhoneNumber()User 類移動(dòng)到Phone類中,因?yàn)樗{(diào)用了Phone類的很多方法。

          8. Data Clumps(數(shù)據(jù)泥團(tuán))

          數(shù)據(jù)項(xiàng)就像小孩子,喜歡成群結(jié)隊(duì)地呆在一塊。如果一些數(shù)據(jù)項(xiàng)總是一起出現(xiàn)的,并且一起出現(xiàn)更有意義的,就可以考慮,按數(shù)據(jù)的業(yè)務(wù)含義來封裝成數(shù)據(jù)對象。反例如下:

          public?class?User?{

          ????private?String?firstName;
          ????private?String?lastName;

          ????private?String?province;
          ????private?String?city;
          ????private?String?area;
          ????private?String?street;
          }

          正例:

          public?class?User?{

          ????private?UserName?username;
          ????private?Adress?adress;
          }

          class?UserName{
          ????private?String?firstName;
          ????private?String?lastName;
          }
          class?Address{
          ????private?String?province;
          ????private?String?city;
          ????private?String?area;
          ????private?String?street;
          }

          9. Primitive Obsession (基本類型偏執(zhí))

          多數(shù)編程環(huán)境都有兩種數(shù)據(jù)類型,結(jié)構(gòu)類型和基本類型。這里的基本類型,如果指Java語言的話,不僅僅包括那八大基本類型哈,也包括String等。如果是經(jīng)常一起出現(xiàn)的基本類型,可以考慮把它們封裝成對象。我個(gè)人覺得它有點(diǎn)像Data Clumps(數(shù)據(jù)泥團(tuán)) 舉個(gè)反例如下:

          //?訂單
          public?class?Order?{
          ????private?String?customName;
          ????private?String?address;
          ????private?Integer?orderId;
          ????private?Integer?price;
          }

          正例:

          //?訂單類
          public?class?Order?{
          ????private?Custom?custom;
          ????private?Integer?orderId;
          ????private?Integer?price;
          }
          //?把custom相關(guān)字段封裝起來,在Order中引用Custom對象
          public?class?Custom?{
          ????private?String?name;
          ????private?String?address;
          }

          當(dāng)然,這里不是所有的基本類型,都建議封裝成對象,有關(guān)聯(lián)或者一起出現(xiàn)的,才這么建議哈。

          10. Switch Statements (Switch 語句)

          這里的Switch語句,不僅包括Switch相關(guān)的語句,也包括多層if...else的語句哈。很多時(shí)候,switch語句的問題就在于重復(fù),如果你為它添加一個(gè)新的case語句,就必須找到所有的switch語句并且修改它們。

          示例代碼如下:

          ????String?medalType?=?"guest";
          ????if?("guest".equals(medalType))?{
          ????????System.out.println("嘉賓勛章");
          ?????}?else?if?("vip".equals(medalType))?{
          ????????System.out.println("會(huì)員勛章");
          ????}?else?if?("guard".equals(medalType))?{
          ????????System.out.println("守護(hù)勛章");
          ????}
          ????...

          這種場景可以考慮使用多態(tài)優(yōu)化:

          //勛章接口
          public?interface?IMedalService?{
          ????void?showMedal();
          }

          //守護(hù)勛章策略實(shí)現(xiàn)類
          public?class?GuardMedalServiceImpl?implements?IMedalService?{
          ????@Override
          ????public?void?showMedal()?{
          ????????System.out.println("展示守護(hù)勛章");
          ????}
          }
          //嘉賓勛章策略實(shí)現(xiàn)類
          public?class?GuestMedalServiceImpl?implements?IMedalService?{
          ????@Override
          ????public?void?showMedal()?{
          ????????System.out.println("嘉賓勛章");
          ????}
          }

          //勛章服務(wù)工廠類
          public?class?MedalServicesFactory?{

          ????private?static?final?Map?map?=?new?HashMap<>();
          ????static?{
          ????????map.put("guard",?new?GuardMedalServiceImpl());
          ????????map.put("vip",?new?VipMedalServiceImpl());
          ????????map.put("guest",?new?GuestMedalServiceImpl());
          ????}
          ????public?static?IMedalService?getMedalService(String?medalType)?{
          ????????return?map.get(medalType);
          ????}
          }

          當(dāng)然,多態(tài)只是優(yōu)化的一個(gè)方案,一個(gè)方向。如果只是單一函數(shù)有些簡單選擇示例,并不建議動(dòng)不動(dòng)就使用動(dòng)態(tài),因?yàn)轱@得有點(diǎn)殺雞使用牛刀了。

          11.Parallel Inheritance Hierarchies( 平行繼承體系)

          平行繼承體系 其實(shí)算是Shotgun Surgery的特殊情況啦。當(dāng)你為A類的一個(gè)子類Ax,也必須為另一個(gè)類B相應(yīng)的增加一個(gè)子類Bx。

          解決方法:遇到這種情況,就要消除兩個(gè)繼承體系之間的引用,有一個(gè)類是可以去掉繼承關(guān)系的。

          12. Lazy Class (冗贅類)

          把這些不再重要的類里面的邏輯,合并到相關(guān)類,刪掉舊的。一個(gè)比較常見的場景就是,假設(shè)系統(tǒng)已經(jīng)有日期工具類DateUtils,有些小伙伴在開發(fā)中,需要用到日期轉(zhuǎn)化等,不管三七二十一,又自己實(shí)現(xiàn)一個(gè)新的日期工具類。

          13. Speculative Generality(夸夸其談未來性)

          盡量避免過度設(shè)計(jì)的代碼。例如:

          • 只有一個(gè)if else,那就不需要班門弄斧使用多態(tài);
          • 如果某個(gè)抽象類沒有什么太大的作用,就運(yùn)用Collapse Hierarchy(折疊繼承體系)
          • 如果函數(shù)的某些參數(shù)沒用上,就移除。

          14. Temporary Field(令人迷惑的臨時(shí)字段)

          某個(gè)實(shí)例變量僅為某種特定情況而定而設(shè),這樣的代碼就讓人不易理解,我們稱之為 Temporary Field(令人迷惑的臨時(shí)字段)。反例如下:

          public?class?PhoneAccount?{

          ????private?double?excessMinutesCharge;
          ????private?static?final?double?RATE?=?8.0;

          ????public?double?computeBill(int?minutesUsed,?int?includedMinutes)?{
          ????????excessMinutesCharge?=?0.0;
          ????????int?excessMinutes?=?minutesUsed?-?includedMinutes;
          ????????if?(excessMinutes?>=?1)?{
          ????????????excessMinutesCharge?=?excessMinutes?*?RATE;
          ????????}
          ????????return?excessMinutesCharge;
          ????}

          ????public?double?chargeForExcessMinutes(int?minutesUsed,?int?includedMinutes)?{
          ????????computeBill(minutesUsed,?includedMinutes);
          ????????return?excessMinutesCharge;
          ????}
          }

          思考一下,臨時(shí)字段excessMinutesCharge是否多余呢?

          15. Message Chains (過度耦合的消息鏈)

          當(dāng)你看到用戶向一個(gè)對象請求另一個(gè)對象,然后再向后者請求另一個(gè)對象,然后再請求另一個(gè)對象...這就是消息鏈。實(shí)際代碼中,你看到的可能是一長串getThis()或一長串臨時(shí)變量。反例如下:

          A.getB().getC().getD().getTianLuoBoy().getData();

          A想要獲取需要的數(shù)據(jù)時(shí),必須要知道B,又必須知道C,又必須知道D...其實(shí)A需要知道得太多啦,回頭想下封裝性,嘻嘻。其實(shí)可以通過拆函數(shù)或者移動(dòng)函數(shù)解決,比如由B作為代理,搞個(gè)函數(shù)直接返回A需要數(shù)據(jù)。

          16. Middle Man (中間人)

          對象的基本特征之一就是封裝,即對外部世界隱藏其內(nèi)部細(xì)節(jié)。封裝往往伴隨委托,過度運(yùn)用委托就不好:某個(gè)類接口有一半的函數(shù)都委托給其他類。可以使用Remove Middle Man優(yōu)化。反例如下:

          A.B.getC(){
          ???return?C.getC();
          }

          其實(shí),A可以直接通過C去獲取C,而不需要通過B去獲取。

          17. Inappropriate Intimacy(狎昵關(guān)系)

          如果兩個(gè)類過于親密,過分狎昵,你中有我,我中有你,兩個(gè)類彼此使用對方的私有的東西,就是一種壞代碼味道。我們稱之為Inappropriate Intimacy(狎昵關(guān)系)

          建議盡量把有關(guān)聯(lián)的方法或?qū)傩猿殡x出來,放到公共類,以減少關(guān)聯(lián)。

          18. Alternative Classes with Different Interfaces (異曲同工的類)

          A類的接口a,和B類的接口b,做的的是相同一件事,或者類似的事情。我們就把A和B叫做異曲同工的類。

          可以通過重命名,移動(dòng)函數(shù),或抽象子類等方式優(yōu)化

          19. Incomplete Library Class (不完美的類庫)

          大多數(shù)對象只要夠用就好,如果類庫構(gòu)造得不夠好,我們不可能修改其中的類使它完成我們希望完成的工作??梢葬u紫:包一層函數(shù)或包成新的類。

          20. Data Class (純數(shù)據(jù)類)

          什么是Data Class? 它們擁有一些字段,以及用于訪問(讀寫)這些字段的函數(shù)。這些類很簡單,僅有公共成員變量,或簡單操作的函數(shù)。

          如何優(yōu)化呢?將相關(guān)操作封裝進(jìn)去,減少public成員變量。比如:

          • 如果擁有public字段-> Encapsulate Field
          • 如果這些類內(nèi)含容器類的字段,應(yīng)該檢查它們是不是得到了恰當(dāng)?shù)胤庋b-> Encapsulate Collection封裝起來
          • 對于不該被其他類修改的字段-> Remove Setting Method->找出取值/設(shè)置函數(shù)被其他類運(yùn)用的地點(diǎn)-> Move Method 把這些調(diào)用行為搬移到Data Class來。如果無法搬移整個(gè)函數(shù),就運(yùn)用Extract Method產(chǎn)生一個(gè)可被搬移的函數(shù)->Hide Method把這些取值/設(shè)置函數(shù)隱藏起來。

          21. Refused Bequest (被拒絕的饋贈(zèng))

          子類應(yīng)該繼承父類的數(shù)據(jù)和函數(shù)。子類繼承得到所有函數(shù)和數(shù)據(jù),卻只使用了幾個(gè),那就是繼承體系設(shè)計(jì)錯(cuò)誤,需要優(yōu)化。

          • 需要為這個(gè)子類新建一個(gè)兄弟類->Push Down MethodPush Down Field把所有用不到的函數(shù)下推給兄弟類,這樣一來,超類就只持有所有子類共享的東西。所有超類都應(yīng)該是抽象的。
          • 如果子類復(fù)用了超類的實(shí)現(xiàn),又不愿意支持超類的接口,可以不以為然。但是不能胡亂修改繼承體系->Replace Inheritance with Delegation(用委派替換繼承).

          22. Comments (過多的注釋)

          這個(gè)點(diǎn)不是說代碼不建議寫注釋哦,而是,建議大家避免用注釋解釋代碼,避免過多的注釋。這些都是常見注釋的壞味道:

          • 多余的解釋
          • 日志式注釋
          • 用注釋解釋變量等
          • ...

          如何優(yōu)化呢?

          • 方法函數(shù)、變量的命名要規(guī)范、淺顯易懂、避免用注釋解釋代碼。
          • 關(guān)鍵、復(fù)雜的業(yè)務(wù),使用清晰、簡明的注釋

          23. 神奇命名

          方法函數(shù)、變量、類名、模塊等,都需要簡單明了,淺顯易懂。避免靠自己主觀意識(shí)瞎起名字。

          反例:

          boolean?test?=?chenkParamResult(req);

          正例:

          boolean?isParamPass?=?chenkParamResult(req);

          24. 神奇魔法數(shù)

          日常開發(fā)中,經(jīng)常會(huì)遇到這種代碼:

          if(userType==1){
          ???//doSth1
          }else?If(?userType?==2){
          ???//doSth2
          }
          ...

          代碼中的這個(gè)1和2都表示什么意思呢?再比如setStatus(1)中的1又表示什么意思呢?看到類似壞代碼,可以這兩種方式優(yōu)化:

          • 新建個(gè)常量類,把一些常量放進(jìn)去,統(tǒng)一管理,并且寫好注釋;
          • 建一個(gè)枚舉類,把相關(guān)的魔法數(shù)字放到一起管理。

          25. 混亂的代碼層次調(diào)用

          我們代碼一般會(huì)分dao層service層controller層。

          • dao層主要做數(shù)據(jù)持久層的工作,與數(shù)據(jù)庫打交道。
          • service層主要負(fù)責(zé)業(yè)務(wù)邏輯處理。
          • controller層負(fù)責(zé)具體的業(yè)務(wù)模塊流程的控制。

          所以一般就是controller調(diào)用serviceservice調(diào)dao。如果你在代碼看到controller直接調(diào)用dao,那可以考慮是否優(yōu)化啦。反例如下

          @RestController("user")
          public?class?UserController?{

          ????Autowired
          ????private?UserDao?userDao;

          ????@RequestMapping("/queryUserInfo")
          ????public?String?queryUserInfo(String?userName)?{
          ????????return?userDao.selectByUserName(userName);
          ????}
          }

          參考與感謝

          • 軟工實(shí)驗(yàn):常見的代碼壞味道以及重構(gòu)舉例[2]
          • 22種代碼的壞味道,一句話概括[3]
          • 【重構(gòu)】 代碼的壞味道總結(jié) Bad Smell[4]
          • Code Smell[5]
          • 《重構(gòu)改善既有代碼的設(shè)計(jì)》


          推薦閱讀:

          騰訊二面:Redis 事務(wù)支持 ACID 么?

          讀懂Redis源碼,我總結(jié)了這7點(diǎn)心得
          緩存和數(shù)據(jù)庫一致性問題,看這篇就夠了
          搞懂異地多活,看這篇就夠了
          聊聊分布式鎖——Redis和Redisson的方式
          看一遍就理解:MVCC原理詳解

          關(guān)互聯(lián)網(wǎng)全棧架構(gòu),價(jià)

          瀏覽 1202
          1點(diǎn)贊
          評論
          1收藏
          分享

          手機(jī)掃一掃分享

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

          手機(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>
                  色婷婷激情视频 | 色婷婷成人做爱a片免费看网站 | 堆女郎松果浴室自慰正在播放 | 五月丁香六 | 无码免费一区二区三区在线 |