开始

又到了一年该总结的时候了,去年写总结的时候也不会想到今年的变化会这么大吧。

年前工作又忙碌了起来到现在才有空来继续写。

生活

生活上的变化是如此的巨大,没想到短短一年就步入了婚姻的殿堂。这也导致今年外出旅游的时间比宅在家里变多了许多。

唯一的不足是由于通勤时间的变化导致下半年的骑行完全搁置,体重也没有能再下一步。明年必须恢复骑行锻炼。

记性的变差导致年底似乎回忆不起来很多东西,所以决定从25年开始进行每周回顾。

购物

  • A7C2 + 2070G :这应该算是今年最大的一笔购物开支了,在价格回归理性之后在线下入了这个套装。虽然从年末来看又吃灰了好久但是外出的时候有点所谓的仪式感似乎也是不错的。
  • 红米投影仪:这应该算是今年最有性价比的电子产品了,500多的价格有不错的显示效果,睡前看看剧非常不错。
  • 京造冲锋衣300:性价比不错,冬天穿也很合适
  • AirPods pro:效果不错,但是盒子跟耳机非常粘脏东西而且不好去除。
  • 比乐蒂摩卡壶:因为今年不在家所以使用频率不高,从性价比来说属于一般
  • 幻刺pro磨豆机:没有买过其他的所以也没有对比,使用上来说并不费力,手柄坏了一个售后也给发了一个新的。

还包括智能马桶、电热水器啥的就不写出来了,因为还没回家住没有体验到。。。

tips:

  • 1688比拼多多可能更便宜
  • 对于成熟的工业品需要对附加价值去魅,买一般品质的高性价比。

阅读

今年似乎没有很多的阅读,只看了寥寥几本书都没什么印象。唯一看的长篇可能就是《邓小平时代》了。《财新周刊》属于每周读物,但是感觉真材实料变少了。

看剧

  • 真探:逼格很高,完美落地
  • 权游:没有想象中的那么好看,感觉爆点全靠死人
  • 兄弟连、太平洋战争:兄弟连很好看,太平洋战争太压抑了,特别是在雨林里的那段。
  • 成瘾剂量:推荐。
  • 死神第三季:爆点不多,等最终季。

游戏

  • 血源诅咒:今年终于玩上了最佳游戏。
  • 圣兽之王:画风很赞但是游戏深度有点欠缺。
  • 异度之刃2:好玩,爱玩。就是系统太反人类了,还要各种肝,一周目结束之后就放下了。

今年投入时间最多的可能就是炉石和漫威卡牌了。炉石还是那个炉石,但是今年从漫威里还是学到了不少:

  • 卡组构筑上可以分为破坏别人的展开和增强自己的展开
  • 卡牌游戏(或者说所有游戏)心态很重要,而慢节奏的卡牌更看重每一手的抉择
  • 在抉择里其实信息差起了很大的作用,所以并不是越主流的卡主越好上分,这也是我觉得漫威设计比炉石好的地方,劣势卡组也能靠信息差拿到更高的魔方

技术

技术方面似乎没有什么长进,在网安和逆向方面的推进进度并不如人意,期间还看了一些rust但是没有in action。

25年还是要把个人网站好好建设一下,完成几个个人项目。

10个好习惯

  1. 保持提交足够小
  2. 对于每一个想要的改变,先让改变变得简单(警告:这可能很难),然后再进行简单的改变。 PS:童子军原则
  3. 所有代码都是一种负担。未部署的代码是负担的死神。
  4. 知道何时在测试框架的能力。
  5. 如果某个特定功能没有合适的地方,就为它创建一个新的模块(或类或组件),你会在后面找到合适的位置。PS:不要过早设计
  6. 如果你不知道 API 应该是什么样子,可以先写测试,因为这会迫使你考虑“客户”,在这种情况下就是你自己。
  7. 复制粘贴可以一次。第二次你就引入了重复(即三份副本),不要这样做。你应该有足够的数据点来创建一个足够好的抽象。
  8. 设计会变得陈旧。你可以通过重构来减缓它们变陈旧的速度,但最终你需要改变事物的运作方式。PS:系统的熵增
  9. 技术债务可以分为三种主要类型:1)阻止您现在做事情的因素,2)将来会阻止您做事情的因素,以及 3)可能会阻止您将来做事情的因素。其他所有分类都是这三种的子集。尽量减少#1 中的内容,并尽量专注于#2。忽略#3。
  10. 可测试性与良好的设计相关。某些东西不易测试暗示设计需要更改。有时那个设计是你的测试设计。 PS:可测试性的重要性(如何写出可测试的代码??)

Things I Learnt The Hard Way

