这几天开发遇到了很多坑,大部分坑都是自己前期没规划好,后期因为业务已经开发完了,如果要进行大修改,又要回归测试所有的功能,最后只能不了了之。入坑后出坑的代价太大了,所以想分享一些日常开发心得,也希望童鞋们不要走我的老路。好了废话不多说,开始今天入坑血泪史介绍。
一、请使用对象参数
为什么方法参数最好用对象呢,核心原因就是为了好扩展,特别是service服务,每一个方法可能都会有很多地方引用,如果方法参数发生变动,那意味着所有调用这个方法的地方,都得进行修改,这个工作量可想而知。
下面我们来举一个例子:
代码语言:javascript复制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个呢?瞬间想死的心都有了。
代码语言:javascript复制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
如果我们用对象作为接口的参数,就可以很大程度上,避免这个问题,就算需要添加新的判断类型,只要在对象中新增一个字段,然后修改各自的代码即可。
二、常量请使用枚举:
上面的代码有一个性别这个字段,如果我对这个字段没有添加任何注解,大家是不是都不知道这个字段的含义,一方面是可读性太差,另一方面因为没有做限制,客户端就是随意设置值,安全方面也有很大的隐患。
如果我们修改为用枚举来标识性别呢?大家来看一下代码:
代码语言:javascript复制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到底是什么性别了,直接设置对应的英文名称即可,安全又可读。
三、请不要让方法吃成胖子
很多人开发的时候很喜欢,把一整个业务写一个方法里面,导致方法的代码又多又长,过一段时间,自己修改都有吐血的冲动,下面我们来看看这种神级代码。
代码语言:javascript复制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;
}
}
就比如上面的例子,如果财富的计算方式很复杂的化,所有的计算逻辑全部写在一个方法里面,就会显得方法很臃肿,代码可读性差不说,如果没有相关的注解,修改都不知道从何入手。我们将上面的计算公式拆分为一个个函数,来看看会不会有什么改变。
代码语言:javascript复制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嵌套,跳来跳去,这也是为什么祖传代码几乎修改不了的一个很大原因,比如上述财富值计算是各个维护分开的,如果是各个维度相互关联的呢,直接实现会变成什么样子呢?
代码语言:javascript复制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中直接添加,例如:
代码语言:javascript复制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
然后相关需求一来,又要加一种类型,就会导致这个服务越来越大,大到最后你不可维护,变成传说中的祖传代码,如果是如上这种情况,博主建议大家可以新建一个服务类,让各个服务类的职责单一(单一指责原则)。
代码语言:javascript复制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 向模板渲染引擎层传输的对象。
接口的请求参数要准确,前端不需要传的字段,最好不要暴露出去,这样也会减少双方的沟通成本,而且后续升级接口的时候,也可以确定参数的各自含义。
代码语言:javascript复制@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对象,这样就会对剩余的参数产生困扰。
代码语言:javascript复制@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值。
代码语言:javascript复制@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(接口的业务数据),我们将上面的接口修改一下。
代码语言:javascript复制@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(可以根据自己需求添加),例如:
代码语言:javascript复制/**
* 基础异常类
* 用途:用于在处理业务时,向框架抛出异常,框架将该异常作为错误信息进行输出。<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了,但是到线上的时候可没办法进行错误调试,甚至有些错误是瞬间性的,下一刻你就复现不出来了,所以如果没有日志记录这些错误信息,那就相当于蒙着眼睛过河一样,岌岌可危。
总结:
日常开发经验就分享到这边了,当然还有很多很多的场景博主没有说,这得靠童鞋们平时的积累了。博客的爬坑历程就到这边了,对上述有疑问或者想分享交流的童靴,欢迎随时@博主哦~