日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

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

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷解決方案
如何提高Java代碼的質(zhì)量

1、可讀性

1.1 準(zhǔn)確命名

每種編程語言都有自己的命名規(guī)范,不同語言的風(fēng)格差異有大有小,下面以Java為例:

Java整體命名風(fēng)格為UpperCamelCase或lowerCamelCase形式。不管是類還是變量命名要見名知意,切勿使用縮寫或中文;風(fēng)格統(tǒng)一,盡量使用英文名詞,切勿中英文混合;盡量避免和Java自帶類庫(kù)重名,切勿使用Java關(guān)鍵字命名。

  • 包命名規(guī)范

包命名使用小寫英文名詞,使用“.”分割,每個(gè)被分割的單元盡量只有一個(gè)名詞,命名規(guī)范為:

域名.公司/個(gè)人名稱.項(xiàng)目名稱.模塊名稱

  • 類命名規(guī)范

類采用UpperCamelCase命名風(fēng)格,一些特殊的縮寫可以采用全大寫,如XML。類命名使用名詞描述類的作用。

  • 接口命名規(guī)范

接口采用UpperCamelCase命名風(fēng)格,由于接口定義的是一類功能或動(dòng)作,所以接口的命名一般使用形容詞或動(dòng)詞描述接口的行為。

  • 抽象類命名規(guī)范

抽象類除了滿足UpperCamelCase風(fēng)格外,一般需要加上Abstract前綴。

  • 異常類命名規(guī)范

異常類除了滿足UpperCamelCase風(fēng)格外,一般需要加上Exception或Error后綴,使用名詞描述什么異?;蝈e(cuò)誤。

  • 枚舉類命名規(guī)范

枚舉類除了滿足UpperCamelCase風(fēng)格外,一般加上Enum后綴,枚舉類中的枚舉值采用全部大寫風(fēng)格,單詞與單詞之間使用“_”進(jìn)行分割。

  • 方法命名規(guī)范

方法命名采用lowerCamelCase風(fēng)格,一般使用動(dòng)詞+名詞來命名,比較常見的有doXxx,handleXxx,findXxxx。

  • 變量命名規(guī)范

變量命名采用lowerCamelCase風(fēng)格,一般使用名詞描述變量的作用,需要注意的是區(qū)別于常量,盡量不要使用特殊符號(hào)前綴或使用“_”分割符號(hào)。

  • 常量命名規(guī)范

常量命名采用全部大寫,單詞與單詞之間使用“_”進(jìn)行分割。

1.2 代碼風(fēng)格

在日常的項(xiàng)目開發(fā)中,一個(gè)項(xiàng)目有很多人協(xié)同開發(fā),每個(gè)人使用的開發(fā)工具不一樣,比如大家常用的vs code和idea,不同的開發(fā)工具或代碼習(xí)慣也會(huì)導(dǎo)致代碼風(fēng)格不一致,我們?cè)陂_發(fā)時(shí)可能習(xí)慣性的進(jìn)行代碼格式化,就會(huì)導(dǎo)致整個(gè)類改動(dòng)非常多,在代碼合并時(shí)容易沖突。

我們可以在項(xiàng)目中增加.editorconfig文件來統(tǒng)一代碼風(fēng)格。

root = true

[*.{adoc,bat,groovy,html,java,js,jsp,kt,kts,md,properties,py,rb,sh,sql,svg,txt,xml,xsd}]
charset = utf-8

[*.{groovy,java,kt,kts,xml,xsd}]
indent_style = tab #tab鍵縮進(jìn),可選"space"、"tab"
indent_size = 4 #縮進(jìn)空格為4個(gè)
end_of_line = lf #結(jié)尾換行符,可選"lf"、"cr"、"crlf"
charset = utf-8 #文件編碼
trim_trailing_whitespace = true #不保留行末的空格
insert_final_newline = true #文件末尾增加一個(gè)空行
curly_bracket_next_line = false #大括號(hào)不另起一行
spaces_around_operators = true #運(yùn)算符兩邊都有空格
indent_brace_style = 1tbs #條件語句格式是1tbs

