Three methods of eliminating redundant code developed in Java

I Using factory mode + template method mode

Let's take cake making as an example to demonstrate how to eliminate duplicate code.

Suppose we want to make three kinds of cakes with different flavors, namely Matcha, cocoa and strawberry cake. In fact, the making methods of the three kinds of cakes are very similar, only the added powder is not enough

Similarly, if we use code to realize the cake making process, we need to write a lot of repeated code, which is easy to generate bugs. We can use factory mode and template method mode to avoid repetition.

First, define a cake class cake:

@Data
public class Cake {
	// 蛋糕名称
	String cakeName;
	
	Integer sugar;
	
	Integer eggs;
	
	Integer flour;
	
	// 添加剂(可可粉,抹茶粉,草莓)
	String supplement;
}

Then define an abstract parent class for making cakes:

@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);
    
}

We define three different subclasses: Matcha cake, cocoa cake and strawberry cake making. They all inherit the abstract parent class abstractcakeservice, which are

TeaCakeService,CocoaCakeService,StrawBerryCakeService。

Implementation of Matcha cake teacakeservice:

@Service(value = "TeaCakeService")
public class TeaCakeService extends AbstractCakeService{

	@Override
	protected void addOtherMaterial(Cake cake) {
		cake.setCakeName("抹茶蛋糕");
		cake.setSupplement("抹茶粉");
		System.out.println("当前正在制作好吃的抹茶蛋糕");
	}

}

Implementation of cocoa cake cocoacakeservice:

@Service(value = "CocoaCakeService")
public class CocoaCakeService extends AbstractCakeService{

	@Override
	protected void addOtherMaterial(Cake cake) {
		cake.setCakeName("可可蛋糕");
		cake.setSupplement("可可粉");
		System.out.println("当前正在制作好吃的可可蛋糕");
	}

}

Implementation of cocoacakeservice for strawberry cake:

@Service(value = "StrawBerryCakeService")
public class StrawBerryCakeService extends AbstractCakeService{

	@Override
	protected void addOtherMaterial(Cake cake) {
		cake.setCakeName("草莓蛋糕");
		cake.setSupplement("新鲜草莓");
		System.out.println("当前正在制作好吃的草莓蛋糕");
	}

}

The three kinds of cake making are called xxxcakeservice, so we can splice the cake type string cakeservice to form the name of the bean, and then use spring's

The IOC container obtains abstractcakeservice directly through the name of bean, and calls its docake method to make different cakes

The spring container implements the factory pattern.

The control layer code called is as follows:

@RestController
public class cakeController {
	
	@Autowired
	private ApplicationContext applicationContext;

	@GetMapping("doCake")
	public Cake doCake(@RequestParam("supplement") String supplement) {
		AbstractCakeService cake = (AbstractCakeService) applicationContext.getBean(supplement + "CakeService");
		return cake.doCake();
	}
}

Introduce the required additives to make the cake:

In this way, we use the factory mode + template method mode, which not only eliminates duplicate code, but also avoids the risk of modifying existing code. This is the opening and closing in the design pattern

Principle: closed for modification and open for extension.

Git code implementation path:

https://github.com/jichunyang19931023/dailyDemo/tree/master/cake

II Eliminate duplicate code with annotation + reflection

If we want to interface with the external system, we need an interface to output the definition and parameters of the interface. At this time, we can use the annotation + reflection mechanism to solve this problem.

If there is an interface for creating bills that needs to generate API information, in order to split the interface logic from the logic implementation, you first need to use POJO classes (only attributes but no attributes)

Define all interface parameters in the way of any business logic (data class), as shown below:

@Data
public class CreateTicketAPI {
	// 用户名
	String userName;
	// 密码
	String password;
	// 当前时间的时间戳
	Long currentTime;
}

We can add some metadata to the interface and all parameters through custom annotations. As shown below, we define an interface annotation API, including interface description, URL address and request method:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Inherited
public @interface API {
    String desc() default "";
    String url() default "";
    String type() default "GET";
}

Then, we define a user-defined annotation @ apifield, which is used to describe each field specification of the interface, including three attributes: whether the parameter is null, type and Description:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@Documented
@Inherited
public @interface APIField {
    // 可否为空
    boolean isrequired() default false;
    // 参数类型
    String type() default "";
    // 参数说明,备注
    String remark() default "";
}

Modify the original POJO class and supplement the metadata of the interface and each parameter field (i.e. the corresponding attribute related information). The inherited abstractapi class is an empty entity

Now, the interface in this case has no public data that can be abstracted into the base class:

@API(url = "/creatTicket",desc = "创建票据接口",type = "GET")
@Data
public class CreateTicketAPI extends AbstractAPI{
	
	@APIField(isrequired = true,type = "String",remark = "用户名称")
	String userName;
	
	@APIField(isrequired = true,remark = "密码")
	String password;
	
	@APIField(isrequired = false,type = "Long",remark = "当前时间的时间戳")
	Long currentTime;
}

Above, we have realized the description of API parameters through annotations. Next, let's look at how reflection works with annotations to implement interface properties and parameter descriptions:

@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;
	}
}

Finally, the control layer calls:

@RestController
public class APIController {
	
	@Autowired
	private 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);
    }
}

Many general processing involving class structure can reduce repeated code according to this pattern. Reflection gives us fixed logic when we don't know the class structure

Members of the processing class; Annotations give us the ability to supplement metadata for these members, so that when we use reflection to implement general logic, we can get more information from the outside

The data we care about.

Git code implementation path:

https://github.com/jichunyang19931023/dailyDemo/tree/master/api

III Eliminate duplicate code with attribute copy tools

For a three-tier system, considering the decoupling and isolation between layers and the different data requirements of each layer, each layer usually has its own POJO as the data entity.

For example, the entities in the data access layer are generally called DataObject or do, the entities in the business logic layer are generally called domain, and the entities in the presentation layer are generally called data

Transfer object or dto. If we write the assignment code between these entities manually, it is also easy to make mistakes, so we can use the attribute copy tool to eliminate duplicate generations

Code.

Mapping tools like BeanUtils are used to convert beans. The copyproperties method also allows us to provide properties that need to be ignored:


ComplicatedOrderDTO orderDTO = new ComplicatedOrderDTO();
ComplicatedOrderDO orderDO = new ComplicatedOrderDO();
BeanUtils.copyProperties(orderDTO,orderDO,"id");
return orderDO;

Or you can use a great toolkit hutool. The following is the official document website, which supports different types of copies, which is very convenient:

https://www.hutool.cn/docs/#/

The content of this article comes from the network collection of netizens. It is used as a learning reference. The copyright belongs to the original author.
THE END
分享
二维码
< <上一篇
下一篇>>