首先,实名反对各答非所问和调侃的回答。
题主的提问非常详细,认真。实名赞扬题主提问。
事实上,这是一个非常不错的题目。该题目涉及软件架构设计与开发的多个方面,具有很强的通用性。研究好这个问题对于开发能力的提升很大。
今天有时间,我来解答一下这个问题。并且,最后还会附上实现代码。
最终实现的效果如下所示:
实现的效果如下,这是实际截取的图:
我用尽可能用深入浅出的解答和实实在在的代码,支持每一个真诚的提问者。
整个回答不仅包含实现,还包括架构设计过程,会比较长。建议大家读完。
如果有不清楚的地方,大家可以在评论区提问。
整个解答包括问题定义、模型设计、方案设计、最终实现等多个环节。展现了系统架构设计的全部流程。
目录如下:
1 功能定义
2 模型设计
2.1 上层切面
2.2 下层切面
2.3 混合切面
3 对象属性对比功能实现
4 对象属性处理
4.1 普通属性
4.2 特殊属性
4.3 业务属性
5 易用性注解
6 存储设计
7 方案总结
8 系统实现
1 功能定义
在开发一个系统之前,我们先要对系统进行明确的定义。
在一个软件系统中,通常存在增删改查四类操作。对于日志系统而已,这四类操作的处理难度不同。
查询操作往往不需要记录日志,增加和删除操作涉及一个对象状态,编辑操作涉及对象编辑前和编辑后的两个状态。
编辑操作是整个日志模块中最难处理的。只要掌握了编辑操作,则新增操作、删除操作、查询操作都很简单了。因为,新增操作可以理解为null到新对象的编辑,删除操作可以理解为旧对象到null的编辑,查询操作可以理解为旧对象到旧对象的编辑。
因此,本文主要以编辑操作为例进行介绍。
为了便于描述,我们假设一个学校卫生大扫除系统。
这个系统中包含很多方法,例如分配大扫除工作的assignTask方法,开始某个具体工作的startTask方法,验收某个具体工作的checkTask方法,增加新的人员的addUser方法等。每个方法都有不同的参数,涉及不同的对象。
以startTask方法为例,开始一个任务需要在任务中记录开始时间、责任人、使用的工具,整个方法如下:
public String startTask(String taskId, Integer userId, Date startTime, Tool tool) {
// 业务代码}
最简单的记录日志的方法便是在代码中直接根据业务逻辑写入日志操作语句,例如:
public String startTask(String taskId, Integer userId, Date startTime, Tool tool) {
// 业务代码 log.add("操作类型:开始任务。任务编号:" + taskId + ";责任人:" + userId ……);
// 业务代码}
如果你真的打算使用上述的方法记录日志,那已经没有什么可以教你的了。
你要做的就是提升自己Ctrl + C和Ctrl +V的速度,努力成为一个真正的CV大神直到顶级CRUD工程师。
而如果你想要设计一个较为专业、通用、易用的日志模块,那请继续向下阅读。我们必须从模型设计开始慢慢展开。
2 模型设计
设计系统的第一部是抽象,抽象出一个简单的便于处理的模型。
我们可以把用户操作抽象为下面的模型,即用户通过业务逻辑修改了持久层中的数据。
要想记录日志,那我们需要在整个流程中设置一道切面,用以获取和记录操作的影响。
而这一道切面的位置十分关键,我们下面探讨这一点。本章节的探讨与讨论一个问题:单一切面能否实现用户操作日志的记录。如果使用单一的切面能实现日志记录功能,那就太好了。这意味着我们只要在系统中定义一个日志切面,则所有的用户操作都会被记录。
而如果单一的切面无法做到,那我们的日志操作就需要侵入业务逻辑。在展开讨论之前要注意,这里只是模型设计,请忽略一些细节。例如,参数是英文变量名,不便于表意;某些参数是id,与系统强耦合等。这些都不是模型层需要考虑的,我们会在后续的设计中解决这些问题。
2.1 上层切面
首先,我们考虑在整个业务逻辑的最上层设置切面如下图所示:
这一层其实就是业务逻辑入口处,以下面的方法为例:
public String startTask(String taskId, Integer userId, Date startTime, Tool tool) {
// 业务代码}
我们可以得到的日志信息有:
startTask:方法的名称
- taskId:方法的参数名,及其对应的参数值,例如15
- userId:方法的参数名,及其对应的参数值,例如3
- startTime:方法的参数名,及其对应的参数值,例如 2019-12-21 15:15
- t