原因
==
大多数的研发人员,做的工作都是业务功能开发,也就是常说的 CRUD。只是不同的业务场景,CRUD的复杂度不同而已。
可是对于业务代码来说,很多情况下不太好套用设计模式,或者说没法很好的应用设计模式。
平时看到的最多的是策略模式的文章吧,为什么呢?
我猜是因为这个最好写,应用在业务代码里比较简单;随便一个稍微复杂点的场景,就可以套用一下策略模式,把多个 if 拆分到多个类里。
的确,业务代码里适当的使用策略模式可以降低复杂度;但就算用也得住一个度,不要把各种业务里的 if 都换成策略模式了,不然代码会炸的……
之前见过一个项目,虽然是内部xx系统,但那个研发小哥可能是走火入魔了。抽了 80 多个策略类出来,这八十多个类里又分为十几个组,每组七八个策略类,但每个类里的代码,也不过十几二十行,而且还有重复代码。
我当时问了他一句:你在养蛊吗?
像这个研发小哥就是一个反例,滥用设计模式,过分的将各种分支代码全部套进设计模式了。不过我猜想他可能是为了学习吧,学以致用……
其他的委托代理状态之类的模式,想应用在业务代码里,就比较费劲了,因为没有那么多合适的场景。但策略模式则不同,有 if 的地方都可以尝试套一下……
可是设计模式是用来解决问题,降低/转移复杂度的,而不是增加复杂度。
非业务代码里的设计模式
===========
跳出业务代码来,甚至说跳出纯业务代码来之后,想应用设计模式就比较简单了,甚至不需要你硬套,遇到问题时就自然的会想到用设计模式来解决。
举个栗子
====
系统里一般需要一个 traceId/requestId 来将整个链路串起来,配合日志打印或者集中式的APM抽取。
就拿单体应用来说,一般用日志框架的 MDC 来绑定这个 traceId。在 Filter 或者 一些 AOP 里,给 MDC 一个 traceID,那么整个调用链路都可以用这一个 ID,打印日志时就可以根据 traceId 区分不同请求了,就像这样:
2021-06-10 18:31:44.227 [ThreadName] [000] INFO loggerName - 请求第0步
2021-06-10 18:31:44.227 [ThreadName] [000] INFO loggerName - 请求第1步
2021-06-10 18:31:44.227 [ThreadName] [000] INFO loggerName - 请求第2步
2021-06-10 18:31:44.227 [ThreadName] [111] INFO loggerName - 请求第0步
2021-06-10 18:31:44.227 [ThreadName] [111] INFO loggerName - 请求第1步
2021-06-10 18:31:44.227 [ThreadName] [111] INFO loggerName - 请求第2步
…
通过 000/111 这个 traceId 就可以区分是哪个请求。
可 MDC 是通过 ThreadLocal 进行存储数据的,ThreadLocal 毕竟是和线程绑定的。如果链路中使用了线程池处理,那可怎么办?线程池里子线程打印日志的时候,MDC 可获取不到主线程的 traceId,但对于这个请求来说,主子线程都是一个链路……
还记得这句话吗?
计算机科学领域的任何问题都可以通过增加一个间接的中间层来解决”
这里借助委托模式,来增加一个中间层,问题就很好解决了。
既然是主子线程的数据传递问题,那么只需要在创建子线程的时候,从主线程里将 MDC 里的 traceId 拿出来,传递给新建的子线程就可以了,就像这样:
public class MDCDelegateRunnable implements Runnable{
private Runnable target;
private String traceId;
public MDCDelegateRunnable(Runnable target, String traceId) {
this.target = target;
this.traceId = traceId;
}
@Override
public void run() {
MDC.put(“traceId”,traceId);
target.run();
MDC.remove(“traceId”);
}
}
然后再来一个委托模式的线程池,将 execute方法重写。把线程池中原本的 Runnable 对象包装为刚才的 MDCDelegateRunnable,在创建时,将 traceId 通过构造参数传递
public class MDCDelegateExecutorService extends AbstractExecutorService {
public MDCDelegateExecutorService(AbstractExecutorService target) {
this.target = target;
}
private AbstractExecutorService target;
@Override
public void shutdown() {
target.shutdown();
}
//…
@Override
public void execute(@NotNull Runnable command) {
target.execute(new MDCDelegateRunnable(command, MDC.get(“traceId”)));
}
}
搞定,来测试一下:
public static void main(String[] args) throws IOException, ExecutionException, InterruptedException {
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V获取:vip1024b (备注Java)
最近我根据上述的技术体系图搜集了几十套腾讯、头条、阿里、美团等公司21年的面试题,把技术点整理成了视频(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,这里以图片的形式给大家展示一部分
a)**
[外链图片转存中…(img-AuNcX678-1711815454750)]
[外链图片转存中…(img-huBKHu8h-1711815454750)]
最近我根据上述的技术体系图搜集了几十套腾讯、头条、阿里、美团等公司21年的面试题,把技术点整理成了视频(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,这里以图片的形式给大家展示一部分
[外链图片转存中…(img-BrtiR577-1711815454751)]