1.3 注釋規(guī)約

類注釋

類注釋采用/**......*/,在每個(gè)類的頭部要有必要的注釋信息,包括:作者、創(chuàng)建時(shí)間、類功能描述


/**
* 簡(jiǎn)單分流算法實(shí)驗(yàn), 每次分流只用考慮當(dāng)前的桶, 不用回溯歷史版本
* {@link https://duapp.yuque.com/team_tech/confluence-data-iwskfg/dzmogk}
* @author hufei
* @date 2021/6/8 7:59 下午
*/

接口注釋

接口注釋采用/**......*/,在滿足類注釋的基礎(chǔ)上,接口注釋應(yīng)該包含接口的目的、如何使用。


/**
* AB分桶算法接口規(guī)范
* 對(duì)外暴露實(shí)驗(yàn)桶計(jì)算接口,該接口有一個(gè)抽象實(shí)現(xiàn)類AbstractBucketAlgorithm,具體的分桶算法實(shí)現(xiàn)這個(gè)抽象類
* @author hufei
* @date 2021/6/8 6:06 下午
*/

方法注釋

方法注釋采用/**......*/,描述方法的功能、輸入、輸出及返回值說明

/**
* 計(jì)算實(shí)驗(yàn)層的桶信息
* @param layerId 分層 id
* @param expId 實(shí)驗(yàn) Id
* @param expRatio 新的實(shí)驗(yàn)占層流量比例
* @param existsLayerBucket 老的層流量實(shí)驗(yàn)配比
* @return 新的層流量實(shí)驗(yàn)配比
* @throws BucketAlgorithmException
*/

方法內(nèi)部注釋

代碼做了些什么以及為什么這樣做,特別是復(fù)雜的邏輯處理部分,要盡可能的給出詳細(xì)的注釋。

全局變量注釋

包括變量的功能、取值范圍、注意事項(xiàng)等的說明。


/**
* 代表kafka收到消息的答復(fù)數(shù),0就是不要答復(fù),愛收到?jīng)]收到.1就是有一個(gè)leader broker答復(fù)就行,all是所有broker都要收到才行
* 0: Producer不等待kafka服務(wù)器的答復(fù),消息立刻發(fā)往socket buffer,這種方式不能保證kafka收到消息,設(shè)置成這個(gè)值的時(shí)候retries參數(shù)就失效了,因?yàn)閜roducer不知道kafka收沒收到消息,所以所謂的重試就沒有意義了,發(fā)送返回值的offset全默認(rèn)是-1.
* 1: 等待leader記錄數(shù)據(jù)到broker本地log即可.不等待leader同步到其他followers,那么假如此時(shí)剛好leader收到消息并答復(fù)后,leader突然掛了,其他fowller還沒來得及復(fù)制消息呢,那么這條消息就會(huì)丟失了.
* all:等待所有broker記錄消息.保證消息不會(huì)丟失(只要從節(jié)點(diǎn)沒全掛),這種方式是最高可用的 acks默認(rèn)值是1.
*/
private String acks = "0";

局部變量注釋

主要的局部變量必須有注釋,無特別意義的情況下可以不加注釋。

2可靠性

2.1 增強(qiáng)健壯性

慎用遞歸算法

遞歸算法寫起來很簡(jiǎn)單,但用的不好容易導(dǎo)致堆棧溢出和死循環(huán)問題。因此盡量不要使用遞歸算法,如果要使用需要注意以下幾個(gè)問題:

把退出條件放在函數(shù)最上方,這樣比較清晰,防止程序一直不滿足退出條件而導(dǎo)致堆棧溢出


public int recursiveAlgorithm(){
if (退出條件)
return 0;
......
}

避免在遞歸函數(shù)中出現(xiàn)過大的局部變量,這會(huì)加速堆棧空間的消耗


public int recursiveAlgorithm(){
char buf[] = new char[1024];
}

增加一個(gè)最大遞歸深度,防止出現(xiàn)死循環(huán)導(dǎo)致堆棧溢出

