代码优化
这里无限期做更新。 还是由于本科生比较菜的原因吧,学校里没人教这些,只能偶尔跟小伙伴聊到某些很奇怪的地方怎么写的时候,才能学到。毕竟老师都懒得看你的代码。 可能自己逛github、stackoverflow少了吧orz。 因为自己主要写Java,所以下面就拿Java做例子吧
数据层
1. 数据接收Request
数据接收就比较简单了,因为 Spring 有自带的注解,@RequestBody,@ReuqestParam和@RequestHeader,所以只要打上注解就好了。
但是实际上,这里有个巨坑,一直无法理解,就是@ReuqestBody与@ReuqestParam无法一起使用,也就是说,当你希望通过一个实体对象接收数据,用到@ReuqestBody Data data,的时候,你就没有办法使用@RueqestParam接收其他参数了。 于是找了一种解决方法,通过原生的HttpServletRequest的getParam方法来接收,且这些参数必须待在URL上,否则后台将会接不到。于是有了下面的代码。
public Result cloneActivity(@RequestBody ActivityDto activityDto,
@RequestHeader("Authorization") String token,
HttpServletRequest request) throws Exception {
String acid = request.getParameter("acid");
Integer options = Integer.valueOf(request.getParameter("options"));
return activityCloneService.cloneActivity(acid, activityDto, options);
}
期初呢感觉一点毛病都没有的,因为实在没办法了,接不到参数啊。 实际上只是因为自己懒得再封装多一层数据层,因为觉得每一个接口都要自定义一个数据进行@RequestBody的接收,感觉要命的啊。 但是自己封装了个,这样的类之后,好像这个就没啥毛病了。
public class RequestData<T> {
private T data;
private Map<String, Object> map;
public static <E> getMapParameter(String key, Class<E> clz) throw ReuqestDataException {
Object obj = map.getOrDefault(key, null);
if (obj == null) {
throw new ReuqestDataException(Result.fail("参数不能为空", ErrorCode.BAD_REQUEST));
}
if (clz.isInstance(obj)) {
return clz.cast(obj);
}
throw new ReuqestDataException(Result.fail("参数类型不正确", ErrorCode.BAD_REQUEST));
}
// get set ...
}
2. 数据返回Response
返回层其实相对比较简单。但是这上面也是经过了很头疼的脑洞时间 最初写项目的时候,返回的数据报有:
// 正确返回案例
return "返回数据成功"
然后发现没有错误案例,就成了:
// 正确返回案例
Map<String, Object> map = new HashMap<>();
map.put("msg", "成功返回");
map.put("anyData", data); // 注意这里的anyData,这个变量名是自定义的
return map;
// 错误返回案例
Map<String, Object> map = new HashMap<>();
map.put("errMsg", "错误返回");
map.put("errCode", "错误码");
return map;
总觉得这样好像,没啥毛病了。 但是头疼的地方来了,当时这个项目是两个人写的。竟然errorMessage写出了。不知道多少种写法 有:errMsg,errorMsg,errorMessage.. 本人竟然某天晚上一个个搜把所有的这些都改统一了。改统一了!!?? 不得不说当时有多蠢。
直到又换了个项目,才把这里给妥妥的封装成了:
public static Result Success = success(null);
public static Result False = fail("接口未实现", ErrorCode.INTERFACE_NOT_FINISH);
@ApiModelProperty(value = "结果说明 true = 成功 false = 失败 ", allowableValues = "true,false", required = true)
//参数说明 参数肯定会存在
private boolean status;
@ApiModelProperty("提示信息")
private String message;
@ApiModelProperty("错误信息")
private String errorMsg;
@ApiModelProperty("错误代码")
private int errorCode;
@ApiModelProperty("操作返回的数据结果")
private Object data;
就大概的省略一下自定义封装的构造函数吧 感觉也还是不错的是吧。 但是问题又来了。这些数据究竟实在服务层(service)上返回呢,还是在控制层(Controller)返回呢。 这个问题我纠结了很久,但是因为有业务错误情况呀,如果我在控制层返回,那么我错误的业务逻辑,应该如何判断呢,那么我的控制层是不是又会多很多奇奇怪怪的东西呢。毕竟控制层只是相当于转发路由的。 于是服务层代码大概就这样的:
public Result deleteActivityAtlas(String acid, String pid) throws Exception {
ActivityAtlasDto activityAtlasDto = checkActivityAtlasBelongToActivity(acid, pid);
if (activityAtlasDto == null) {
return Result.fail("没有操作权限", ErrorCode.FORBIDDEN);
}
activityAtlasRepository.delete(activityAtlasDto);
return Result.success("删除图片成功");
}
这个砖就这么把项目基本搬完了。也没人觉得他很怪异。只是看起来Service层的复用性,不太好,耦合度有那么点高。 直至某一天跟小伙伴(开发大佬)聊天,问他这一块怎么做的。 他说,我直接把业务当异常抛出来啊。orz 抛出来??!然后全局异常处理器一接,美滋滋。 然后代码差不多就成了这样了。
public String updateActivityAtlas(ActivityAtlasDto activityAtlasDto) throws ResultException, Exception {
ActivityAtlasDto correctActivityAtlasDto = checkActivityAtlasBelongToActivity(activityAtlasDto.getAcid(), activityAtlasDto.getPid());
if (correctActivityAtlasDto == null) {
throw new ResultException(Result.fail("没有操作权限", ErrorCode.FORBIDDEN));
}
UpdateUtil.copyNonNullProperties(activityAtlasDto, correctActivityAtlasDto);
activityAtlasRepository.save(correctActivityAtlasDto);
return "更新图片成功";
}
emmm,大概服务端的Reuqest和Response就那么多了吧。 如果还有能优化的地方,待补充ORZ
数据实体
如果平时大家用MyBits或者Hibernate,那么应该一定会有这样的情况吧。 某些字段,我不想传给前端。 某些字段,我不想从后台搜出来。 那么这种时候,从数据设计上,就出现了DTO和VO。DO(Entity) 这里就不做解释了吧。就是元数据对象。
元数据为DO,而DTO和VO都是可以直接从DO中继承的。 DTO是专门跟数据库持久层打交道的数据封装对象。 VO是专门跟前台打交道的数据封装对象。
对于VO
- 不需要的属性 对于不需要的属性,这边的做法是继承后,把那个属性在VO类中重写了,并加上@JsonIgnore这个注解,即可在该类序列化的时候将其无视。不传给前端
- 从DTO转过来的属性 往往从DTO转过来的属性很奇怪,比如一些属性是个对象,对象里的属性需要显示到前台。 栗子如下:
public class DataDto {
private ChildrenDataDto childrenDataDto;
private String message;
// set get ...
}
class ChildrenDataDto {
private String childrenMessage;
// set get ...
}
你给前端的时候,前端总不希望,他要childrenDataDto.childrenMessage把这个属性取出吧。 这时候,我们可以做个mapper文件,将childrenDataDto.childrenMessage 映射成 childrenMessage。然后通过反射的原理,将DTO转成VO,这样一想,是不是美滋滋。 然而这种想法,早有大神实现了,Dozer工具类(基于BeanUtils)。 只需要通过
dozerFactory.convert();
这个方法即可实现转换。
这样子设计是不是顿时感觉,VO原来那么好做
对于DTO
DTO就不太好优化了,现在的优化思想有两个。 首选我们要了解清楚,DTO到底是干啥的,简单来说,就是跟数据库打交道的。 单表查询的DTO其实基本上可以等同于DO,但是连表查询的时候,DTO就会变得极其复杂。 而往往,我们需要的DTO是各式各样的,从数据库中搜出来的也是各式各样。 我对DTO的优化想法如下
- 子类继承 父类只提供基础的一些数据 而子类则实现更多的属性。更为具体
- 通过@Transient注解 这个注解可以无视掉某些字段不进入数据库的SQL查询。 可以同VO一样。继承下来再重写修改啥的。 其他的DTO。就老老实实写吧哈哈哈哈哈哈哈。