一、项目的总结
短短的一个月很快就过去了,在这一个月里我学到了很多有关Java等方面的知识,也了解了关于软件开发的流程。了解了自己的不足,知道了自己的努力方向。
(1)前期准备
在正式开始写代码之前我们还需要做一些准备工作,比如:需求拆解、环境准备和技术准备。老师将我们分为三个小组,每个小组负责一个模块,我们小组负责注册/登录、用户管理、管理员和个人中心。每个小组根据需求文档去讨论和编写自己模块的设计,包括模块功能、数据库设计、接口文档和业务流程图。
在设计阶段,我和组员讨论最多的就是数据库的设计和接口文档。数据库的设计我们最初是将学员和导师分开为两个表,但后来我在查看需求文档时发现用户是在注册后才能选择身份,因此我们不能直接将注册的信息添加到对应的表中。在讨论中我们提出两种解决方案。第一种是在原有的方案上对用户注册的信息暂时存储在服务器中,等用户选择身份后再统一添加到对应的表中。第二种是将学生和导师放在同一张表中,再根据身份表对他们进行身份验证。最后根据和组员的讨论,我们最终选择了第二种方案。第一种方案有着明显的缺点,如果用户在注册后直接退出,那么服务器中暂时存储的数据也会消失,下次用户要用的话还要重新注册。还有就是用户在注册后可以直接跳过选择身份,以游客的身份进行访问。学生和导师的信息差距不大,不需要额外进行区分。
在写接口文档时我们小组也是有两种方案,一种是根据数据库的表来写接口文档,每个表都写增删改查的接口。第二种是根据功能来写接口文档,每个功能需要用到哪些数据我们就写对应的接口。我们小组在这个问题上讨论一两个小时,最后由组长决定使用第一种方案。在讨论中我认为第一种方案比较好,我们对数据的操作无非就是增删查改,我们一开始就把这些接口写好了,以后需要用到时直接调用就行,不需要去更改service层的代码,为以后的扩展提供了便利。使用第二种方案虽然能够直观的了解到当前需求文档的功能和所需的接口,但这会导致有些接口重复出现,有些接口没有用到的就没有。除了写小组负责的接口外,我们还和其他小组沟通,了解他们需要我们提供什么接口和我们需要提供接口。
(2)项目开发
项目开发阶段和平常的一样,先写配置文件和启动器,主要是数据库的连接、连接池、redis和mybatis的配置。然后写DAO层的实体类,使用@Date注解来节省代码的get()、set()、toString()等方法,使用@TableId注解来对应数据库中的主键属性,使用@NotEmpty来保证字段不能为空,使用@Pattern注解来进行参数验证。接着写Service层的接口和实现类,在实现类上使用@Service注解进行标记,在类成员变量、方法及构造函数使用@Autowired进行标记并完成自动装配,使用@Override注解来指定方法的重写。最后写Controller层,使用@RestController注解来返回json,是@ResponseBody和@Controller的组合注解,通过@RequestMapping注解可以定义不同的处理器映射规则,通过@RequestBody可以将请求体中的JSON字符串绑定到相应的bean上,用@validated来校验数据,@RequestParam将请求参数绑定到控制器的方法参数上。
(3)接口测试
项目的开发结束后就对项目中的接口进行测试,确保每个接口都可以被使用。
(4)项目后续
项目的开发和测试完成后就要进行代码的优化,查看代码是否符合规范。然后我们小组就要做mysql的主从复制和多数据源,redis缓存的实际操作。主从复制是在A服务器上进行数据的更新,通过 binlog 日志记录同步到B服务器上,并重新执行同步过来的 binlog 数据,从而达到两台服务器数据一致。主从复制可以实时灾备,用于故障切换,读写分离,提供查询服务,实现负载均衡,数据热备,避免影响业务。redis是做MySQL的缓存,每当查询数据,会先从redis里查询,发现没有这条数据再去Mysql里查询,并把结果写入redis同时设置生存时间。(保持的key是sql语句,value是结果。几乎是没有逻辑的)。当对Mysql进行修改和新增操作后,redis并不会修改。只有当key过期下次查询时才会更新数据。
二、读后感
在做项目的过程中我除了写代码外还读了一本书《我的第一本算法书》。算法就是计算或者解决问题的步骤,我们需看算法的运行时间来选择合适的算法。
(1)数据结构
数据存储于内存时,决定了数据顺序和位置关系的便是“数据结构”。数据结构分为链表、数组、栈、队列、哈希表、堆和二叉查找树。
链表的数据呈现性排列,在链表中,数据的添加和删除都较为方便,就是访问比较耗费时间。
数组也是数据呈线性排列的一种数据结构,在数组中,访问数据十分简单,而添加和删除数据比较耗工夫。
栈也是一种数据呈线性排列的数据结构,不过在这种结构中,我们只能访问最新添加的数据,因为栈只能在一端操作。
队列中的数据也呈线性排列。但队列中添加和删除数据的操作分别是在两端进行的。
哈希表在存储数据的过程中,如果发生冲突,可以利用链表在已有数据的后面插入新数据来解决冲突。
堆是一种图的树形结构,被用于实现“优先队列”。优先队列是一种数据结构,可以自由添加数据,但取出数据时要从最小值开始按顺序取出。在堆的树形结构中,各个顶点被称为“结点”(node),数据就存储在这些结点中。
二叉查找树采用了图的树形结构,数据存储于二叉查找树的各个结点中。
(2)排序
排序就是将输入的数字按照从小到大的顺序进行排列。
冒泡排序就是重复“从序列右边开始比较相邻两个数字的大小,再根据结果交换两个数字的位置”这一操作的算法。
选择排序就是重复“从待排序的数据中寻找最小值,将其与序列最左边的数字进行交换”这一操作的算法。
插入排序是一种从序列左端开始依次对数据进行排序的算法。需要将取出的数据与其左边的数字进行比较。如果左边的数字更小,就不需要继续比较,如果取出的数字比左边已归位的数字都要小,就必须不停地比较大小,交换数字,直到它到达整个序列的最左边为止。
堆排序的特点是利用了数据结构中的堆。
归并排序算法会把序列分成长度相同的两个子序列,当无法继续往下分时(也就是每个子序列中只有一个数据时),就对子序列进行归并。归并指的是把两个排好序的子序列合并成一个有序序列。该操作会一直重复执行,直到所有子序列都归并为一个整体为止。
快速排序算法首先会在序列中随机选择一个基准值,然后将除了基准值以外的数分为“比基准值小的数”和“比基准值大的数”这两个类别,对里面的数据进行排序时同样也会使用快速排序。
(3)数组的查找
线性查找是一种在数组中查找数据的算法,线性查找的操作很简单,只要在数组中从头开始依次往下查找即可。二分查找也是一种在数组中查找数据的算法,它只能查找
已经排好序的数据。
二分查找通过比较数组中间的数据与目标数据的大小,可以得知目标数据是在数组的左边还是右边。因此,比较一次就可以把查找范围缩小一半。重复执行该操作就可以找到目标数据,或得出目标数据不存在的结论。
(4)图的搜索
由顶点和连接每对顶点的边所构成的图形就是图。加了权的图被称为“加权图”。没有权的边只能表示两个顶点的连接状态,而有权的边就可以表示顶点之间的“连接程度”。给边加上箭头的图叫作“有向图”,没有箭头的图是“无向图”。
广度优先搜索是一种对图进行搜索的算法。此时并不知道图的整体结构,而我们的目的是从起点开始顺着边搜索,直到到达指定顶点(即终点)。在此过程中每走到一个顶点,就会判断一次它是否为终点。广度优先搜索会优先从离起点近的顶点开始搜索。广度优先搜索的特征为从起点开始,由近及远进行广泛的搜索。因此,目标顶点离起点越近,搜索结束得就越快。
深度优先搜索和广度优先搜索一样,都是对图进行搜索的算法,目的也都是从起点开始搜索直到到达指定顶点(终点)。深度优先搜索会沿着一条路径不断往下搜索直到不能再继续为止,然后再折返,开始搜索下一条候补路径。
贝尔曼 - 福特算法是一种在图中求解最短路径问题的算法。最短路径问题就是在加权图指定了起点和终点的前提下,寻找从起点到终点的路径中权重总和最小的那条路径。
狄克斯特拉(Dijkstra)算法也是求解最短路径问题的算法,使用它可以求得从起点到终点的路径中权重总和最小的那条路径路径。比起需要对所有的边都重复计算权重和更新权重的贝尔曼 - 福特算法,狄克斯特拉算法多了一步选择顶点的操作,这使得它在求最短路径上更为高效。