新聞中心
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) {
Resultresult = 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就很方便。
Setsets = Sets.newHashSet(1, 2, 3, 4, 5, 6);
Setsets2 = Sets.newHashSet(3, 4, 5, 6, 7, 8, 9);
//交集
SetViewintersection = Sets.intersection(sets, sets2);
//差集
SetViewdiff = Sets.difference(sets, sets2);
//并集
SetViewunion = 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 MapcalculateBucket(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 MapcalculateBucket(Integer layerId, Integer expId, Integer expRatio, Map expGroups, Map > existsBucket) throws BucketAlgorithmException {
calculateVerify();
MapbucketMap = 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, ListlayerHistoryFlow) {
return new JSONObject();
}
/**
* @return
* @author hufei
* @description 新建實(shí)驗(yàn)
* @date 2021/6/10 11:24 上午
*/
public JSONObject newExp(MapexpGroups) {
return new JSONObject();
}
public JSONObject calculateExp(MapexpGroups, 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, ListlayerHistoryFlow) {
......
}
/**
* @return
* @author hufei
* @description 新建實(shí)驗(yàn)
* @date 2021/6/10 11:24 上午
*/
@Override
public JSONObject newExp(MapexpGroups) {
......
}
/**
* 實(shí)驗(yàn)組流量變更:
* 1.優(yōu)先從右邊空白開始分配
* 2.先增后減
*
* @param expGroups
* @param expHistoryFlow
* @return
*/
@Override
public JSONObject calculateExp(MapexpGroups, List expHistoryFlow) {
.......
}
/**
* 遞歸計(jì)算
*/
private void calculateExpRecursion(JSONObject currentExpFlow, MapgroupCountMap, 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


咨詢
建站咨詢