系统设计

  • 先制定规范,然后编写代码
  • 将步骤写为注释
  • Gherkin 是您理解期望的朋友
  • 单元测试很好,集成测试更好
  • 测试使 API 更优秀
  • 在命令行上运行您知道如何执行的测试(可以在持续集成工具中使用)
  • 准备好丢掉你的代码
  • 好的语言配备了集成测试 (如果一种语言在其标准库中提供了一个测试框架——即使是最小的——那么围绕它的生态系统将拥有比没有测试框架的语言更好的测试)
  • 未来思维就是未来垃圾
  • 文档是给未来自己的情书
  • 功能文档是其合同
  • 如果函数描述中包含”and”,那么它就是错误的
  • 不要将布尔值用作参数
  • 注意接口变化
  • 好的编程语言配有集成文档
  • 一种语言远不止是一种语言
  • 有时候,让应用程序崩溃总比无所作为要好
  • 如果你知道如何处理这个问题,就处理它
  • 类型说明您的数据是什么
  • 如果您的数据有模式,请使用结构来保持它
  • 理解并远离货物崇拜(”如果大公司在背后支持这一点,那就很好。”)
  • “合适的工具”只是推动议程
  • “正确的工具”比你想象的更明显
  • 不要干扰项目外的事务(改框架代码)
  • 数据流击打模式(当你理解数据在代码中如何流动时,你最终会得到比应用一堆设计模式更好的代码。)
  • 设计模式用于描述解决方案,而不是寻找解决方案(解决你的问题;找到一个好的解决方案;然后你可以检查模式以了解你如何命名该解决方案。)
  • 学习基础的函数式编程
  • 认知成本是可读性的杀手 PS:降低系统中总的认知成本
  • 神奇的七,加或减二(考虑函数组合, 而不是函数调用)
  • 快捷方式很好,但仅在短期内有效
  • 抵制“简单”的诱惑(对幕后的事情保持好奇。)
  • 始终在日期中使用时区
  • 始终使用 UTF-8
  • 开始愚蠢(脱离大型IDE工作)
  • 日志用于事件,而非用户界面
  • 调试器被高估了(日志记录可以在任何地方运行。你可能在崩溃时没有你想要的信息(例如,不同的日志级别),但你可以启用日志记录以便稍后找出一些东西。)
  • 始终使用版本控制系统
  • 每个更改一个提交
  • “git add -p” 是你在过度更改时的好帮手
  • 按数据/类型而非功能组织项目
  • 创建库
  • 学习监控
  • 配置文件是朋友(使用配置文件并用不同的配置文件运行应用程序)
  • 命令行选项很奇怪,但很有帮助
  • 不仅是函数组合,还有应用组合
  • 即使是应用程序组合,也要从简单开始
  • 优化是为了编译器(您需要做的是为您的代码思考一个更好的设计,而不是如何改进当前的代码。代码是供人类阅读的。始终如此。优化是编译器的工作。)
  • 惰性求值

团队协作

  • 代码审查不是为了风格
  • 代码格式化工具可以,但它们并不是灵丹妙药
  • 代码风格:遵循它
  • 显式优于隐式(sleep() -> sleepForSecs()
  • 公司寻找专家,但更长时间保留通才
  • 考虑用户
  • 处理用户数据的最佳安全方式是不捕获它
  • 记录“花费我超过 1 小时解决的愚蠢错误”
  • 如果它在你的电脑上无法运行,你就有问题

个人

  • 该停的时候就该停
  • 行为准则保护你,而不是他们
  • 学会说不
  • 您对您代码的使用负责
  • 不要在事情未完成时说“完成了”
  • 你将以艰难的方式了解自己
  • 人们对代码/架构感到生气/烦恼,因为他们在乎
  • 从你的烦恼中学习
  • 注意人们对你的反应
  • 学会识别有毒的人;远离他们
  • 注意微侵犯行为
  • 不,我认为它们是“不可修复的”
  • 有毒/微侵略者只有在你是他们时才能被修复
  • 英雄项目:你总有一天会做到的
  • 不要将“英雄项目”和“英雄综合症”混淆
  • 学会何时放弃
  • 信息技术世界是一个非常小的蛋
  • 纸质笔记实际上是有帮助的
  • Trello 很酷,但便利贴更好
  • 关于你愚蠢解决方案的博客仍然比保持沉默要好
  • 但关闭评论
  • 把你的愚蠢解决方案发布到网上
  • 保持“我不知道的事情”列表

REF

Spring Boot自定义参数处理实体ID

在实际业务中经常需要判断接收的实体ID是否在表中存在和向线程中注入该实体ID对应的数据,下面摘录了两种思路:

实现Converter来获取主键ID

1
2
3
4
5
6
7
8
9
10
@RestController
class GitRepositoryController {

@GetMapping("/repositories/{repoId}")
String getSomething(@PathVariable("repoId") GitRepositoryId repositoryId) {
// ... load and return repository
}

}

1
2
3
4
5
6
7
8
9
@Value
class GitRepositoryId {
private final long value;

public static GitRepositoryId valueOf(String value){
return new GitRepositoryId(Long.parseLong(value));
}
}

1
2
3
4
5
6
7
8
9
10
@Component
class GitRepositoryIdConverter implements Converter<String, GitRepositoryId> {

@Override
public GitRepositoryId convert(String source) {
// TODO 这里可以增加 exist 检测 和 在线程上下文中注入实体信息
return new GitRepositoryId(Long.parseLong(source));
}
}

实现 HandlerMethodArgumentResolver

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
@RequiredArgsConstructor
class GitRepositoryArgumentResolver implements HandlerMethodArgumentResolver {

private final GitRepositoryFinder repositoryFinder;

@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.getParameter().getType() == GitRepository.class;
}

@Override
public Object resolveArgument(
MethodParameter parameter,
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest,
WebDataBinderFactory binderFactory) {

String requestPath = ((ServletWebRequest) webRequest)
.getRequest()
.getPathInfo();

String slug = requestPath
.substring(0, requestPath.indexOf("/", 1))
.replaceAll("^/", "");

return gitRepositoryFinder.findBySlug(slug)
.orElseThrow(NotFoundException::new);
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Component
@RequiredArgsConstructor
class GitRepositoryArgumentResolverConfiguration implements WebMvcConfigurer {

private final GitRepositoryFinder repositoryFinder;

@Override
public void addArgumentResolvers(
List<HandlerMethodArgumentResolver> resolvers) {
resolvers.add(new GitRepositoryArgumentResolver(repositoryFinder));
}

}

REF

0%