使用參數(shù)校驗(yàn)

項(xiàng)目中有很多接口提供給前端,有一些參數(shù)在后續(xù)的邏輯處理中不能為空并且沒有做非空校驗(yàn),靠口頭的約定參數(shù)不能為空,如果前端調(diào)用時(shí)沒有傳參就會(huì)出現(xiàn)空指針異常。最好的解決辦法是統(tǒng)一參數(shù)校驗(yàn)框架,在接口的入?yún)⑦M(jìn)行非空限制,如果為空,統(tǒng)一拋出異常。

@Valids({
@Valid(names = "request.expStatus",required = true,regex = "[1,2]",error = "實(shí)驗(yàn)狀態(tài)必須為1或者2"),
@Valid(names = "request.weekDateList,request.type" , required = true)
})

冪等校驗(yàn)

我們的系統(tǒng)應(yīng)該做好冪等校驗(yàn),等于同一筆業(yè)務(wù)操作,不管調(diào)用多少次,得到的結(jié)果都是一樣的。比如用戶重復(fù)下單、MQ消息重復(fù)消費(fèi)、前端重復(fù)提交表單等,這些都是需要保證冪等操作。

2.2 善始善終

異常處理

我們?cè)谶M(jìn)行異常處理時(shí),一定要把必須執(zhí)行的語句放到finally塊中,比如讀寫文件時(shí),在finally塊中關(guān)閉IO連接。

BufferedReader br = null;
try {
br = new BufferedReader(new FileReader(new File("")));
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
if (br != null) {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}

加鎖釋放鎖

我們?cè)谌粘5拈_發(fā)中經(jīng)常會(huì)碰到資源競(jìng)爭(zhēng)的問題,此時(shí)我們需要對(duì)競(jìng)爭(zhēng)的資源進(jìn)行加鎖處理,如果我們忘記釋放鎖,就會(huì)導(dǎo)致其他的請(qǐng)求阻塞。

Lock lock = new ReentrantLock();
lock.lock();
......
lock.unlock();

資源釋放

我們?cè)谌粘5拈_發(fā)中會(huì)用到數(shù)據(jù)庫(kù)或網(wǎng)絡(luò)資源,此時(shí)我們需要建立數(shù)據(jù)庫(kù)連接或網(wǎng)絡(luò)連接,如果我們忘記釋放連接就會(huì)導(dǎo)致數(shù)據(jù)庫(kù)資源或網(wǎng)絡(luò)資源一直被占用,直到連接失效。雖然我們?cè)谌粘5拈_發(fā)中不會(huì)直接創(chuàng)建連接而是使用連接池,但是連接數(shù)最大連接數(shù)如果設(shè)置的過大,也會(huì)導(dǎo)致資源的耗盡。


ResultSet resultSet = null;
Statement statement = null;
Connection connection = null;
try {
Class.forName("com.mysql.cj.jdbc.Driver");
connection = DriverManager.getConnection(url,user,pwd);
statement = connection.createStatement();
resultSet = statement.executeQuery("select * from student");
} catch (SQLException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
try {
if (resultSet != null) {
resultSet.close();
}
} catch (SQLException e) {
log.error(e.getMessage(),e);
}
try {
if (statement != null) {
statement.close();
}
} catch (SQLException e){
log.error(e.getMessage(),e);
}
try {
if (connection != null) {
connection.close();
}
} catch (SQLException e){
log.error(e.getMessage(),e);
}
}

2.3 異常處理

盡量使用非受檢異常

受檢異常的缺陷:

  • 受檢異常使接口聲明脆弱

例如我們定義一個(gè)接口

interface User {
public void changePassword() throws MySecurityException;
}

隨著業(yè)務(wù)的開發(fā),接口拋出的異常增加,比如新增一個(gè)RejectChangeException,那就需要修改User接口,這就會(huì)導(dǎo)致所有的User接口調(diào)用者都要追加對(duì)RejectChangeException異常的處理。

  • 受檢異常使代碼的可讀性降低

