日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問(wèn)題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷(xiāo)解決方案
25種代碼壞味道總結(jié)+優(yōu)化示例

 前言

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

創(chuàng)新互聯(lián)建站專注于芒市企業(yè)網(wǎng)站建設(shè),響應(yīng)式網(wǎng)站建設(shè),商城建設(shè)。芒市網(wǎng)站建設(shè)公司,為芒市等地區(qū)提供建站服務(wù)。全流程按需制作網(wǎng)站,專業(yè)設(shè)計(jì),全程項(xiàng)目跟蹤,創(chuàng)新互聯(lián)建站專業(yè)和態(tài)度為您提供的服務(wù)

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

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

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

同一個(gè)類(lèi)的兩個(gè)函數(shù)含有相同的表達(dá)式

 
 
 
 
  1. class A {  
  2.     public void method1() {  
  3.         doSomething1  
  4.         doSomething2  
  5.         doSomething3  
  6.     }  
  7.     public void method2() {  
  8.         doSomething1  
  9.         doSomething2  
  10.         doSomething4  
  11.     }  

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

 
 
 
 
  1. class A {  
  2.     public void method1() {  
  3.         commonMethod();  
  4.         doSomething3  
  5.     }  
  6.     public void method2() {  
  7.         commonMethod();  
  8.         doSomething4  
  9.     }   
  10.     public void commonMethod(){  
  11.        doSomething1  
  12.        doSomething2  
  13.     }  

兩個(gè)互為兄弟的子類(lèi)內(nèi)含相同的表達(dá)式

 
 
 
 
  1. class A extend C {  
  2.     public void method1() {  
  3.         doSomething1  
  4.         doSomething2  
  5.         doSomething3  
  6.     }  
  7. }  
  8. class B extend C {  
  9.     public void method1() {  
  10.         doSomething1  
  11.         doSomething2  
  12.         doSomething4  
  13.     }  

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

 
 
 
 
  1. class C {  
  2.     public void commonMethod(){  
  3.      doSomething1  
  4.      doSomething2  
  5.    }  
  6.  class A extend C {  
  7.     public void method1() {  
  8.         commonMethod();  
  9.         doSomething3  
  10.     }  
  11. }  
  12. class B extend C {  
  13.     public void method1() {  
  14.         commonMethod();  
  15.         doSomething4  
  16.     }  

兩個(gè)毫不相關(guān)的類(lèi)出現(xiàn)重復(fù)代碼

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

2 .Long Method (長(zhǎng)函數(shù))

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

 
 
 
 
  1. public class Test {  
  2.     private String name;  
  3.     private Vector orders = new Vector();  
  4.     public void printOwing() {  
  5.         //print banner  
  6.         System.out.println("****************");  
  7.         System.out.println("*****customer Owes *****");  
  8.         System.out.println("****************");  
  9.         //calculate totalAmount  
  10.         Enumeration env = orders.elements();  
  11.         double totalAmount = 0.0;  
  12.         while (env.hasMoreElements()) {  
  13.             Order order = (Order) env.nextElement();  
  14.             totalAmount += order.getAmout();  
  15.         }  
  16.         //print details  
  17.         System.out.println("name:" + name);  
  18.         System.out.println("amount:" + totalAmount);  
  19.         ...... 
  20.     }  

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

 
 
 
 
  1. public class Test {  
  2.     private String name;  
  3.     private Vector orders = new Vector();  
  4.     public void printOwing() {  
  5.         //print banner  
  6.         printBanner();  
  7.         //calculate totalAmount  
  8.         double totalAmount = getTotalAmount();  
  9.         //print details  
  10.         printDetail(totalAmount);  
  11.     }  
  12.     void printBanner(){  
  13.         System.out.println("****************");  
  14.         System.out.println("*****customer Owes *****");  
  15.         System.out.println("****************");  
  16.     }  
  17.     double getTotalAmount(){  
  18.         Enumeration env = orders.elements();  
  19.         double totalAmount = 0.0;  
  20.         while (env.hasMoreElements()) {  
  21.             Order order = (Order) env.nextElement();  
  22.             totalAmount += order.getAmout();  
  23.         }  
  24.         return totalAmount;  
  25.     }  
  26.     void printDetail(double totalAmount){  
  27.         System.out.println("name:" + name);  
  28.         System.out.println("amount:" + totalAmount);  
  29.     }   

3.  Large Class (過(guò)大的類(lèi))

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

 
 
 
 
  1. Class A{  
  2.   public void printOrder(){  
  3.    System.out.println("訂單");  
  4.   }    
  5.   public void printGoods(){  
  6.    System.out.println("商品");  
  7.   }   
  8.   public void printPoints(){  
  9.    System.out.println("積分");  
  10.   }  

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

 
 
 
 
  1. Class Order{  
  2.   public void printOrder(){  
  3.    System.out.println("訂單");  
  4.   }  
  5. }  
  6. Class Goods{  
  7.    public void printGoods(){  
  8.    System.out.println("商品");  
  9.   }  
  10. }   
  11. Class Points{     
  12.   public void printPoints(){  
  13.    System.out.println("積分");  
  14.   }  
  15.  }  

4. Long Parameter List (過(guò)長(zhǎng)參數(shù)列)

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

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

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

 
 
 
 
  1. public void getUserInfo(UserInfoParamDTO userInfoParamDTO){  
  2.   // do something ...  
  3. }  
  4. class UserInfoParamDTO{  
  5.   private String name;  
  6.   private String age;  
  7.   private String sex;  
  8.   private String mobile;  

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

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

 
 
 
 
  1. /**  
  2.  *  公眾號(hào):撿田螺的小男孩  
  3.  */  
  4. public class Car {  
  5.     private String name;  
  6.     void start(Engine engine) {  
  7.         if ("HybridEngine".equals(engine.getName())) {  
  8.             System.out.println("Start Hybrid Engine..."); 
  9.          } else if ("GasolineEngine".equals(engine.getName())) {  
  10.             System.out.println("Start Gasoline Engine...");  
  11.         } else if ("ElectricEngine".equals(engine.getName())) {  
  12.             System.out.println("Start Electric Engine");  
  13.         }  
  14.     }  
  15.     void drive(Engine engine,Car car) {  
  16.         this.start(engine);  
  17.         System.out.println("Drive " + getBrand(car) + " car...");  
  18.     }  
  19.     String getBrand(Car car) {  
  20.         if ("Baoma".equals(car.getName())) {  
  21.             return "BMW";  
  22.         } else if ("BenChi".equals(car.getName())) {  
  23.             return "Benz";  
  24.         } else if ("LaoSiLaiSi".equals(car.getName())) {  
  25.             return "LaoSiLaiSi";  
  26.         }  
  27.         return null; 
  28.      }  
  29.  } 

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

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

            運(yùn)用提煉類(lèi)(Extract Class) 拆分類(lèi)的行為。

               如果不同的類(lèi)有相同的行為,提煉超類(lèi)(Extract Superclass) 和 提煉子類(lèi)(Extract Subclass)。    ”

正例如下:

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

 
 
 
 
  1. //IEngine  
  2. public interface IEngine {  
  3.     void start();  
  4. }  
  5. public class HybridEngineImpl implements IEngine {   
  6.     @Override  
  7.     public void start() {  
  8.         System.out.println("Start Hybrid Engine...");  
  9.     }  

因?yàn)閐rive方法依賴于Car,IEngine,getBand方法;getBand方法是變化的,也跟Car是有關(guān)聯(lián)的,所以可以搞個(gè)抽象Car的類(lèi),每個(gè)品牌汽車(chē)?yán)^承于它即可,如下

 
 
 
 
  1. public abstract class AbstractCar {  
  2.     protected IEngine engine;  
  3.     public AbstractCar(IEngine engine) {  
  4.         this.engine = engine;  
  5.     }  
  6.     public abstract void drive();  
  7. }  
  8. //奔馳汽車(chē)  
  9. public class BenzCar extends AbstractCar {  
  10.     public BenzCar(IEngine engine) { 
  11.          super(engine);  
  12.     }  
  13.     @Override  
  14.     public void drive() {  
  15.       this.engine.start();  
  16.       System.out.println("Drive " + getBrand() + " car..."); 
  17.     }  
  18.     private String getBrand() {  
  19.         return "Benz";  
  20.     }  
  21. }  
  22. //寶馬汽車(chē)  
  23. public class BaoMaCar extends AbstractCar {  
  24.     public BaoMaCar(IEngine engine) {  
  25.         super(engine);  
  26.     }  
  27.     @Override  
  28.     public void drive() {  
  29.         this.engine.start();  
  30.         System.out.println("Drive " + getBrand() + " car...");  
  31.     }  
  32.     private String getBrand() {  
  33.         return "BMW";  
  34.     }  

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

 
 
 
 
  1. public abstract class AbstractRefinedCar extends AbstractCar {  
  2.     public AbstractRefinedCar(IEngine engine) {  
  3.         super(engine);  
  4.     }  
  5.     @Override  
  6.     public void drive() {  
  7.         this.engine.start();  
  8.         System.out.println("Drive " + getBrand() + " car...");  
  9.     }  
  10.     abstract String getBrand();  
  11. }  
  12. //寶馬  
  13. public class BaoMaRefinedCar extends AbstractRefinedCar {  
  14.     public BaoMaRefinedCar(IEngine engine) {  
  15.         super(engine);  
  16.     }  
  17.     @Override  
  18.     String getBrand() {  
  19.         return  "BMW";  
  20.     }  

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

6. Shotgun Surgery(散彈式修改)

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

 
 
 
 
  1. public class DbAUtils {  
  2.     @Value("${db.mysql.url}")  
  3.     private String mysqlDbUrl;  
  4.     ...  
  5. }  
  6. public class DbBUtils {  
  7.     @Value("${db.mysql.url}")  
  8.     private String mysqlDbUrl;  
  9.     ...  

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

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

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

正例如下:

 
 
 
 
  1. public class DbUtils {  
  2.     @Value("${db.mysql.url}")  
  3.     private String mysqlDbUrl;  
  4.     ...  

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

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

 
 
 
 
  1. public class User{  
  2.  private Phone phone;  
  3.   public User(Phone phone){  
  4.         this.phone = phone;  
  5.     }  
  6.     public void getFullPhoneNumber(Phone phone){  
  7.         System.out.println("areaCode:" + phone.getAreaCode());  
  8.         System.out.println("prefix:" + phone.getPrefix());  
  9.         System.out.println("number:" + phone.getNumber());  
  10.     }  

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

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ù)含義來(lái)封裝成數(shù)據(jù)對(duì)象。反例如下:

 
 
 
 
  1. public class User {  
  2.     private String firstName;  
  3.     private String lastName;  
  4.     private String province;  
  5.     private String city;  
  6.     private String area;  
  7.     private String street;  

正例:

 
 
 
 
  1. public class User {  
  2.     private UserName username;  
  3.     private Adress adress;  
  4. }  
  5. class UserName{  
  6.     private String firstName;  
  7.     private String lastName;  
  8. }  
  9. class Address{  
  10.     private String province;  
  11.     private String city;  
  12.     private String area;  
  13.     private String street;  

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

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

 
 
 
 
  1. // 訂單  
  2. public class Order {  
  3.     private String customName;  
  4.     private String address;  
  5.     private Integer orderId;  
  6.     private Integer price;  

正例:

 
 
 
 
  1. // 訂單類(lèi)  
  2. public class Order {  
  3.     private Custom custom;  
  4.     private Integer orderId;  
  5.     private Integer price;  
  6. }  
  7. // 把custom相關(guān)字段封裝起來(lái),在Order中引用Custom對(duì)象  
  8. public class Custom {  
  9.     private String name;  
  10.     private String address;  

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

10. Switch Statements (Switch 語(yǔ)句)

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

示例代碼如下: 

 
 
 
 
  1. String medalType = "guest";  
  2.     if ("guest".equals(medalType)) {  
  3.         System.out.println("嘉賓勛章");  
  4.      } else if ("vip".equals(medalType)) {  
  5.         System.out.println("會(huì)員勛章");  
  6.     } else if ("guard".equals(medalType)) {  
  7.         System.out.println("守護(hù)勛章");  
  8.     }  
  9.     ... 

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

 
 
 
 
  1. //勛章接口  
  2. public interface IMedalService {  
  3.     void showMedal();  
  4. }  
  5. //守護(hù)勛章策略實(shí)現(xiàn)類(lèi)  
  6. public class GuardMedalServiceImpl implements IMedalService {  
  7.     @Override  
  8.     public void showMedal() {  
  9.         System.out.println("展示守護(hù)勛章");  
  10.     }  
  11. }  
  12. //嘉賓勛章策略實(shí)現(xiàn)類(lèi)  
  13. public class GuestMedalServiceImpl implements IMedalService {  
  14.     @Override  
  15.     public void showMedal() {  
  16.         System.out.println("嘉賓勛章");  
  17.     }  
  18. }  
  19. //勛章服務(wù)工廠類(lèi)  
  20. public class MedalServicesFactory {  
  21.     private static final Map map = new HashMap<>();  
  22.     static {  
  23.         map.put("guard", new GuardMedalServiceImpl());  
  24.         map.put("vip", new VipMedalServiceImpl());  
  25.         map.put("guest", new GuestMedalServiceImpl());  
  26.     }  
  27.     public static IMedalService getMedalService(String medalType) {  
  28.         return map.get(medalType);  
  29.     }  

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

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

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

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

12. Lazy Class (冗贅類(lèi))

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

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

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

只有一個(gè)if else,那就不需要班門(mén)弄斧使用多態(tài);

如果某個(gè)抽象類(lèi)沒(méi)有什么太大的作用,就運(yùn)用Collapse Hierarchy(折疊繼承體系)

如果函數(shù)的某些參數(shù)沒(méi)用上,就移除。

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

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

 
 
 
 
  1. public class PhoneAccount {  
  2.     private double excessMinutesCharge;  
  3.     private static final double RATE = 8.0;  
  4.     public double computeBill(int minutesUsed, int includedMinutes) {  
  5.         excessMinutesCharge = 0.0;  
  6.         int excessMinutes = minutesUsed - includedMinutes;  
  7.         if (excessMinutes >= 1) {  
  8.             excessMinutesexcessMinutesCharge = excessMinutes * RATE;  
  9.         }  
  10.         return excessMinutesCharge;  
  11.     }  
  12.     public double chargeForExcessMinutes(int minutesUsed, int includedMinutes) {  
  13.         computeBill(minutesUsed, includedMinutes);  
  14.         return excessMinutesCharge;  
  15.     }  

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

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

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

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

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

16. Middle Man (中間人)

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

 
 
 
 
  1. A.B.getC(){  
  2.    return C.getC();  

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

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

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

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

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

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

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

19. Incomplete Library Class (不完美的類(lèi)庫(kù))

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

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

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

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

如果擁有public字段-> Encapsulate Field

如果這些類(lèi)內(nèi)含容器類(lèi)的字段,應(yīng)該檢查它們是不是得到了恰當(dāng)?shù)胤庋b-> Encapsulate Collection封裝起來(lái)

對(duì)于不該被其他類(lèi)修改的字段-> Remove Setting Method->找出取值/設(shè)置函數(shù)被其他類(lèi)運(yùn)用的地點(diǎn)-> Move Method 把這些調(diào)用行為搬移到Data Class來(lái)。如果無(wú)法搬移整個(gè)函數(shù),就運(yùn)用Extract Method產(chǎn)生一個(gè)可被搬移的函數(shù)->Hide Method把這些取值/設(shè)置函數(shù)隱藏起來(lái)。

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

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

    需要為這個(gè)子類(lèi)新建一個(gè)兄弟類(lèi)->Push Down Method和Push Down Field把所有用不到的函數(shù)下推給兄弟類(lèi),這樣一來(lái),超類(lèi)就只持有所有子類(lèi)共享的東西。所有超類(lèi)都應(yīng)該是抽象的。

    如果子類(lèi)復(fù)用了超類(lèi)的實(shí)現(xiàn),又不愿意支持超類(lèi)的接口,可以不以為然。但是不能胡亂修改繼承體系->Replace Inheritance with Delegation(用委派替換繼承).

22. Comments (過(guò)多的注釋)

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

多余的解釋

日志式注釋

用注釋解釋變量等

 ...

如何優(yōu)化呢?

方法函數(shù)、變量的命名要規(guī)范、淺顯易懂、避免用注釋解釋代碼。

關(guān)鍵、復(fù)雜的業(yè)務(wù),使用清晰、簡(jiǎn)明的注釋

23. 神奇命名

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

反例:

 
 
 
 
  1. boolean test = chenkParamResult(req); 

正例:

 
 
 
 
  1. boolean isParamPass = chenkParamResult(req); 

24. 神奇魔法數(shù)

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

 
 
 
 
  1. if(userType==1){  
  2.    //doSth1  
  3. }else If( userType ==2){  
  4.    //doSth2  
  5. }  
  6. ... 

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

新建個(gè)常量類(lèi),把一些常量放進(jìn)去,統(tǒng)一管理,并且寫(xiě)好注釋;

建一個(gè)枚舉類(lèi),把相關(guān)的魔法數(shù)字放到一起管理。

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

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

dao層主要做數(shù)據(jù)持久層的工作,與數(shù)據(jù)庫(kù)打交道。

service層主要負(fù)責(zé)業(yè)務(wù)邏輯處理。

controller層負(fù)責(zé)具體的業(yè)務(wù)模塊流程的控制。

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

 
 
 
 
  1. @RestController("user")  
  2. public class UserController {  
  3.     Autowired  
  4.     private UserDao userDao;  
  5.     @RequestMapping("/queryUserInfo")  
  6.     public String queryUserInfo(String userName) {  
  7.         return userDao.selectByUserName(userName);  
  8.     }  
  9. }  

當(dāng)前題目:25種代碼壞味道總結(jié)+優(yōu)化示例
本文來(lái)源:http://m.5511xx.com/article/coeppch.html