這幾天開發遇到了很多坑,大部分坑都是自己前期沒規劃好,後期因爲業務已經開發完了,如果要進行大修改,又要回歸測試所有的功能,最後只能不了了之。入坑後出坑的代價太大了,所以想分享一些日常開發心得,也希望童鞋們不要走我的老路。好了廢話不多說,開始今天入坑血淚史介紹。
一、請使用對象參數
爲什麼方法參數最好用對象呢,核心原因就是爲了好擴展,特別是service服務,每一個方法可能都會有很多地方引用,如果方法參數發生變動,那意味着所有調用這個方法的地方,都得進行修改,這個工作量可想而知。
下面我們來舉一個例子:
public class PersonService {
public int getTreasureIndicator(int age) {
if (age < 20) {
return 5;
} else {
return 10;
}
}
}
public class Child {
public int getTreasureIndicator() {
PersonService personService = new PersonService();
return personService.getTreasureIndicator(10);
}
}
public class Old {
public int getTreasureIndicator() {
PersonService personService = new PersonService();
return personService.getTreasureIndicator(50);
}
}
public class Test {
public static void main(String[] args) {
Child child = new Child();
Old old = new Old();
System.out.println("child:" + child.getTreasureIndicator());
System.out.println("old:" + old.getTreasureIndicator());
}
}
child:5
old:10
咋一看好像沒什麼問題,但是後續需求如果修改成,小孩指標計算不變,但是老人的指標需要加上性別這個字段呢?理想狀態是隻要修改老人的請求參數和服務類就可以了,但是現在因爲只有一個參數,如果加上另外一個參數,就意味着所有的引用到這個方法的地方都需要修改。這還是兩個,要是10個呢?瞬間想死的心都有了。
public class PersonService {
public int getTreasureIndicator(PersonDto personDto) {
if (personDto.getAge() >= 20 && personDto.getSex()==1) {
return 8;
} else if(personDto.getAge() >= 20 && personDto.getSex()==2){
return 10;
}else {
return 5;
}
}
}
public class Child {
public int getTreasureIndicator() {
PersonService personService = new PersonService();
PersonDto personDto = new PersonDto();
personDto.setAge(10);
return personService.getTreasureIndicator(personDto);
}
}
public class Old {
public int getTreasureIndicator() {
PersonService personService = new PersonService();
PersonDto personDto = new PersonDto();
personDto.setAge(50);
personDto.setSex(1);
return personService.getTreasureIndicator(personDto);
}
}
public class Test {
public static void main(String[] args) {
Child child = new Child();
Old old = new Old();
System.out.println("child:" + child.getTreasureIndicator());
System.out.println("old:" + old.getTreasureIndicator());
}
}
child:5
old:8
如果我們用對象作爲接口的參數,就可以很大程度上,避免這個問題,就算需要添加新的判斷類型,只要在對象中新增一個字段,然後修改各自的代碼即可。
二、常量請使用枚舉:
上面的代碼有一個性別這個字段,如果我對這個字段沒有添加任何註解,大家是不是都不知道這個字段的含義,一方面是可讀性太差,另一方面因爲沒有做限制,客戶端就是隨意設置值,安全方面也有很大的隱患。
如果我們修改爲用枚舉來標識性別呢?大家來看一下代碼:
public class PersonService {
public int getTreasureIndicator(PersonDto personDto) {
if (personDto.getAge() >= 20 && personDto.getSex()==SexEnum.SEX_GIRL.getSex()) {
return 8;
} else if(personDto.getAge() >= 20 && personDto.getSex()==SexEnum.SEX_BOY.getSex()){
return 10;
}else {
return 5;
}
}
}
public class Old {
public int getTreasureIndicator() {
PersonService personService = new PersonService();
PersonDto personDto = new PersonDto();
personDto.setAge(50);
personDto.setSex(SexEnum.SEX_GIRL.getSex());
return personService.getTreasureIndicator(personDto);
}
}
public enum SexEnum {
SEX_GIRL(1), SEX_BOY(2);
/**
* 性別
*/
private int sex;
SexEnum(int sex) {
this.sex = sex;
}
public int getSex() {
return sex;
}
public void setSex(int sex) {
this.sex = sex;
}
}
從枚舉的英文單詞中就可以馬上讀懂性別是什麼,而且設置值的時候也不需要判斷1、2到底是什麼性別了,直接設置對應的英文名稱即可,安全又可讀。
三、請不要讓方法喫成胖子
很多人開發的時候很喜歡,把一整個業務寫一個方法裏面,導致方法的代碼又多又長,過一段時間,自己修改都有吐血的衝動,下面我們來看看這種神級代碼。
public class PersonService {
public int getTreasureIndicator(PersonDto personDto) {
int result = 0;
// 計算年齡得分
if (personDto.getAge() < 20) {
result += 5;
} else if (personDto.getAge() >= 20) {
result += 10;
}
// 計算性別得分
if (personDto.getSex() == SexEnum.SEX_GIRL.getSex()) {
result += 5;
} else if (personDto.getSex() == SexEnum.SEX_BOY.getSex()) {
result += 10;
}
// 計算家庭得分
if (personDto.getFamilyMembers() < 5) {
result += 5;
} else if (personDto.getFamilyMembers() >= 5) {
result += 10;
}
// 計算顏值得分
if (personDto.getFaceScore() < 5) {
result += 5;
} else if (personDto.getFaceScore() >= 5) {
result += 10;
}
// 計算身高得分
if (personDto.getHeight() < 170) {
result += 5;
} else if (personDto.getHeight() >= 170) {
result += 10;
}
return result;
}
}
就比如上面的例子,如果財富的計算方式很複雜的化,所有的計算邏輯全部寫在一個方法裏面,就會顯得方法很臃腫,代碼可讀性差不說,如果沒有相關的註解,修改都不知道從何入手。我們將上面的計算公式拆分爲一個個函數,來看看會不會有什麼改變。
public class PersonService {
public int getTreasureIndicator(PersonDto personDto) {
int result = 0;
result += getAgeScore(personDto);
result += getSexScore(personDto);
result += getFamilyMembersScore(personDto);
result += getFaceScore(personDto);
result += getHeightScore(personDto);
return result;
}
private int getAgeScore(PersonDto personDto){
int result = 0;
if (personDto.getAge() < 20) {
result += 5;
} else if (personDto.getAge() >= 20) {
result += 10;
}
return result;
}
private int getSexScore(PersonDto personDto){
int result = 0;
if (personDto.getSex() == SexEnum.SEX_GIRL.getSex()) {
result += 5;
} else if (personDto.getSex() == SexEnum.SEX_BOY.getSex()) {
result += 10;
}
return result;
}
private int getFamilyMembersScore(PersonDto personDto){
int result = 0;
if (personDto.getFamilyMembers() < 5) {
result += 5;
} else if (personDto.getFamilyMembers() >= 5) {
result += 10;
}
return result;
}
private int getFaceScore(PersonDto personDto){
int result = 0;
if (personDto.getFaceScore() < 5) {
result += 5;
} else if (personDto.getFaceScore() >= 5) {
result += 10;
}
return result;
}
private int getHeightScore(PersonDto personDto){
int result = 0;
if (personDto.getHeight() < 170) {
result += 5;
} else if (personDto.getHeight() >= 170) {
result += 10;
}
return result;
}
}
如果將各個計算方式細分之後,就算我們不添加任何的註解,也可以明顯看出計算公式是由什麼組成的,每個公式的計算細節是什麼。代碼的可讀性和可維護性大大增強了。
四、請不要一層層嵌套if-else
祖傳老代碼中,很多都有非常多的if-else嵌套,跳來跳去,這也是爲什麼祖傳代碼幾乎修改不了的一個很大原因,比如上述財富值計算是各個維護分開的,如果是各個維度相互關聯的呢,直接實現會變成什麼樣子呢?
public class PersonService {
public int getTreasureIndicator(PersonDto personDto) {
int result = 0;
if (personDto.getAge() < 20) {
if (personDto.getSex() == SexEnum.SEX_GIRL.getSex()) {
if (personDto.getFamilyMembers() < 5) {
if (personDto.getFaceScore() < 5) {
if (personDto.getHeight() < 170) {
result += 5;
} else if (personDto.getHeight() >= 170) {
result += 10;
}
} else if (personDto.getFaceScore() >= 5) {
if (personDto.getHeight() < 170) {
result += 5;
} else if (personDto.getHeight() >= 170) {
result += 10;
}
}
} else if (personDto.getFamilyMembers() >= 5) {
if (personDto.getFaceScore() < 5) {
if (personDto.getHeight() < 170) {
result += 5;
} else if (personDto.getHeight() >= 170) {
result += 10;
}
} else if (personDto.getFaceScore() >= 5) {
if (personDto.getHeight() < 170) {
result += 5;
} else if (personDto.getHeight() >= 170) {
result += 10;
}
}
}
} else if (personDto.getSex() == SexEnum.SEX_BOY.getSex()) {
if (personDto.getFamilyMembers() < 5) {
if (personDto.getFaceScore() < 5) {
if (personDto.getHeight() < 170) {
result += 5;
} else if (personDto.getFaceScore() >= 170) {
result += 10;
}
} else if (personDto.getFaceScore() >= 5) {
if (personDto.getHeight() < 170) {
result += 5;
} else if (personDto.getFaceScore() >= 170) {
result += 10;
}
}
} else if (personDto.getFamilyMembers() >= 5) {
if (personDto.getFaceScore() < 5) {
if (personDto.getHeight() < 170) {
result += 5;
} else if (personDto.getHeight() >= 170) {
result += 10;
}
} else if (personDto.getFaceScore() >= 5) {
if (personDto.getHeight() < 170) {
result += 5;
} else if (personDto.getHeight() >= 170) {
result += 10;
}
}
}
}
} else if (personDto.getAge() >= 20) {
if (personDto.getSex() == SexEnum.SEX_GIRL.getSex()) {
if (personDto.getFamilyMembers() < 5) {
if (personDto.getFaceScore() < 5) {
if (personDto.getHeight() < 170) {
result += 5;
} else if (personDto.getHeight() >= 170) {
result += 10;
}
} else if (personDto.getFaceScore() >= 5) {
if (personDto.getHeight() < 170) {
result += 5;
} else if (personDto.getHeight() >= 170) {
result += 10;
}
}
} else if (personDto.getFamilyMembers() >= 5) {
if (personDto.getFaceScore() < 5) {
if (personDto.getHeight() < 170) {
result += 5;
} else if (personDto.getHeight() >= 170) {
result += 10;
}
} else if (personDto.getFaceScore() >= 5) {
if (personDto.getHeight() < 170) {
result += 5;
} else if (personDto.getHeight() >= 170) {
result += 10;
}
}
}
} else if (personDto.getSex() == SexEnum.SEX_BOY.getSex()) {
if (personDto.getFamilyMembers() < 5) {
if (personDto.getFaceScore() < 5) {
if (personDto.getHeight() < 170) {
result += 5;
} else if (personDto.getHeight() >= 170) {
result += 10;
}
} else if (personDto.getFaceScore() >= 5) {
if (personDto.getHeight() < 170) {
if (personDto.getHeight() < 170) {
result += 5;
} else if (personDto.getHeight() >= 170) {
result += 10;
}
} else if (personDto.getFaceScore() >= 170) {
if (personDto.getHeight() < 170) {
result += 5;
} else if (personDto.getHeight() >= 170) {
result += 10;
}
}
}
} else if (personDto.getFamilyMembers() >= 5) {
if (personDto.getFaceScore() < 5) {
if (personDto.getHeight() < 170) {
result += 5;
} else if (personDto.getHeight() >= 170) {
result += 10;
}
} else if (personDto.getFaceScore() >= 5) {
if (personDto.getHeight() < 170) {
result += 5;
} else if (personDto.getHeight() >= 170) {
result += 10;
}
}
}
}
}
return result;
}
}
看到這樣的代碼就問你絕望不,一般來說,我們的if-else結構最好不要超過三層,因爲如果超過三層的話,代碼可讀性已經變動特別差了。if-else的優化,網上有很多方法,博主這邊就不多做介紹了,最有效的就是將if-else進行拆分,由各個函數自己去實現,這樣就可以最大程度避免多層嵌套了。
五、請不要在一個服務中做太多事情
博主遇到過很多之前定義了一個服務類,然後之後和這個服務類相關的業務,就全部寫到這個服務類中,最後導致服務類代碼超過3000行,改也改不動,因爲引用的地方實在是太多了,根本沒辦法知道這個服務類到底做了哪些事情,跟黑盒一樣。就比如我們上面的服務類,如果新加一個健康指標計算的方法,我相信很多人會直接在PersonService中直接添加,例如:
public class PersonService {
public int getTreasureIndicator(PersonDto personDto) {
int result = 0;
result += getAgeScore(personDto);
result += getSexScore(personDto);
result += getFamilyMembersScore(personDto);
result += getFaceScore(personDto);
result += getHeightScore(personDto);
return result;
}
public int getHealthIndicator(PersonDto personDto) {
int result = 0;
result += getAgeScore(personDto);
result += getSexScore(personDto);
result += getFamilyMembersScore(personDto);
result += getFaceScore(personDto);
result += getHeightScore(personDto);
return result;
}
private int getAgeScore(PersonDto personDto){
int result = 0;
if (personDto.getAge() < 20) {
result += 5;
} else if (personDto.getAge() >= 20) {
result += 10;
}
return result;
}
private int getSexScore(PersonDto personDto){
int result = 0;
if (personDto.getSex() == SexEnum.SEX_GIRL.getSex()) {
result += 5;
} else if (personDto.getSex() == SexEnum.SEX_BOY.getSex()) {
result += 10;
}
return result;
}
private int getFamilyMembersScore(PersonDto personDto){
int result = 0;
if (personDto.getFamilyMembers() < 5) {
result += 5;
} else if (personDto.getFamilyMembers() >= 5) {
result += 10;
}
return result;
}
private int getFaceScore(PersonDto personDto){
int result = 0;
if (personDto.getFaceScore() < 5) {
result += 5;
} else if (personDto.getFaceScore() >= 5) {
result += 10;
}
return result;
}
private int getHeightScore(PersonDto personDto){
int result = 0;
if (personDto.getHeight() < 170) {
result += 5;
} else if (personDto.getHeight() >= 170) {
result += 10;
}
return result;
}
}
public class Child {
public int getTreasureIndicator() {
PersonService personService = new PersonService();
PersonDto personDto = new PersonDto();
personDto.setAge(10);
return personService.getTreasureIndicator(personDto);
}
public int getHealthIndicator() {
PersonService personService = new PersonService();
PersonDto personDto = new PersonDto();
personDto.setAge(10);
return personService.getHealthIndicator(personDto);
}
}
public class Old {
public int getTreasureIndicator() {
PersonService personService = new PersonService();
PersonDto personDto = new PersonDto();
personDto.setAge(50);
personDto.setSex(SexEnum.SEX_GIRL.getSex());
return personService.getTreasureIndicator(personDto);
}
public int getHealthIndicator() {
PersonService personService = new PersonService();
PersonDto personDto = new PersonDto();
personDto.setAge(10);
return personService.getHealthIndicator(personDto);
}
}
public class Test {
public static void main(String[] args) {
Child child = new Child();
Old old = new Old();
System.out.println("child-treasure:" + child.getTreasureIndicator());
System.out.println("child-health:" + child.getHealthIndicator());
System.out.println("old-treasure:" + old.getTreasureIndicator());
System.out.println("old-health:" + old.getHealthIndicator());
}
}
child-treasure:20
child-health:20
old-treasure:30
old-health:20
然後相關需求一來,又要加一種類型,就會導致這個服務越來越大,大到最後你不可維護,變成傳說中的祖傳代碼,如果是如上這種情況,博主建議大家可以新建一個服務類,讓各個服務類的職責單一(單一指責原則)。
public class PersonService {
protected int getAgeScore(PersonDto personDto){
int result = 0;
if (personDto.getAge() < 20) {
result += 5;
} else if (personDto.getAge() >= 20) {
result += 10;
}
return result;
}
protected int getSexScore(PersonDto personDto){
int result = 0;
if (personDto.getSex() == SexEnum.SEX_GIRL.getSex()) {
result += 5;
} else if (personDto.getSex() == SexEnum.SEX_BOY.getSex()) {
result += 10;
}
return result;
}
protected int getFamilyMembersScore(PersonDto personDto){
int result = 0;
if (personDto.getFamilyMembers() < 5) {
result += 5;
} else if (personDto.getFamilyMembers() >= 5) {
result += 10;
}
return result;
}
protected int getFaceScore(PersonDto personDto){
int result = 0;
if (personDto.getFaceScore() < 5) {
result += 5;
} else if (personDto.getFaceScore() >= 5) {
result += 10;
}
return result;
}
protected int getHeightScore(PersonDto personDto){
int result = 0;
if (personDto.getHeight() < 170) {
result += 5;
} else if (personDto.getHeight() >= 170) {
result += 10;
}
return result;
}
}
public class PersonHealthService extends PersonService{
public int getHealthIndicator(PersonDto personDto) {
int result = 0;
result += getAgeScore(personDto);
result += getSexScore(personDto);
result += getFamilyMembersScore(personDto);
result += getFaceScore(personDto);
result += getHeightScore(personDto);
return result;
}
}
public class PersonTreasureService extends PersonService{
public int getTreasureIndicator(PersonDto personDto) {
int result = 0;
result += getAgeScore(personDto);
result += getSexScore(personDto);
result += getFamilyMembersScore(personDto);
result += getFaceScore(personDto);
result += getHeightScore(personDto);
return result;
}
}
因爲兩個服務有很多的共性代碼,所以直接抽出來作爲他們的父類,所以也就有上面的代碼了,這樣以後有新的業務進來,只需要創建對應的服務類就可以了(開閉原則)。
六、請規範好請求參數
在寫接口的時候,很多人會遇到該用基礎類型的字段呢(int、String之類)還是使用對象來接收呢,如果用對象來接收,大家就會傾向於,後續相關的接口還是直接用這個對象來接收,這就會導致對象的字段越來越多,最後造成前端每次都會過來問,這個接口應該傳哪些字段。。。
分層領域模型規約:
- DO(Data Object):與數據庫表結構一一對應,通過 DAO 層向上傳輸數據源對象。(Entity)
- DTO(Data Transfer Object):數據傳輸對象,Service 和 Manager 向外傳輸的對象。
- BO(Business Object):業務對象。可以由 Service 層輸出的封裝業務邏輯的對象。
- QUERY:數據查詢對象,各層接收上層的查詢請求。注:超過 2 個參數的查詢封裝,禁止 使用 Map 類來傳輸。
- VO(View Object):顯示層對象,通常是 Web 向模板渲染引擎層傳輸的對象。
接口的請求參數要準確,前端不需要傳的字段,最好不要暴露出去,這樣也會減少雙方的溝通成本,而且後續升級接口的時候,也可以確定參數的各自含義。
@Controller
@RequestMapping("/api/person")
@Slf4j
public class PersonController {
@ResponseBody
@PostMapping("/getChildHealthIndicator")
public int getChildHealthIndicator(@RequestBody PersonDto personDto) {
Child child = new Child();
return child.getHealthIndicator(personDto.getAge());
}
}
就比如getChildHealthIndicator接口只需要age值,但是前端那邊看到卻是personDto對象,這樣就會對剩餘的參數產生困擾。
@Controller
@RequestMapping("/api/person")
@Slf4j
public class PersonController {
@ResponseBody
@PostMapping("/getChildHealthIndicator")
public int getChildHealthIndicator(@RequestBody ChildHealthIndicatorQuery childHealthIndicatorQuery) {
Child child = new Child();
return child.getHealthIndicator(childHealthIndicatorQuery.getAge());
}
}
@Data
public class ChildHealthIndicatorQuery {
private int age;
}
如果用ChildHealthIndicatorQuery來代替PersonDto,這樣前端看到的就只有age這個字段,可以無歧義的進行傳參。當然正常開發過程中,肯定不會設計的這麼細,不然會產生大量的pojo對象,要根據實際項目來設計。
七、entity中請不要做參數校驗
我以前遇到同事,直接在entity(數據庫表映射類)裏面做判斷,然後另外一個同事,因爲數據庫新增了字段,所以又重新生成了一遍entity類(mybatis插件生成),寫完業務代碼後發佈,最後結果可想而知。
對於entity類,是絕對禁止任何層面的判斷修改的,因爲這個類隨時都有可能被覆蓋。
八、http返回碼請不要直接返回一個字段
很多童鞋剛開始寫代碼的時候,很喜歡接口業務需要返回什麼內容,就直接返回數據,例如上面的案例就是直接返回一個int值。
@Controller
@RequestMapping("/api/person")
@Slf4j
public class PersonController {
@ResponseBody
@PostMapping("/getChildHealthIndicator")
public int getChildHealthIndicator(@RequestBody ChildHealthIndicatorQuery childHealthIndicatorQuery) {
Child child = new Child();
return child.getHealthIndicator(childHealthIndicatorQuery.getAge());
}
}
但是大家要知道,任何的接口都有出問題的可能性,而且問題還不止一種,可能是五花八門,前端可能需要根據不同的錯誤進行相應的提示,所以直接返回一個字段數據肯定是不合理的。一般來說,至少需要返回三個字段:code(狀態碼)、msg(狀態碼對應的說明)、data(接口的業務數據),我們將上面的接口修改一下。
@Controller
@RequestMapping("/api/person")
@Slf4j
public class PersonController {
@ResponseBody
@PostMapping("/getChildHealthIndicator")
public CustResponse getChildHealthIndicator(@RequestBody ChildHealthIndicatorQuery childHealthIndicatorQuery) {
Child child = new Child();
CustResponse custResponse = new CustResponse();
try{
int result = child.getHealthIndicator(childHealthIndicatorQuery.getAge());
custResponse.setCode(200);
custResponse.setMsg("success");
custResponse.setDetails(result);
}catch (Exception e){
custResponse.setCode(500);
custResponse.setMsg(e.getMessage());
}
return custResponse;
}
}
@Data
@ApiModel
public class CustResponse {
/**
* 200 成功
* 500 失敗
*/
@ApiModelProperty(value = "狀態碼,200 = '成功',500 = '失敗'")
private Integer code;
@ApiModelProperty(value = "返回消息")
private String msg;
@ApiModelProperty(value = "返回實體")
private Object details;
public CustResponse() {
super();
}
/**
* @param code
* @param msg
*/
public CustResponse(Integer code, String msg) {
super();
this.code = code;
this.msg = msg;
}
/**
* @param code
* @param details
*/
public CustResponse(Integer code, Object details) {
super();
this.code = code;
this.details = details;
}
}
九、請定義好異常的狀態碼
很多童靴拋異常,很喜歡直接拋出RuntimeException異常,然後就一串異常信息,想看這個異常是屬於哪一個服務,哪一塊業務,核心參數等,都沒有。。。排查問題難度直線上升。
所以定義異常的時候一般需要定義code+msg(可以根據自己需求添加),例如:
/**
* 基礎異常類
* 用途:用於在處理業務時,向框架拋出異常,框架將該異常作爲錯誤信息進行輸出。<br>
* 使用場景:比如在深層業務代碼判斷參數無正確,就可以直接拋出該異常。<br>
* <pre>
* code rule:{2}{2}{4}
* | | |
* sys | |
* module|
* error
* 如:[10020001]前四位數爲系統+模塊編號,後4位爲錯誤代碼
*
* @Author: linzhiqiang
*/
@Data
public class BaseException extends RuntimeException implements Serializable {
private static final long serialVersionUID = -5196111058043675557L;
/**
* 參數不能爲空
**/
public static final BaseException PARAMS_NOT_NULL = new BaseException(10000000, "參數{0}不能爲空");
/**
* 參數非法
**/
public static final BaseException PARAMS_IS_ILLICIT = new BaseException(10000001, "參數{0}非法");
private Integer code;
private String msg;
public BaseException() {
super();
}
public BaseException(String msg) {
super(msg);
this.msg = msg == null ? "" : msg;
}
public BaseException(Integer code, String msg) {
super(msg);
this.code = code;
this.msg = msg == null ? "" : msg;
}
public BaseException(Integer code, String msg, Integer userId) {
super(msg);
this.code = code;
msg = msg == null ? "" : msg;
this.msg = userId + ":" + msg;
}
/**
* 格式化消息
*
* @param args
*/
public BaseException format(Object... args) {
return new BaseException(this.code, MessageFormat.format(this.msg, args));
}
}
public int getHealthIndicator(int age) {
if (age < 0) {
BaseException baseException = new BaseException(01010001, "年齡不能小於0");
throw baseException;
}
PersonHealthService personService = new PersonHealthService();
PersonDto personDto = new PersonDto();
personDto.setAge(age);
return personService.getHealthIndicator(personDto);
}
通過這種方式拋出的異常,我們就可以很明顯的找到這個異常對應的服務、模塊、錯誤內容,加快我們排查效率。
十、不要吝嗇,請多打點錯誤日誌
很多童靴寫代碼沒有打印日誌的習慣,可能很多時候因爲是本地開發,出問題的時候直接調試一下就ko bug了,但是到線上的時候可沒辦法進行錯誤調試,甚至有些錯誤是瞬間性的,下一刻你就復現不出來了,所以如果沒有日誌記錄這些錯誤信息,那就相當於蒙着眼睛過河一樣,岌岌可危。
總結:
日常開發經驗就分享到這邊了,當然還有很多很多的場景博主沒有說,這得靠童鞋們平時的積累了。博客的爬坑歷程就到這邊了,對上述有疑問或者想分享交流的童靴,歡迎隨時@博主哦~
想要更多幹貨、技術猛料的孩子,快點拿起手機掃碼關注我,我在這裏等你哦~
林老師帶你學編程:https://wolzq.com