一個(gè)方法增加了受檢異常,則調(diào)用者必須對(duì)異常進(jìn)行處理,比如調(diào)用無受檢異常:

public static void main(String[] args) {
userImpl.changePassword();
}

如果調(diào)用受檢異常就不一樣了:


public static void main(String[] args) {
try {
userImpl.changePassword();
} catch(Exception e){
e.printStackTrace();
}
}

如果一個(gè)方法使用了受檢異常,那么調(diào)用者就必須處理,特別是在多個(gè)異常的情況下,要增加多個(gè)catch塊進(jìn)行處理,就會(huì)增加代碼復(fù)雜度。

  • 受檢異常增加了開發(fā)工作量

不要在finally塊中處理返回值

在finally塊中進(jìn)行return會(huì)導(dǎo)致如下問題:

  • 覆蓋了try代碼塊中的return返回值,比如下面的方法,我們傳入100返回結(jié)果也是-1。


public static void main(String[] args) {
calculate(100);
}
public static int calculate(int number) throws Exception {
try {
if (number < 100) {
throw new DataFormatException("數(shù)據(jù)格式錯(cuò)誤");
} else {
return number;
}
} catch (Exception e) {
throw e;
} finally {
return -1;
}
}
  • 屏蔽異常

當(dāng)我們?cè)趖ry塊中拋出異常時(shí),異常線程在監(jiān)視到有異常發(fā)生時(shí),就會(huì)在異常表中登記當(dāng)前的異常類型為DataFormatException,但是當(dāng)執(zhí)行器執(zhí)行finally代碼塊時(shí),就會(huì)重新為方法賦值,也就是告訴調(diào)用者“該方法執(zhí)行正確,沒有異常產(chǎn)生”,比如我們調(diào)用上面的方法,傳入-1,不會(huì)拋出異常。

異常封裝

Java中提供了異常處理機(jī)制,可以保證程序的健壯性,但是Java中提供的都是通用異常,我們?cè)陧?xiàng)目的開發(fā)中需要封裝一些業(yè)務(wù)的異常。

統(tǒng)一異常處理

在項(xiàng)目開發(fā)中,可以用切面統(tǒng)一異常處理或依賴SpringMVC的ControllerAdvice,將錯(cuò)誤信息按項(xiàng)目統(tǒng)一格式返回給前端,這樣在開發(fā)過程中只用拋出異常就可以了。

禁止直接吞掉異常

吞掉異常會(huì)導(dǎo)致難以排查程序運(yùn)行過程中出現(xiàn)的問題,應(yīng)該將異常向上拋出。

2.4 留意編譯告警

程序中的編譯告警容易被忽略,因?yàn)榧词钩霈F(xiàn)了告警,源文件仍能被編輯通過并運(yùn)行,尤其是我們?cè)陂_發(fā)的過程中使用IDE,但這些告警中往往隱藏著一些潛在的問題。

2.5 盡早暴露問題

一個(gè)bug在項(xiàng)目的開發(fā)、自測(cè)、測(cè)試、發(fā)布等階段被發(fā)現(xiàn),其修復(fù)成本是不一樣的,越往后修復(fù)成本越高,尤其到了線上可能還會(huì)造成一定的資損。項(xiàng)目研發(fā)在開發(fā)和自測(cè)的過程中,應(yīng)重視代碼質(zhì)量,自測(cè)或使用bug掃描工具來盡早的發(fā)現(xiàn)問題。

SpotBugs

SpotBugs提供靜態(tài)字節(jié)代碼分析,它使用靜態(tài)分析來查找400多種錯(cuò)誤模式,例如空指針取消引用、無限遞歸循環(huán),對(duì)Java庫(kù)的錯(cuò)誤使用和死鎖。

3、可維護(hù)性

3.1 記錄日志

  • 所有后臺(tái)都要有操作日志、數(shù)據(jù)變更日志
  • 日志要配置異步寫盤
  • 線上僅保留WARN和ERROR級(jí)別日志
  • 所有日志都要有traceId
  • 異常日志要有堆棧、入?yún)?、能說清楚是什么錯(cuò)誤的信息
  • 打印日志時(shí),禁止直接用JSON工具將對(duì)象轉(zhuǎn)換成String

