在写代码前,构建其代码层次, 使代码具有模块化。
我会将代码分为四层:
- 数据层
- 接口层
- 业务逻辑层
- 用户界面层
这样的分层有助于模块化和代码的可维护性。每一层都有其明确的职责,使得代码更易于理解和修改。当需要对某一层进行修改或优化时,可以专注于该层,而不必担心影响到其他层的代码。
- 我们首先来看数据层
这里数据层的LIST_INSERT()
和LIST_REMOVE()
作用是链表操作的宏。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define NAME_LENGTH 16
#define PHONE_LENGTH 32
#define BUFFER_LENGTH 128
#define MIN_TOKEN_LENGTH 5
#define INFO printf
#define LIST_INSERT(item, list) do { \
item->prev = NULL; \
item->next = list; \
if ((list) != NULL) (list)->prev = item; \
(list) = item; \
} while(0)
#define LIST_REMOVE(item, list) do { \
if (item->prev != NULL) item->prev->next = item->next; \
if (item->next != NULL) item->next->prev = item->prev; \
if (list == item) list = item->next; \
item->prev = item->next = NULL; \
} while(0)
至于为什么使用宏可以看这篇文章 为什么使用宏
- 数据层的
struct person
和struct contacts
:定义了联系人和联系人列表的数据结构。
struct person {
char name[NAME_LENGTH];
char phone[PHONE_LENGTH];
struct person *next;
struct person *prev;
};
struct contacts {
struct person *people;
int count;
};
数据层中的save_file()和
load_file()`:负责将联系人信息保存到文件和从文件中加载联系人信息。
int save_file(struct person *people, const char *filename) {
FILE *fp = fopen(filename, "w");
if (fp == NULL) return -1;
struct person *item = NULL;
for (item = people; item != NULL;item = item->next) {
fprintf(fp, "name: %s, phone: %s\n", item->name, item->phone);
fflush(fp);
}
fclose(fp);
}
int load_file(struct person **ppeople, int *count, const char *filename) {
FILE *fp = fopen(filename, "r");
if (fp == NULL) return -1;
while (!feof(fp)) {
char buffer[BUFFER_LENGTH] = {0};
fgets(buffer, BUFFER_LENGTH, fp);
int length = strlen(buffer);
INFO("length : %d\n", length);
// name: qiuxiang,telephone: 98765678123
char name[NAME_LENGTH] = {0};
char phone[PHONE_LENGTH] = {0};
if (0 != parser_token(buffer, length, name, phone)) {
continue;
}
struct person *p = (struct person*)malloc(sizeof(struct person));
if (p == NULL) return -2;
memcpy(p->name, name, NAME_LENGTH);
memcpy(p->phone, phone, PHONE_LENGTH);
person_insert(ppeople, p);
(*count) ++;
}
fclose(fp);
return 0;
}
数据层中的parser_token()
:解析文件中的字符串,提取联系人的姓名和电话。
int parser_token(char *buffer, int length, char *name, char *phone) {
if (buffer == NULL) return -1;
if (length < MIN_TOKEN_LENGTH) return -2;
int i = 0, j = 0, status = 0;
for (i = 0;buffer[i] != ',';i ++) {
if (buffer[i] == ' ') {
status = 1;
} else if (status == 1) {
name[j ++] = buffer[i];
}
}
status = 0;
j = 0;
for (;i < length;i ++) {
if (buffer[i] == ' ') {
status = 1;
} else if (status == 1) {
phone[j ++] = buffer[i];
}
}
INFO("file token : %s --> %s\n", name, phone);
return 0;
}
- 接下来是接口层:这一层提供了一组接口,用于操作数据层的结构。这些函数提供了对联系人链表的基本操作。
// define interface
char name[NAME_LENGTH] = {0};
char phone[PHONE_LENGTH] = {0};
int person_insert(struct person **ppeople, struct person *ps) {
if (ps == NULL) return -1;
LIST_INSERT(ps, *ppeople);
return 0;
}
int person_delete(struct person **ppeople, struct person *ps) {
if (ps == NULL) return -1;
if (ppeople == NULL) return -2;
LIST_REMOVE(ps, *ppeople);
return 0;
}
struct person* person_search(struct person *people, const char *name) {
struct person *item = NULL;
for (item = people;item != NULL;item = item->next) {
if (!strcmp(name, item->name))
break;
}
return item;
}
int person_traversal(struct person *people) {
struct person *item = NULL;
for (item = people;item != NULL;item = item->next) {
INFO("name: %s,phone: %s\n", item->name, item->phone);
}
return 0;
}
- 业务逻辑层:这一层包含了程序的主要业务逻辑,处理用户的输入和程序的流程控制。
这些函数根据用户的操作(添加、打印、删除、搜索、保存和加载)来处理联系人信息。
int insert_entry(struct contacts *cts) {
if (cts == NULL) return -1;
struct person *p = (struct person*)malloc(sizeof(struct person));
if (p == NULL) return -2;
memset(p, 0, sizeof(struct person));
// name
INFO("Please Input Name: \n");
scanf("%s", p->name); //
// phone
INFO("Please Input Phone: \n");
scanf("%s", p->phone);
// add people
if (0 != person_insert(&cts->people, p)) {
free(p);
return -3;
}
cts->count ++;
INFO("Insert Success\n");
return 0;
}
int print_entry(struct contacts *cts) {
if (cts == NULL) return -1;
// cts->people
person_traversal(cts->people);
}
int delete_entry(struct contacts *cts) {
if (cts == NULL) return -1;
INFO("Please Input Name : \n");
char name[NAME_LENGTH] = {0};
scanf("%s", name);
// person
struct person *ps = person_search(cts->people, name);
if (ps == NULL) {
INFO("Person don't Exit\n");
return -2;
}
INFO("name: %s, phone: %s\n", ps->name, ps->phone);
// delete
person_delete(&cts->people, ps);
free(ps);
return 0;
}
int search_entry(struct contacts *cts) {
if (cts == NULL) return -1;
INFO("Please Input Name : \n");
char name[NAME_LENGTH] = {0};
scanf("%s", name);
// person
struct person *ps = person_search(cts->people, name);
if (ps == NULL) {
INFO("Person don't Exit\n");
return -2;
}
INFO("name: %s,phone: %s\n", ps->name, ps->phone);
return 0;
}
int save_entry(struct contacts *cts) {
if (cts == NULL) return -1;
INFO("Please Input Save Filename :\n");
char filename[NAME_LENGTH] = {0};
scanf("%s", filename);
save_file(cts->people, filename);
}
int load_entry(struct contacts *cts) {
if (cts == NULL) return -1;
INFO("Please Input Load Filename :\n");
char filename[NAME_LENGTH] = {0};
scanf("%s", filename);
load_file(&cts->people, &cts->count, filename);
}
-
- 用户界面层:这一层处理与用户的交互。
menu_info()
:显示程序菜单。
void menu_info(void) {
INFO("\n\n********************************************************\n");
INFO("***** 1. Add Person\t\t2. Print People ********\n");
INFO("***** 3. Del Person\t\t4. Search Person *******\n");
INFO("***** 5. Save People\t\t6. Load People *********\n");
INFO("***** Other Key for Exiting Program ********************\n");
INFO("********************************************************\n\n");
}
- main函数入口
int main() {
struct contacts *cts = (struct contacts *)malloc(sizeof(struct contacts));
if (cts == NULL) return -1;//malloc返回的是void*类型的指针 所以要转换类型
memset(cts, 0, sizeof(struct contacts));
while (1) {
menu_info();
int select = 0;
scanf("%d", &select);
switch (select) {
case OPER_INSERT:
insert_entry(cts);
break;
case OPER_PRINT:
print_entry(cts);
break;
case OPER_DELETE:
delete_entry(cts);
break;
case OPER_SEARCH:
search_entry(cts);
break;
case OPER_SAVE:
save_entry(cts);
break;
case OPER_LOAD:
load_entry(cts);
break;
default:
goto exit;
}
}
exit:
free(cts);
return 0;
}