什么是生活里的抽象?
在生活交谈过程中,我们每每会把别人说的话,好像能理解,但是不具象。也就是我们常说的:听君一席话,胜似一席话。就是说了相当于没说,我们称之为抽象。
什么是代码里的抽象?
都说艺术来源生活,代码的抽象思维源泉依然来自生活,但是代码里的抽象比生活的抽象还是相对更具象一点,至少会把要做的事情的大概轮廓描述出来,然后具体的要做的事情交给子类去实现,也就是代码的抽象更像老板,负责画大饼,描绘伟大蓝图,让社畜们去冲锋陷阵。
了解了抽象的大致定义,我们来看看代码中具体的抽象实现,看看初级 -> 中级 -> 高级如果具体画大饼。
我们以日志打印为例,看看三种实现方式的优劣势,为什么会有初中高级之分。
插播一则通告:本人在代码一线工作近八年时间,有非常丰富的面试经验,有需要优化简历,模拟面试的同学可以联系即时沟通号:xiaolang1530368931。将简历优化成大厂面试官想看的,提前回答大厂面试官可能会问的问题,为进大厂做最后的冲刺。
使用AOP切面(初级)
- 具体实现
- 我们定义切面如下,我们的切点定义在org.github.controller所有的public方法
package org.github.aop;
import com.sun.deploy.util.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.github.json.JsonUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.Map;
/**
* @Author coffe
* @Description 日志切面
* @Date 2024/9/8 10:50
*/
@Aspect
@Component
public class LogAspect {
private static final Logger LOGGER = LoggerFactory.getLogger(LogAspect.class);
@Pointcut("execution(public * org.github.controller..*.*(..))")
public void pointCut() {
}
@Around("pointCut()")
public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
String methodName = proceedingJoinPoint.getSignature().getName();
LOGGER.info("methodName={},reqParam={}", methodName, buildReqParam(proceedingJoinPoint));
Object proceed = proceedingJoinPoint.proceed();
LOGGER.info("methodName={},respParam={}", methodName, JsonUtils.silentObjToJsonString(proceed));
return proceed;
}
/**
* 构建请求参数
*
* @param proceedingJoinPoint
* @return
*/
private String buildReqParam(ProceedingJoinPoint proceedingJoinPoint) {
Object[] args = proceedingJoinPoint.getArgs();
if (args == null || args.length == 0) {
return null;
}
StringBuilder reqParam = new StringBuilder();
Arrays.stream(args).forEach(param -> {
String simpleName = param.getClass().getSimpleName();
reqParam.append(simpleName).append(":").append(JsonUtils.silentObjToJsonString(param)).append(",");
});
StringBuilder finalStringBuilder = reqParam;
int lastIndexOf = finalStringBuilder.lastIndexOf(",");
if (lastIndexOf != -1) {
finalStringBuilder = finalStringBuilder.deleteCharAt(lastIndexOf);
}
return finalStringBuilder.toString();
}
}
- 定义测试controller如下
package org.github.controller;
import javafx.util.Pair;
import org.github.service.GreetingService;
import org.github.template.BizTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
/**
* @Author coffe
* @Description
* @Date 2024/9/8 11:48
*/
@RestController
public class LogController {
@Autowired
private GreetingService greetingService;
@RequestMapping(path = "/logRecord")
public Object logRecord(@RequestParam(value = "name") String name,@RequestParam(value = "age") Integer age) {
Pair<String, Integer> pair = new Pair<String, Integer>(name, age);
return pair;
}
}
- 运行后结果
2024-09-08 17:43:01.438 INFO 13160 --- [nio-8081-exec-1] org.github.aop.LogAspect : methodName=logRecord,reqParam=String:"xy",Integer:28
2024-09-08 17:43:01.532 INFO 13160 --- [nio-8081-exec-1] org.github.aop.LogAspect : methodName=logRecord,respParam={"key":"xy","value":28}
相信各位小伙伴一看代码就知道上面实现的功能就是定义一个环绕切面进行出入参报文的打印,我们来看下这种实现方式的优劣势。
- 优势
- 无需关注切面具体实现,日志自动打出。
- 劣势
- 定制化不够,假如我有些接口有日志落库需求,或者打印格式要求,那切面无法满足。
使用模板模式(中级)
- 具体实现
- 我们定义模板如下
package org.github.template;
import org.github.json.JsonUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @Author coffe
* @Description
* @Date 2024/9/8 12:38
*/
public abstract class AbstractLogBase<Req, Resp> {
private static final Logger LOGGER = LoggerFactory.getLogger(AbstractLogBase.class);
public Resp execute(Req reqParam, String bizName) {
LOGGER.info("bizName={},reqParam={}", bizName, JsonUtils.silentObjToJsonString(reqParam));
Resp resp = doExecute(reqParam);
LOGGER.info("bizName={},respParam={}", bizName, JsonUtils.silentObjToJsonString(resp));
return resp;
}
protected abstract Resp doExecute(Req reqParam);
}
- 定义子类与测试类如下
package org.github.service;
import org.github.template.AbstractLogBase;
import org.springframework.stereotype.Service;
/**
* @Author coffe
* @Description
* @Date 2024/9/8 16:35
*/
@Service
public class GreetingService extends AbstractLogBase<String, String> {
@Override
public String doExecute(String reqParam) {
return "Hello," + reqParam;
}
}
package org.github.controller;
import javafx.util.Pair;
import org.github.service.GreetingService;
import org.github.template.BizTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
/**
* @Author coffe
* @Description
* @Date 2024/9/8 11:48
*/
@RestController
public class LogController {
@Autowired
private GreetingService greetingService;
@RequestMapping(path = "/greeting")
public String greeting(String name) {
return greetingService.execute(name, "say hello");
}
}
- 运行后结果
2024-09-08 17:49:45.504 INFO 13160 --- [nio-8081-exec-4] org.github.template.AbstractLogBase : bizName=say hello,reqParam="xy"
2024-09-08 17:49:45.504 INFO 13160 --- [nio-8081-exec-4] org.github.template.AbstractLogBase : bizName=say hello,respParam="Hello,xy"
- 优势
- 定制化更高,例如我们的bizName可以在具体运用时传入,不再像切面一样固定取方法名。
- 拓展性更好,我定义模板后不仅仅可以打印日志,我还可以根据业务需要拓展其他额外的功能。
- 劣势
- 复杂度更高,需要定义一个类去继承基类,这对于较为简单的接口是极为不友好的。
以上两种方式都是对代码进行了不同程度的抽象,让代码可复用性更高,但是同样引入了额外的成本,那有没有一种写法可以极低成本的既兼容了定制化与拓展性,复杂性也更低。那就要请出咱们今天的主角,使用内部接口进行高级抽象。
使用内部接口(高级)
- 具体实现
- 我们定义模板类如下
package org.github.template;
import org.github.json.JsonUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @Author coffe
* @Description
* @Date 2024/9/8 16:42
*/
public class BizTemplate {
private static final Logger LOGGER = LoggerFactory.getLogger(BizTemplate.class);
public interface BizAction<Req, Resp> {
String bizName();
/**
* 日志记录
*
* @param req
*/
default void logRecord(Req req) {
String bizName = this.bizName();
LOGGER.info("bizName={},reqParam={}", bizName, JsonUtils.silentObjToJsonString(req));
}
/**
* 执行业务
*
* @param req
* @return
*/
Resp doBiz(Req req);
}
public static <Req, Resp> Resp execute(Req req, BizAction<Req, Resp> bizAction) {
String bizName = bizAction.bizName();
bizAction.logRecord(req);
Resp resp = bizAction.doBiz(req);
LOGGER.info("bizName={},resp={}", bizName, JsonUtils.silentObjToJsonString(resp));
return resp;
}
}
- 定义测试类如下
package org.github.controller;
import javafx.util.Pair;
import org.github.service.GreetingService;
import org.github.template.BizTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
/**
* @Author coffe
* @Description
* @Date 2024/9/8 11:48
*/
@RestController
public class LogController {
@RequestMapping(path = "/templateLogRecord")
public Object templateLogRecord(String name) {
return BizTemplate.execute(name, new BizTemplate.BizAction<String, String>() {
@Override
public String bizName() {
return "templateLogRecord";
}
@Override
public String doBiz(String name) {
return "hello," + name;
}
});
}
}
- 运行后效果
2024-09-08 17:57:55.609 INFO 13160 --- [nio-8081-exec-7] org.github.template.BizTemplate : bizName=templateLogRecord,reqParam="xy"
2024-09-08 17:57:55.609 INFO 13160 --- [nio-8081-exec-7] org.github.template.BizTemplate : bizName=templateLogRecord,resp="hello,xy"
- 优势
- 用法极其简单
- 可读性非常高,分步骤操作
- 定制化、拓展性都非常好,并且可以使用default关键字提供默认实现,进一步简化用法。
- 劣势
几乎没有,我现在强的可怕用在这里真的太合适不过了。
以上就是关于顶级抽象思维的分享,希望大家能够将这种思维运用到工作当中,用起来是真的香,如果大家觉得有一点点受益的话,希望点赞、评论、转发来支持我做更加优质的分享。
分享一句非常喜欢的话:把根牢牢扎深,再等春风一来,便会春暖花开。
PS:以上引用信息以及图片均来自网络公开信息,如有侵权,请留言或联系
504401503@qq.com,立马删除。