ARTS

首先简单介绍一下 ARTS 各部分的含义:

  • Algorithm

    每周至少做一个 leetcode 的算法题目;

  • Review

    阅读并点评至少一篇英文技术文章;

  • Tip

    学习至少一个技术技巧;

  • Share

    分享一篇有观点和思想的技术文章。

接下来将会按照以上步骤完成 ARTS 一星期计划。

Algorithm

由于最近需要考 PAT,故暂不考虑 leetcode,而采用 PAT 官方提供了题目集进行练习。

1042 Shuffling Machine

这个题目虽然是英文的,不过非常简单。整个过程就是一个洗牌的过程,提供洗牌的次数和洗牌的顺序。初始牌的顺序都是按照自然数序列。似乎我的思路和标准解答似乎不太一样,且标准解答中似乎也要简单且好理解一些。

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define SIZE 54

int visits[SIZE] = {0};

char map[5] = {'S', 'H', 'C', 'D', 'J'};

void swap(int* left, int* right) {
int temp = *left;
*left = *right;
*right = temp;
}

int main() {
int cards[SIZE] = {0};
// printf("Initization:\n");
for(int i = 0; i < SIZE; i++) {
cards[i] = i;
// printf("%d\n", cards[i]);
}
// Input repeat times
int repeatTimes = 0;
scanf("%d", &repeatTimes);

// printf("Repeat times: %d\n", repeatTimes);

int order[SIZE] = {0};

// printf("Order:\n");
for(int i = 0; i < SIZE; i++) {
scanf("%d", &order[i]);
order[i]--;
// printf("%d\n", order[i]);
}

for(int j = 0; j < repeatTimes; j++) {
memset(visits, 0, SIZE * visits[0]);
for(int i = 0; i < SIZE; i++) {
if(visits[i] > 0) {
continue;
}
int k = order[i];
while(k != i) {
int temp = cards[i];
cards[i] = cards[k];
cards[k] = temp;
visits[k] = 1;
k = order[k];
}
}
}

// printf("Result:\n");
for(int i = 0; i < SIZE; i++) {
char prefix = map[cards[i] / 13];
int num = cards[i] % 13 + 1;
if(i != 0) {
printf(" ");
}
printf("%c%d", prefix, num);
}

return 0;
}

Review

Information Is Bits + Context (Section 1.1)

Computer Systems: A Programmer’s Perspective, Third Edition

这是本书的第一章的第一小结。以 hello.c 这个最简单的
C 语言程序来作为切入点,剖析并深入计算机系统。

书籍中表示计算机中的任何系统信息(不论是磁盘文件,内存中的程序,内存中的用户数据,通过网络传输的数据)都是以流(bits)的形式存在。唯一能够区分数据类型的就是上下文(context),我们可以根据上下文可以知道某些字节(bytes)是整数、浮点数、字符串或者机器指令。

Tip

JPA 表的复用

我们经常会遇到这样一个问题:

多个实体对应一张表,按照正常的逻辑来看,我们可能新增一个 type 字段进行标识该条记录是隶属于哪个实体的。这样虽然能够解决燃眉之急,但在后期维护和对接来看,并不是特别优雅。

我们需要解决的问题在于:

逻辑上多个实体,物理上仅一张表。

这里我们以评论表:comments 为例。评论表对应两个实体:PostComment(文章评论) 和 JournalComment(日志评论),这里自然会想到在评论表中添加 type 字段,加以区分。这里确实该这么做,但是如何在应用程序中处理起来更加优雅呢?

我们可以通过一个注解 @DiscriminatorColumn 完成 type 的自动定义,并且在每次进行数据库操作的时候都自动加上这个字段,而无需我们程序员手动判断。这样在逻辑上就是两张互不相干的表了。

BaseComment Entity:(Virtual)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Entity(name = "BaseComment")
@Table(name = "comments")
@DiscriminatorColumn(name = "type", discriminatorType = DiscriminatorType.INTEGER, columnDefinition = "int default 0")
public class BaseComment {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

/**
* Commentator's name.
*/
private String author;

/**
* Commentator's email.
*/
private String email;

...
}

PostComment Entity:Context

1
2
3
4
5
@Entity(name = "PostComment")
@DiscriminatorValue("0")
public class PostComment extends BaseComment {

}

JournalComment Entity:

1
2
3
4
5
@Entity(name = "JournalComment")
@DiscriminatorValue("2")
public class JournalComment extends BaseComment {

}

Conclusions

通过这种注解的方式,在逻辑上分离了两种不同的评论(物理上仍然是同一张表),大大减轻了程序的判断逻辑,降低出错的概率,让程序变得更加优雅。

Share

有关 Eager Read Derivation Pattern 的详解。

https://martinfowler.com/bliki/EagerReadDerivation.html