3.2 明確錯(cuò)誤提示

在產(chǎn)品的使用中,我們會(huì)提示一些錯(cuò)誤信息給用戶,但是如果提供籠統(tǒng)的錯(cuò)誤提示可能令用戶感到困惑,如:“服務(wù)暫不可用”,尤其我們不能提示:“系統(tǒng)內(nèi)部錯(cuò)誤,請(qǐng)聯(lián)系系統(tǒng)管理員!”給用戶,這會(huì)降低用戶對(duì)產(chǎn)品的信任度。我們可以提示具體的錯(cuò)誤信息,如“xxx信息未填,請(qǐng)先填寫完成?!?/p>

3.3 保持代碼簡(jiǎn)潔性

避免嵌套if/else

代碼中經(jīng)常會(huì)進(jìn)行空值判斷和邏輯判斷,if/else嵌套會(huì)使得代碼邏輯看起來非常復(fù)雜。


public static String getDepartmentNameOfUser(String username) {
Result result = getUserByName(username);
if (result != null) {
User user = result.getData();
if (user != null) {
Department department = user.getDepartment();
if (department != null) {
return department.getName();
}
}
}
return "未知部門";
}

盡量避免嵌套if/else,可以這樣寫:

public static String getDepartmentNameOfUser(String username) {
Result result = getUserByName(username);
if (result == null) {
return "未知部門";
}
User user = result.getData();
if (user == null) {
return "未知部門";
}
Department department = user.getDepartment();
if (department == null) {
return "Department為空";
}
return department.getName();
}

抽取類、方法

讓類或方法的職責(zé)更加明確,不要把所有的邏輯寫到一個(gè)方法里面。

public boolean addExp(ExpVO expVO){
//校驗(yàn)參數(shù)是否正確,如果失敗直接拋出異常
checkParamValidate(expVO);
//新增實(shí)驗(yàn)信息
addExp(expVO);
//新增實(shí)驗(yàn)組
expGroupService.addExpGroup(expVO);
//新增實(shí)驗(yàn)層
expLayerService.addExpLayer(expVO);
//計(jì)算實(shí)驗(yàn)流量
List flowEntityList = flowService.calculateFlow(expVO);
//保存流量信息
flowService.saveFlow(flowEntityList);
}

不要使用魔法值

不要在代碼中使用魔法值,這樣后續(xù)如果值變化改動(dòng)起來會(huì)漏掉。


public boolean addExp(){
//服務(wù)端實(shí)驗(yàn)
expEntity.setExpType(1);
}

可以使用枚舉:


public boolean addExp(){
expEntity.setExpType(ExpTypeEnum.SERVER.getExpType());
}

3.4 使用開源工具

使用一些開源工具可以減少我們重復(fù)造輪子,而且常用的開源工具都有完整的單元測(cè)試覆蓋,可以有效的減少bug的出現(xiàn)。

Google Guava

Guava是一組來自Google的核心Java庫(kù),其中包括集合、緩存、原生類型、并發(fā)、常用注解、基本字符串操作和I/O等等。

比如集合的交、并、查集,使用Google Guava就很方便。


Set sets = Sets.newHashSet(1, 2, 3, 4, 5, 6);
Set sets2 = Sets.newHashSet(3, 4, 5, 6, 7, 8, 9);
//交集
SetView intersection = Sets.intersection(sets, sets2);
//差集
SetView diff = Sets.difference(sets, sets2);
//并集
SetView union = Sets.union(sets, sets2);

Apache Commons

Apache Commons是對(duì)JDK的擴(kuò)展,包含了很多開源的工具,下面是我們項(xiàng)目中常用的工具:

Commons Lang3:處理Java基本對(duì)象方法的工具類,提供對(duì)字符、數(shù)組等基本對(duì)象的操作。

Commons Codec:提供常用的編碼和解碼方法,如DES、SHA1、Base64。

