java开发之消除冗余代码的3种方法
一.利用工厂模式+模板方法模式
我们以做蛋糕为例来演示如何消除重复代码。
假设我们要做3种不同口味的蛋糕,分别是抹茶,可可和草莓蛋糕,实际上3种蛋糕的制作方法是极其相似的,只有添加的粉剂不
同,如果用代码来实现蛋糕制作流程,要写大量重复代码,容易产生BUG,我们可以使用工厂模式和模板方法模式来避免重复。
首先定义一个蛋糕类Cake:
@Data
public class Cake {// 蛋糕名称String cakeName;Integer sugar;Integer eggs;Integer flour;// 添加剂(可可粉,抹茶粉,草莓)String supplement;
}
再定义一个制作蛋糕的抽象父类:
@Service
public abstract class AbstractCakeService {//处理做蛋糕的重复逻辑在父类实现public Cake doCake(){Cake cake = new Cake();cake.setEggs(2);cake.setFlour(250);cake.setSugar(30);//让子类实现不同的蛋糕处理addOtherMaterial(cake);return cake;}// 不同属性的赋值留给子类实现protected abstract void addOtherMaterial(Cake cake);}
我们定义3个不同的子类:抹茶蛋糕,可可蛋糕,草莓蛋糕制作,他们都继承抽象的父类AbstractCakeService,分别为
TeaCakeService,CocoaCakeService,StrawberryCakeService。
抹茶蛋糕TeaCakeService的实现:
@Service(value = "TeaCakeService")
public class TeaCakeService extends AbstractCakeService{@Overrideprotected void addOtherMaterial(Cake cake) {cake.setCakeName("抹茶蛋糕");cake.setSupplement("抹茶粉");System.out.println("当前正在制作好吃的抹茶蛋糕");}}
可可蛋糕CocoaCakeService的实现:
@Service(value = "CocoaCakeService")
public class CocoaCakeService extends AbstractCakeService{@Overrideprotected void addOtherMaterial(Cake cake) {cake.setCakeName("可可蛋糕");cake.setSupplement("可可粉");System.out.println("当前正在制作好吃的可可蛋糕");}}
草莓蛋糕CocoaCakeService的实现:
@Service(value = "StrawberryCakeService")
public class StrawberryCakeService extends AbstractCakeService{@Overrideprotected void addOtherMaterial(Cake cake) {cake.setCakeName("草莓蛋糕");cake.setSupplement("新鲜草莓");System.out.println("当前正在制作好吃的草莓蛋糕");}}
3种蛋糕制作都叫 XXXCakeService,那我们就可以把蛋糕类型字符串拼接 CakeService构成 Bean 的名称,然后利用 Spring 的
IoC 容器,通过 Bean 的名称直接获取到 AbstractCakeService,调用其 doCake方法来实现不同蛋糕的制作调用,这就是借助
Spring 容器实现了工厂模式。
调用的控制层代码如下:
@RestController
public class cakeController {@Autowiredprivate ApplicationContext applicationContext;@GetMapping("doCake")public Cake doCake(@RequestParam("supplement") String supplement) {AbstractCakeService cake = (AbstractCakeService) applicationContext.getBean(supplement + "CakeService");return cake.doCake();}
}
传入需要的添加剂,实现蛋糕的制作:
这样一来,我们利用工厂模式 + 模板方法模式,不仅消除了重复代码,还避免了修改既有代码的风险。这就是设计模式中的开闭
原则:对修改关闭,对扩展开放。
git代码实现路径:
https://github.com/jichunyang19931023/dailyDemo/tree/master/cake
二.利用注解 + 反射消除重复代码
假如我们要和外部系统进行接口服务对接,需要一个接口来输出接口的定义和传参等信息,此时我们可以利用注解+反射的机制来解决这个问题。
如果有一个创建票据的接口需要生成API的信息,要实现接口逻辑和逻辑实现的剥离,首先需要以 POJO 类(只有属性没有
任何业务逻辑的数据类)的方式定义所有的接口参数,如下所示:
@Data
public class CreateTicketAPI {// 用户名String userName;// 密码String password;// 当前时间的时间戳Long currentTime;
}
我们可以通过自定义注解为接口和所有参数增加一些元数据。如下所示,我们定义一个接口的注解 API,包含接口接口说明, URL 地址和请求方式:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Inherited
public @interface API {String desc() default "";String url() default "";String type() default "GET";
}
然后,我们再定义一个自定义注解 @APIField,用于描述接口的每一个字段规范,包含参数的可否为空、类型和说明三个属性:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@Documented
@Inherited
public @interface APIField {// 可否为空boolean isRequired() default false;// 参数类型String type() default "";// 参数说明,备注String remark() default "";
}
修改原始的POJO 类,补充接口和每个参数字段的元数据(也就是对应的属性相关信息),继承的 AbstractAPI 类是一个空实
现,这个案例中的接口并没有公共数据可以抽象放到基类:
@API(url = "/creatTicket", desc = "创建票据接口", type = "GET")
@Data
public class CreateTicketAPI extends AbstractAPI{@APIField(isRequired = true, type = "String", remark = "用户名称")String userName;@APIField(isRequired = true, type = "String", remark = "密码")String password;@APIField(isRequired = false, type = "Long", remark = "当前时间的时间戳")Long currentTime;
}
以上,我们通过注解实现了对 API 参数的描述。接下来,我们再看看反射如何配合注解实现接口属性和参数说明:
@Slf4j
@Service
public class APIService {public JSONObject apiCreate(AbstractAPI api){JSONObject result = new JSONObject();// 从API注解获取请求说明API apiObj = api.getClass().getAnnotation(API.class);result.put("apiDesc", apiObj.desc());result.put("apiUrl", apiObj.url());result.put("apiType", apiObj.type());JSONArray apiParams = JSONUtil.createArray();Arrays.stream(api.getClass().getDeclaredFields()) // 获得所有字段.filter(field -> field.isAnnotationPresent(APIField.class)) // 查找标记了注解的字段.peek(field -> field.setAccessible(true)) // 设置可以访问私有字段.forEach(field -> {JSONObject paramObj = new JSONObject();// 获得注解APIField apiField = field.getAnnotation(APIField.class);Object value = "";try {// 反射获取字段值value = field.get(api);} catch (IllegalAccessException e) {log.error("反射获取字段值发生异常", e);}paramObj.put("paramName", field.getName());paramObj.put("paramType", apiField.type());paramObj.put("isRequired", apiField.isRequired());paramObj.put("remark", apiField.remark());paramObj.put("paramValue", value);apiParams.add(paramObj);});result.put("apiParams", apiParams);return result;}
}
最后控制层进行调用:
@RestController
public class APIController {@Autowiredprivate APIService apiService;@GetMapping(value = "/getApi")public JSONObject getApi(String userName, String password, Long currentTime) {CreateTicketAPI ticketAPI = new CreateTicketAPI();ticketAPI.setUserName(userName);ticketAPI.setPassword(password);ticketAPI.setCurrentTime(currentTime);return apiService.apiCreate(ticketAPI);}
}
许多涉及类结构性的通用处理,都可以按照这个模式来减少重复代码。反射给予了我们在不知晓类结构的时候,按照固定的逻辑
处理类的成员;而注解给了我们为这些成员补充元数据的能力,使得我们利用反射实现通用逻辑的时候,可以从外部获得更多我
们关心的数据。
git代码实现路径:
https://github.com/jichunyang19931023/dailyDemo/tree/master/api
三.利用属性拷贝工具消除重复代码
对于三层架构的系统,考虑到层之间的解耦隔离以及每一层对数据的不同需求,通常每一层都会有自己的 POJO 作为数据实体。
比如,数据访问层的实体一般叫作 DataObject 或 DO,业务逻辑层的实体一般叫作 Domain,表现层的实体一般叫作 Data
Transfer Object 或 DTO。如果手动写这些实体之间的赋值代码,同样容易出错,所以我们可以利用属性拷贝工具来消除重复代
码。
使用类似 BeanUtils 这种 Mapping 工具来做 Bean 的转换,copyProperties 方法还允许我们提供需要忽略的属性:
ComplicatedOrderDTO orderDTO = new ComplicatedOrderDTO();
ComplicatedOrderDO orderDO = new ComplicatedOrderDO();
BeanUtils.copyProperties(orderDTO, orderDO, "id");
return orderDO;
或者可以使用一个很棒的工具包hutool来实现,下面是官方文档网址,支持不同类型的拷贝,十分方便:
https://www.hutool.cn/docs/#/
java开发之消除冗余代码的3种方法相关推荐
- java single instance_java单例模式(具体代码显现)两种方法
判断是否存在 /** * 懒汉式 */ public class LazySingleInstance { // 私有构造方法 private LazySingleInstance(){}; // 私 ...
- java冗余_Java使用lombok消除冗余代码的方法步骤
一.项目背景 在写Java程序的时候经常会遇到如下情形: 新建了一个Class类,然后在其中设置了几个字段,最后还需要花费很多时间来建立getter和setter方法. lombok项目的产生就是为了 ...
- 简化开发|Lombok神器带你消除冗余代码
前言 Lombok是一款Java开发插件,使得Java开发者可以通过其定义的一些注解来消除业务过程中冗余的代码,尤其是简单的Java模型对象(POJO).而当我们如果在开发环境中使用Lombok开发插 ...
- Java开发中经常使用到的几种WebService技术实现方案
Java开发中经常使用到的几种WebService技术实现方案 随着异构系统互联需求的不断增加,WebService的重要性也日益彰显出来.凭借webservice,我们可以实现基于不同程序语言的项目 ...
- Eclipse远程调试Java代码的三种方法
Eclipse远程调试Java代码的三种方法, 第1种方法是用来调试已经启动的Java程序,Eclipse可以随时连接到远程Java程序进行调试, 第2种方法可以调试Java程序启动过程,但是Ecli ...
- java property xml,Java开发中读取XML与properties配置文件的方法
相关阅读: 1. XML文件: 什么是XML?XML一般是指可扩展标记语言,标准通用标记语言的子集,是一种用于标记电子文件使其具有结构性的标记语言. 2.XML文件的优点: 1)XML文档内容和结构完 ...
- eclipse 远程调试java_Eclipse远程调试Java代码的三种方法
Eclipse远程调试Java代码的三种方法, 第1种方法是用来调试已经启动的Java程序,Eclipse可以随时连接到远程Java程序进行调试, 第2种方法可以调试Java程序启动过程,但是Ecli ...
- Java构造和解析Json数据的两种方法详解一
在www.json.org上公布了很多JAVA下的json构造和解析工具,其中org.json和json-lib比较简单,两者使用上差不多但还是有些区别.下面首先介绍用json-lib构造和解析Jso ...
- Java 判断字符串是否为空的四种方法、优缺点与注意事项
以下是Java 判断字符串是否为空的四种方法: 方法一: 最多人使用的一个方法, 直观, 方便, 但效率很低: if(s == null ||"".equals(s)); 方法二: ...
最新文章
- 在VS上配置OpenCV
- python aipspeech_Python调用百度API实现语音识别(二)
- git error(win下)
- msql查询指定日期
- EXCEL文件上传与下载
- [深度学习基础] 深度学习基础及数学原理
- 长江大学微型计算机课设报告,长江大学B第一学期计算机基础试卷.doc
- java学习笔记十二
- eclipse常用的快捷键
- [PYTHON] 深度解析copy.copy() 与 copy.deepcopy()
- javascript的运算(小结)
- python下载bt文件_python获取bt种子的详细信息
- Json-lib, 实现Java对象与JSON数据格式的互转
- 【IPM2020】一种处理多标签文本分类的新颖推理机制
- Linux 用户管理
- WS小世界网络python快速实现——调用networkx包
- 二、Esp32开发环境快速搭建(vscode+PlatformIO IED)
- world分节及分节首页分节页码总页码设置方法
- css3 animation 实现帧动画
- flutter系列之:如丝般顺滑的SliverAppBar