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

          這代碼寫的,狗屎一樣 !

          共 14408字,需瀏覽 29分鐘

           ·

          2020-09-14 20:30


          1.

          導(dǎo)讀


          昨天在技術(shù)交流群,有位讀者吐槽剛剛接手的代碼(文章標題),代碼全篇無格式,無注釋,多個嵌套不打括號,函數(shù)命名不規(guī)范,代碼實在看不下去。隨著軟件項目代碼的日積月累,系統(tǒng)維護成本變得越來越高,代碼質(zhì)量是所有軟件團隊面臨的共同問題。

          持續(xù)地優(yōu)化代碼,提高代碼的質(zhì)量,是提升系統(tǒng)生命力的有效手段之一。軟件系統(tǒng)思維有句話“Less coding, more thinking(少編碼、多思考)”,也有這么一句俚語“Think more, code less(思考越多,編碼越少)”。所以,我們在編碼中多思考多總結(jié),努力提升自己的編碼水平,才能編寫出更優(yōu)雅、更高質(zhì)、更高效的代碼。

          剛剛接觸項目或者實習(xí)階段,寫出的代碼質(zhì)量差難免被吐槽,本文便總結(jié)了一套與Java函數(shù)相關(guān)的編碼規(guī)則,旨在給廣大Java程序員一些編碼建議,有助于大家編寫出更優(yōu)雅、更高質(zhì)、更高效的代碼。這套編碼規(guī)則,通過在高德采集部門的實踐,已經(jīng)取得了不錯的成效。

          2.

          使用通用工具函數(shù)


          2.1 案例一

          現(xiàn)象描述:

          不完善的寫法:

          thisName != null && thisName.equals(name);

          更完善的寫法:

          (thisName == name) || (thisName != null && thisName.equals(name));

          建議方案:

          Objects.equals(name, thisName);

          2.2 案例

          現(xiàn)象描述:

          !(list == null || list.isEmpty());

          建議方案:

          import org.apache.commons.collections4.CollectionUtils;CollectionUtils.isNotEmpty(list);

          2.3?主要收益

          • 函數(shù)式編程,業(yè)務(wù)代碼減少,邏輯一目了然;

          • 通用工具函數(shù),邏輯考慮周全,出問題概率低。

          3.

          拆分超大函數(shù)


          當一個函數(shù)超過80行后,就屬于超大函數(shù),需要進行拆分。

          3.1 案例一:每一個代碼塊都可以封裝為一個函

          每一個代碼塊必然有一個注釋,用于解釋這個代碼塊的功能。

          如果代碼塊前方有一行注釋,就是在提醒你——可以將這段代碼替換成一個函數(shù),而且可以在注釋的基礎(chǔ)上給這個函數(shù)命名。如果函數(shù)有一個描述恰當?shù)拿?,就不需要去看?nèi)部代碼究竟是如何實現(xiàn)的。

          現(xiàn)象描述:

          // 每日生活函數(shù)public void liveDaily() {    // 吃飯    // 吃飯相關(guān)代碼幾十行
          // 編碼 // 編碼相關(guān)代碼幾十行
          // 睡覺 // 睡覺相關(guān)代碼幾十行}

          建議方案:

          // 每日生活函數(shù)public void liveDaily() {    // 吃飯    eat();
          // 編碼 code();
          // 睡覺 sleep();}
          // 吃飯函數(shù)private void eat() { // 吃飯相關(guān)代碼}
          // 編碼函數(shù)private void code() { // 編碼相關(guān)代碼}
          // 睡覺函數(shù)private void sleep() { // 睡覺相關(guān)代碼}

          3.2 案例二:每一個循環(huán)體都可以封裝為一個函

          現(xiàn)象描述:

          // 生活函數(shù)public void live() {    while (isAlive) {        // 吃飯        eat();
          // 編碼 code();
          // 睡覺 sleep(); }}

          建議方案:

          // 生活函數(shù)public void live() {    while (isAlive) {        // 每日生活        liveDaily();    }}
          // 每日生活函數(shù)private void liveDaily() { // 吃飯 eat();
          // 編碼 code();
          // 睡覺 sleep();}

          3.3 案例三:每一個條件體都可以封裝為一個函

          現(xiàn)象描述:

          // 外出函數(shù)public void goOut() {    // 判斷是否周末    // 判斷是否周末: 是周末則游玩    if (isWeekday()) {        // 游玩代碼幾十行    }    // 判斷是否周末: 非周末則工作    else {        // 工作代碼幾十行    }}

          建議方案:

          // 外出函數(shù)public void goOut() {    // 判斷是否周末    // 判斷是否周末: 是周末則游玩    if (isWeekday()) {        play();    }    // 判斷是否周末: 非周末則工作    else {        work();    }}
          // 游玩函數(shù)private void play() { // 游玩代碼幾十行}
          // 工作函數(shù)private void work() { // 工作代碼幾十行}

          3.4?主要收益

          • 函數(shù)越短小精悍,功能就越單一,往往生命周期較長;

          • 一個函數(shù)越長,就越不容易理解和維護,維護人員不敢輕易修改;

          • 在過長函數(shù)中,往往含有難以發(fā)現(xiàn)的重復(fù)代碼。

          4.

          同一函數(shù)內(nèi)代碼塊級別盡量一致


          4.1?案例一

          現(xiàn)象描述:

          // 每日生活函數(shù)public void liveDaily() {    // 吃飯    eat();
          // 編碼 code();
          // 睡覺 // 睡覺相關(guān)代碼幾十行}

          很明顯,睡覺這塊代碼塊,跟eat(吃飯)和code(編碼)不在同一級別上,顯得比較突兀。如果把寫代碼比作寫文章,eat(吃飯)和code(編碼)是段落大意,而睡覺這塊代碼塊屬于一個詳細段落。而在liveDaily(每日生活)這個函數(shù)上,只需要寫出主要流程(段落大意)即可。

          建議方案:

          public void liveDaily() {    // 吃飯    eat();
          // 編碼 code();
          // 睡覺 sleep();}
          // 睡覺private void sleep() { // 睡覺相關(guān)代碼}

          4.2?主要收益

          • 函數(shù)調(diào)用表明用途,函數(shù)實現(xiàn)表達邏輯,層次分明便于理解;

          • 不用層次的代碼塊放在一個函數(shù)中,容易讓人覺得代碼頭重腳輕。


          5.

          封裝相同功能代碼為函數(shù)


          5.1 案例一:封裝相同代碼為函數(shù)

          現(xiàn)象描述:

          // 禁用用戶函數(shù)public void disableUser() {    // 禁用黑名單用戶    List userIdList = queryBlackUser();    for (Long userId : userIdList) {        User userUpdate = new User();        userUpdate.setId(userId);        userUpdate.setEnable(Boolean.FALSE);        userDAO.update(userUpdate);    }
          // 禁用過期用戶 userIdList = queryExpiredUser(); for (Long userId : userIdList) { User userUpdate = new User(); userUpdate.setId(userId); userUpdate.setEnable(Boolean.FALSE); userDAO.update(userUpdate); }}

          建議方案:

          // 禁用用戶函數(shù)public void disableUser() {    // 禁用黑名單用戶    List userIdList = queryBlackUser();    for (Long userId : userIdList) {        disableUser(userId);    }
          // 禁用過期用戶 userIdList = queryExpiredUser(); for (Long userId : userIdList) { disableUser(userId); }}
          // 禁用用戶函數(shù)private void disableUser(Long userId) { User userUpdate = new User(); userUpdate.setId(userId); userUpdate.setEnable(Boolean.FALSE); userDAO.update(userUpdate);}

          5.2 案例二:封裝相似代碼為函數(shù)

          封裝相似代碼為函數(shù),差異性通過函數(shù)參數(shù)控制。

          現(xiàn)象描述:

          // 通過工單函數(shù)public void adoptOrder(Long orderId) {    Order orderUpdate = new Order();    orderUpdate.setId(orderId);    orderUpdate.setStatus(OrderStatus.ADOPTED);    orderUpdate.setAuditTime(new Date());    orderDAO.update(orderUpdate);}
          // 駁回工單函數(shù)public void rejectOrder(Long orderId) { Order orderUpdate = new Order(); orderUpdate.setId(orderId); orderUpdate.setStatus(OrderStatus.REJECTED); orderUpdate.setAuditTime(new Date()); orderDAO.update(orderUpdate);}

          建議方案:

          // 通過工單函數(shù)public void adoptOrder(Long orderId) {    auditOrder(orderId, OrderStatus.ADOPTED);}
          // 駁回工單函數(shù)public void rejectOrder(Long orderId) { auditOrder(orderId, OrderStatus.REJECTED);}
          // 審核工單函數(shù)private void auditOrder(Long orderId, OrderStatus orderStatus) { Order orderUpdate = new Order(); orderUpdate.setId(orderId); orderUpdate.setStatus(orderStatus); orderUpdate.setAuditTime(new Date()); orderDAO.update(orderUpdate);}

          5.3?主要收益

          • 封裝公共函數(shù),減少代碼行數(shù),提高代碼質(zhì)量;

          • 封裝公共函數(shù),使業(yè)務(wù)代碼更精煉,可讀性可維護性更強。


          6.

          封裝獲取參數(shù)值函數(shù)


          6.1?案例一

          現(xiàn)象描述:

          // 是否通過函數(shù)public boolean isPassed(Long userId) {    // 獲取通過閾值    double thisPassThreshold = PASS_THRESHOLD;    if (Objects.nonNull(passThreshold)) {        thisPassThreshold = passThreshold;    }
          // 獲取通過率 double passRate = getPassRate(userId);
          // 判讀是否通過 return passRate >= thisPassThreshold;}

          建議方案:

          // 是否通過函數(shù)public boolean isPassed(Long userId) {    // 獲取通過閾值    double thisPassThreshold = getPassThreshold();
          // 獲取通過率 double passRate = getPassRate(userId);
          // 判讀是否通過 return passRate >= thisPassThreshold;}
          // 獲取通過閾值函數(shù)private double getPassThreshold() { if (Objects.nonNull(passThreshold)) { return passThreshold; } return PASS_THRESHOLD;}

          6.2?主要收益

          • 把獲取參數(shù)值從業(yè)務(wù)函數(shù)中獨立,使業(yè)務(wù)邏輯更清晰;

          • 封裝的獲取參數(shù)值為獨立函數(shù),可以在代碼中重復(fù)使用。


          7.

          通過接口參數(shù)化封裝相同邏輯


          7.1?案例一

          現(xiàn)象描述:

          // 發(fā)送審核員結(jié)算數(shù)據(jù)函數(shù)public void sendAuditorSettleData() {    List settleDataList = auditTaskDAO.statAuditorSettleData();    for (WorkerSettleData settleData : settleDataList) {        WorkerPushData pushData = new WorkerPushData();        pushData.setId(settleData.getWorkerId());        pushData.setType(WorkerPushDataType.AUDITOR);        pushData.setData(settleData);        pushService.push(pushData);    }}
          // 發(fā)送驗收員結(jié)算數(shù)據(jù)函數(shù)public void sendCheckerSettleData() { List settleDataList = auditTaskDAO.statCheckerSettleData(); for (WorkerSettleData settleData : settleDataList) { WorkerPushData pushData = new WorkerPushData(); pushData.setId(settleData.getWorkerId()); pushData.setType(WorkerPushDataType.CHECKER); pushData.setData(settleData); pushService.push(pushData); }

          建議方案:

          // 發(fā)送審核員結(jié)算數(shù)據(jù)函數(shù)public void sendAuditorSettleData() {    sendWorkerSettleData(WorkerPushDataType.AUDITOR, () -> auditTaskDAO.statAuditorSettleData());}
          // 發(fā)送驗收員結(jié)算數(shù)據(jù)函數(shù)public void sendCheckerSettleData() { sendWorkerSettleData(WorkerPushDataType.CHECKER, () -> auditTaskDAO.statCheckerSettleData());}
          // 發(fā)送作業(yè)員結(jié)算數(shù)據(jù)函數(shù)public void sendWorkerSettleData(WorkerPushDataType dataType, WorkerSettleDataProvider dataProvider) { List settleDataList = dataProvider.statWorkerSettleData(); for (WorkerSettleData settleData : settleDataList) { WorkerPushData pushData = new WorkerPushData(); pushData.setId(settleData.getWorkerId()); pushData.setType(dataType); pushData.setData(settleData); pushService.push(pushData); }}
          // 作業(yè)員結(jié)算數(shù)據(jù)提供者接口private interface WorkerSettleDataProvider { // 統(tǒng)計作業(yè)員結(jié)算數(shù)據(jù) public List statWorkerSettleData();}

          7.2?主要收益

          • 把核心邏輯從各個業(yè)務(wù)函數(shù)中抽析,使業(yè)務(wù)代碼更清晰更易維護;

          • 避免重復(fù)性代碼多次編寫,精簡重復(fù)函數(shù)越多收益越大。


          8.

          減少函數(shù)代碼層級


          如果要使函數(shù)優(yōu)美,建議函數(shù)代碼層級在1-4之間,過多的縮進會讓函數(shù)難以閱讀。

          8.1 案例一:利用return提前返回函數(shù)

          現(xiàn)象描述:

          // 獲取用戶余額函數(shù)public Double getUserBalance(Long userId) {    User user = getUser(userId);    if (Objects.nonNull(user)) {        UserAccount account = user.getAccount();        if (Objects.nonNull(account)) {            return account.getBalance();        }    }    return null;}

          建議方案:

          // 獲取用戶余額函數(shù)public Double getUserBalance(Long userId) {    // 獲取用戶信息    User user = getUser(userId);    if (Objects.isNull(user)) {        return null;    }
          // 獲取用戶賬戶 UserAccount account = user.getAccount(); if (Objects.isNull(account)) { return null; }
          // 返回賬戶余額 return account.getBalance();}

          8.2 案例二:利用continue提前結(jié)束循環(huán)

          現(xiàn)象描述:

          // 獲取合計余額函數(shù)public double getTotalBalance(List userList) {    // 初始合計余額    double totalBalance = 0.0D;
          // 依次累加余額 for (User user : userList) { // 獲取用戶賬戶 UserAccount account = user.getAccount(); if (Objects.nonNull(account)) { // 累加用戶余額 Double balance = account.getBalance(); if (Objects.nonNull(balance)) { totalBalance += balance; } } }
          // 返回合計余額 return totalBalance;}

          建議方案:

          // 獲取合計余額函數(shù)public double getTotalBalance(List userList) {    // 初始合計余額    double totalBalance = 0.0D;
          // 依次累加余額 for (User user : userList) { // 獲取用戶賬戶 UserAccount account = user.getAccount(); if (Objects.isNull(account)) { continue; }
          // 累加用戶余額 Double balance = account.getBalance(); if (Objects.nonNull(balance)) { totalBalance += balance; } }
          // 返回合計余額 return totalBalance;}

          特殊說明

          其它方式:在循環(huán)體中,先調(diào)用案例1的函數(shù)getUserBalance(獲取用戶余額),再進行對余額進行累加。

          在循環(huán)體中,建議最多使用一次continue。如果需要有使用多次continue的需求,建議把循環(huán)體封裝為一個函數(shù)。

          8.3 案例三:利用條件表達式函數(shù)減少層級

          請參考下一章的"案例2: 把復(fù)雜條件表達式封裝為函數(shù)"

          8.4?主要收益

          • 代碼層級減少,代碼縮進減少;

          • 模塊劃分清晰,方便閱讀維護。


          9.

          封裝條件表達式函數(shù)


          9.1 案例一:把簡單條件表達式封裝為函數(shù)

          現(xiàn)象描述:

          // 獲取門票價格函數(shù)public double getTicketPrice(Date currDate) {    if (Objects.nonNull(currDate) && currDate.after(DISCOUNT_BEGIN_DATE)        && currDate.before(DISCOUNT_END_DATE)) {        return TICKET_PRICE * DISCOUNT_RATE;    }    return TICKET_PRICE;}

          建議方案:

          // 獲取門票價格函數(shù)public double getTicketPrice(Date currDate) {    if (isDiscountDate(currDate)) {        return TICKET_PRICE * DISCOUNT_RATE;    }    return TICKET_PRICE;}
          // 是否折扣日期函數(shù)private static boolean isDiscountDate(Date currDate) { return Objects.nonNull(currDate) && currDate.after(DISCOUNT_BEGIN_DATE) && currDate.before(DISCOUNT_END_DATE);}

          9.2 案例二:把復(fù)雜條件表達式封裝為函數(shù)

          現(xiàn)象描述:

          // 獲取土豪用戶列表public List getRichUserList(List userList) {    // 初始土豪用戶列表    List richUserList = new ArrayList<>();
          // 依次查找土豪用戶 for (User user : userList) { // 獲取用戶賬戶 UserAccount account = user.getAccount(); if (Objects.nonNull(account)) { // 判斷用戶余額 Double balance = account.getBalance(); if (Objects.nonNull(balance) && balance.compareTo(RICH_THRESHOLD) >= 0) { // 添加土豪用戶 richUserList.add(user); } } }
          // 返回土豪用戶列表 return richUserList;}

          建議方案:

          // 獲取土豪用戶列表public List getRichUserList(List userList) {    // 初始土豪用戶列表    List richUserList = new ArrayList<>();
          // 依次查找土豪用戶 for (User user : userList) { // 判斷土豪用戶 if (isRichUser(user)) { // 添加土豪用戶 richUserList.add(user); } }
          // 返回土豪用戶列表 return richUserList;}
          // 是否土豪用戶private boolean isRichUser(User user) { // 獲取用戶賬戶 UserAccount account = user.getAccount(); if (Objects.isNull(account)) { return false; }
          // 獲取用戶余額 Double balance = account.getBalance(); if (Objects.isNull(balance)) { return false; }
          // 比較用戶余額 return balance.compareTo(RICH_THRESHOLD) >= 0;}

          以上代碼也可以用采用流式(Stream)編程的過濾來實現(xiàn)。

          9.3?主要收益

          • 把條件表達式從業(yè)務(wù)函數(shù)中獨立,使業(yè)務(wù)邏輯更清晰;

          • 封裝的條件表達式為獨立函數(shù),可以在代碼中重復(fù)使用。


          10.

          盡量避免不必要的空指針判斷


          本章只適用于項目內(nèi)部代碼,并且是自己了解的代碼,才能夠盡量避免不必要的空指針判斷。對于第三方中間件和系統(tǒng)接口,必須做好空指針判斷,以保證代碼的健壯性。

          10.1 案例一:調(diào)用函數(shù)保證參數(shù)不為空,被調(diào)用函數(shù)盡量避免不必要的空指針判斷

          現(xiàn)象描述:

          // 創(chuàng)建用戶信息User user = new User();... // 賦值用戶相關(guān)信息createUser(user);
          // 創(chuàng)建用戶函數(shù)private void createUser(User user){ // 判斷用戶為空 if(Objects.isNull(user)) { return; }
          // 創(chuàng)建用戶信息 userDAO.insert(user); userRedis.save(user);}

          建議方案:

          // 創(chuàng)建用戶信息User user = new User();... // 賦值用戶相關(guān)信息createUser(user);
          // 創(chuàng)建用戶函數(shù)private void createUser(User user){ // 創(chuàng)建用戶信息 userDAO.insert(user); userRedis.save(user);}

          10.2 案例二:被調(diào)用函數(shù)保證返回不為空,調(diào)用函數(shù)盡量避免不必要的空指針判斷

          現(xiàn)象描述:

          // 保存用戶函數(shù)public void saveUser(Long id, String name) {    // 構(gòu)建用戶信息    User user = buildUser(id, name);    if (Objects.isNull(user)) {        throw new BizRuntimeException("構(gòu)建用戶信息為空");    }
          // 保存用戶信息 userDAO.insert(user); userRedis.save(user);}
          // 構(gòu)建用戶函數(shù)private User buildUser(Long id, String name) { User user = new User(); user.setId(id); user.setName(name); return user;}

          建議方案:

          // 保存用戶函數(shù)public void saveUser(Long id, String name) {    // 構(gòu)建用戶信息    User user = buildUser(id, name);
          // 保存用戶信息 userDAO.insert(user); userRedis.save(user);}
          // 構(gòu)建用戶函數(shù)private User buildUser(Long id, String name) { User user = new User(); user.setId(id); user.setName(name); return user;}

          10.3 案例三:賦值邏輯保證列表數(shù)據(jù)項不為空,處理邏輯盡量避免不必要的空指針判斷

          現(xiàn)象描述:

          // 查詢用戶列表List userList = userDAO.queryAll();if (CollectionUtils.isEmpty(userList)) {    return;}
          // 轉(zhuǎn)化用戶列表List userVoList = new ArrayList<>(userList.size());for (UserDO user : userList) { UserVO userVo = new UserVO(); userVo.setId(user.getId()); userVo.setName(user.getName()); userVoList.add(userVo);}
          // 依次處理用戶for (UserVO userVo : userVoList) { // 判斷用戶為空 if (Objects.isNull(userVo)) { continue; }
          // 處理相關(guān)邏輯 ...}

          建議方案:

          // 查詢用戶列表List userList = userDAO.queryAll();if (CollectionUtils.isEmpty(userList)) {    return;}
          // 轉(zhuǎn)化用戶列表List userVoList = new ArrayList<>(userList.size());for (UserDO user : userList) { UserVO userVo = new UserVO(); userVo.setId(user.getId()); userVo.setName(user.getName()); userVoList.add(userVo);}
          // 依次處理用戶for (UserVO userVo : userVoList) { // 處理相關(guān)邏輯 ...}

          10.4 案例四:MyBatis查詢函數(shù)返回列表和數(shù)據(jù)項不為空,可以不用空指針判斷

          MyBatis是一款優(yōu)秀的持久層框架,是在項目中使用的最廣泛的數(shù)據(jù)庫中間件之一。通過對MyBatis源碼進行分析,查詢函數(shù)返回的列表和數(shù)據(jù)項都不為空,在代碼中可以不用進行空指針判斷。

          現(xiàn)象描述:

          這種寫法沒有問題,只是過于保守了。

          // 查詢用戶函數(shù)public List queryUser(Long id, String name) {    // 查詢用戶列表    List userList = userDAO.query(id, name);    if (Objects.isNull(userList)) {        return Collections.emptyList();    }
          // 轉(zhuǎn)化用戶列表 List voList = new ArrayList<>(userList.size()); for (UserDO user : userList) { // 判斷對象為空 if (Objects.isNull(user)) { continue; }
          // 添加用戶信息 UserVO vo = new UserVO(); BeanUtils.copyProperties(user, vo); voList.add(vo); }
          // 返回用戶列表 return voList;}

          建議方案:

          // 查詢用戶函數(shù)public List queryUser(Long id, String name) {    // 查詢用戶列表    List userList = userDAO.query(id, name);
          // 轉(zhuǎn)化用戶列表 List voList = new ArrayList<>(userList.size()); for (UserDO user : userList) { UserVO vo = new UserVO(); BeanUtils.copyProperties(user, vo); voList.add(vo); }
          // 返回用戶列表 return voList;}

          10.5?主要收益

          • 避免不必要的空指針判斷,精簡業(yè)務(wù)代碼處理邏輯,提高業(yè)務(wù)代碼運行效率;

          • 這些不必要的空指針判斷,基本屬于永遠不執(zhí)行的Death代碼,刪除有助于代碼維護。

          END


          喜歡文章,點個在看?

          瀏覽 45
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <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>
                  免费在线观看亚洲 | 国产小骚逼 | 欧美日韩黄色一级视频 | 成人偷拍自拍在线观看 | 肏逼网站在线观看 |