Commons BeanUtils:提供Bean的動(dòng)態(tài)生成。

Commons HttpClient:簡(jiǎn)化HTTP客戶端與服務(wù)端的各種通訊。

Log4j

各大開源框架和項(xiàng)目中用的最多的日志框架。

4、可擴(kuò)展性

我們寫的代碼都是為了特定的需求服務(wù)的,但是這些需求并不是一成不變的,當(dāng)需求變更了,如果我們代碼的擴(kuò)展性很好,我們可能只需要簡(jiǎn)單的添加或者刪除模塊就行了,如果擴(kuò)展性不好,可能所有的代碼都需要重寫,所以提供代碼的擴(kuò)展性是必須的,我們?cè)趯懘a的時(shí)候使用設(shè)計(jì)模式可以使代碼具備很好的擴(kuò)展性。

比如AB分流算法,分流算法根據(jù)不同的場(chǎng)景不同的需求有不同的實(shí)現(xiàn),我們定義好算法的接口,不同的分流算法實(shí)現(xiàn)這個(gè)接口,那么我們?cè)谑褂玫臅r(shí)候只需要考慮使用哪個(gè)算法就行了,不需要關(guān)心算法的實(shí)現(xiàn)。

/**
* AB分桶算法
* @author hufei
* @date 2021/6/8 6:06 下午
*/
public interface BucketAlgorithmTemplate {

/**
* ab分桶算法
*
* @param layerId
* @param expId 實(shí)驗(yàn)id
* @param expRatio 實(shí)驗(yàn)占層流量比
* @param expGroups 實(shí)驗(yàn)組詳情
* @param existsBucket 已存在的實(shí)驗(yàn)層和實(shí)驗(yàn)組流量詳情,第一次創(chuàng)建傳null
* @return
* @throws BucketAlgorithmException
*/
public Map calculateBucket(Integer layerId, Integer expId, Integer expRatio, Map expGroups, Map> existsBucket) throws BucketAlgorithmException;
}

/**
* AB分桶算法
* @author hufei
* @date 2021/6/8 6:39 下午
*/
public abstract class AbstractBucketAlgorithm implements BucketAlgorithmTemplate {
/**
* @param expId 實(shí)驗(yàn)id
* @param expRatio 實(shí)驗(yàn)占層流量比
* @param expGroups 實(shí)驗(yàn)組詳情
* @param existsBucket 已存在的實(shí)驗(yàn)層和實(shí)驗(yàn)組流量詳情,第一次創(chuàng)建傳null
* @return
* @author hufei
* @description ab分桶算法
* @date 2021/6/8 6:31 下午
*/
public Map calculateBucket(Integer layerId, Integer expId, Integer expRatio, Map expGroups, Map> existsBucket) throws BucketAlgorithmException {
calculateVerify();
Map bucketMap = new HashMap<>();
JSONObject layerObj = new JSONObject();
JSONObject expObj = new JSONObject();
if (existsBucket == null) {
//如果不存在歷史分流信息,說明層是新建,實(shí)驗(yàn)也是新建
layerObj = newLayer(layerId, expId, expRatio);
expObj = newExp(expGroups);
} else if (existsBucket.get(layerId) != null && existsBucket.size() == 1) {
//只有層的歷史記錄,但是沒有實(shí)驗(yàn)的歷史記錄,說明層已經(jīng)存在實(shí)驗(yàn)是新建
layerObj = calculateLayer(layerId, expId, expRatio, existsBucket.get(layerId));
expObj = newExp(expGroups);
} else if (existsBucket.get(layerId) != null && existsBucket.get(expId) != null) {
//有層和實(shí)驗(yàn)的歷史記錄,說明層不是新建并且實(shí)驗(yàn)也不是新建
layerObj = calculateLayer(layerId, expId, expRatio, existsBucket.get(layerId));
expObj = calculateExp(expGroups, existsBucket.get(expId));
}
bucketMap.put(layerId, layerObj);
bucketMap.put(expId, expObj);
return bucketMap;
}
/**
* @param layerId
* @param expId
* @param expRatio
* @return
* @author hufei
* @description 新建層
* @date 2021/6/10 11:24 上午
*/
public JSONObject newLayer(Integer layerId, Integer expId, Integer expRatio) {
return new JSONObject();
}
public JSONObject calculateLayer(Integer layerId, Integer expId, Integer expRatio, List layerHistoryFlow) {
return new JSONObject();
}
/**
* @return
* @author hufei
* @description 新建實(shí)驗(yàn)
* @date 2021/6/10 11:24 上午
*/
public JSONObject newExp(Map expGroups) {
return new JSONObject();
}

public JSONObject calculateExp(Map expGroups, List expHistoryFlow) {
return new JSONObject();
}
}

/**
* 簡(jiǎn)單分流算法實(shí)驗(yàn), 每次分流只用考慮當(dāng)前的桶, 不用回溯歷史版本
* {@link https://duapp.yuque.com/team_tech/confluence-data-iwskfg/dzmogk}
* @author hufei
* @date 2021/6/8 7:59 下午
*/
public class SimpleBucketAlgorithm extends AbstractBucketAlgorithm {
/**
* @param layerId
* @param expId
* @param expRatio
* @return
* @author hufei
* @description 新建層
* @date 2021/6/10 11:24 上午
*/
@Override
public JSONObject newLayer(Integer layerId, Integer expId, Integer expRatio) {
......
}
@Override
public JSONObject calculateLayer(Integer layerId, Integer expId, Integer expRatio, List layerHistoryFlow) {
......
}
/**
* @return
* @author hufei
* @description 新建實(shí)驗(yàn)
* @date 2021/6/10 11:24 上午
*/
@Override
public JSONObject newExp(Map expGroups) {
......
}
/**
* 實(shí)驗(yàn)組流量變更:
* 1.優(yōu)先從右邊空白開始分配
* 2.先增后減
*
* @param expGroups
* @param expHistoryFlow
* @return
*/
@Override
public JSONObject calculateExp(Map expGroups, List expHistoryFlow) {
.......
}
/**
* 遞歸計(jì)算
*/
private void calculateExpRecursion(JSONObject currentExpFlow, Map groupCountMap, List positiveRatioList, List negativeRatioList, Map groupNeedAddOrReduceRatioMap) {

}
}

5、效率?

5.1 代碼優(yōu)化

循環(huán)優(yōu)化

for (int i=0;i...
}
for (int i=0,size=list.size();i...
}

不要在循環(huán)中創(chuàng)建對(duì)象

集合優(yōu)化

在初始化集合時(shí),盡量指定可預(yù)知的集合大小,減少集合的擴(kuò)容次數(shù)。

5.2 引入并發(fā)

并發(fā)可以很好的提升程序的執(zhí)行時(shí)間,但是使用不好也會(huì)帶來很多問題。如果任務(wù)和任務(wù)之間沒有關(guān)聯(lián)性,我們并發(fā)的執(zhí)行任務(wù)來縮短整體時(shí)間。


CompletableFuture[] cfs = tailorEntry.getValue().values().stream().map(layerExtraInfo -> CompletableFuture.supplyAsync(() -> layerCalculate(layerExtraInfo, userHitWhiteListMap, request.getUserId(), needGroupId, request.getCurrentGroupParm()), asyncFlowServiceExecutor).whenComplete((r, e) -> {
if (!r.isEmpty()) {
hitGroupList.addAll(r);
r.forEach(g -> {
needGroupId.add(g.getId());
});
}
})).toArray(CompletableFuture[]::new);
CompletableFuture.allOf(cfs).join();

提高代碼質(zhì)量是一個(gè)復(fù)雜且持續(xù)的工作,一篇文章的講解也很有限,我們?cè)陧?xiàng)目的開發(fā)中需要持續(xù)不斷的迭代優(yōu)化,來保證代碼的質(zhì)量。?

*文


分享名稱:如何提高Java代碼的質(zhì)量
鏈接URL:http://www.dlmjj.cn/article/cdjpspg.html