4. 通讯录实现的需求分析和架构设计

本文实现的是通讯录产品的需求分析和架构设计,重点在于结构层次的设计,方便代码阅读和维护。

一、通讯录实现的需求分析

1、通讯录的功能清单

  1. 添加一个人员
  2. 打印显示所有人员
  3. 删除一个人员
  4. 查找一个人员
  5. 保存文件
  6. 加载文件

2,数据存储信息

  1. 人员存储方式 ——> 双向链表
  2. 文件存储格式 ——> 人员数据的格式
  3. 人员信息 ——> 姓名,电话
    name: xxx,phone: xxx
    name: xxx,phone: xxx

二、通讯录实现的架构设计

1、架构的设计应该从底层往上分析。

  • 支持层:数据链表的存储,以及文件的读写。
  • 接口层:将底层的链表数据进行读取后解析出name和phone(解包),以及读取name和phone后打包写入链表数据中(打包)。另外还有统一的功能接口层,这样即使文件存储方式改变,上层设计仍可以保持不变。
  • 业务层:业务逻辑

具体举个例子:
添加一个用户(功能) —> 输入用户名和电话号码(业务逻辑) —> 通过接口层add —> 插入到链表中
在这里插入图片描述
在这里插入图片描述

2、代码和难点

2.1代码实现过程中遇到以下几个难点

  1. 二级指针
//插入
/*如果传入struct person *person,一开始person指向的是NULL,那么形参改变不会影响实参。
    参考值传递交换两个数。因此要传入的是二级指针struct person **pperson*/
int person_insert(struct person **pperson,struct person *ps)

//删除
/*如果传入struct person *person,最后person指向的是NULL,那么形参改变不会影响实参。
    参考值传递交换两个数。因此要传入的是二级指针struct person **pperson*/ 
int person_delete(struct person **pperson,struct person *ps)

//加载文件
//若使用struct person *person,刚开始空时候指向的是NULL,无法更改,因此要用二级指针
int load_file(struct person **pperson,int *count,const char *filename)

2.利用状态机读取文件信息存入到通讯录中

//解析文本内的通讯信息
int parser_token(char *buffer,int length,char *name,char *phone)
int load_file(struct person **pperson,int *count,const char *filename)
  1. 文件的操作函数
  2. 链表的插入删除

2.2具体代码如下

#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
//''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
//''''''''''''''''''''支持层''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

//do{...}while(0)一般用于宏定义中,只执行依次,并且条件不成立时退出
//宏不止一行,则在结尾加反斜线符号使得多行能连接上
//把item插入到list之前,(list)是因为传入的是*pperson,加括号是让指针优先,即(*pperson)->prev            
#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)


//person类,包含姓名、电话
struct person
{
    char name[NAME_LENGTH];
    char phone[PHONE_LENGTH];
    struct person *next;
    struct person *prev;
};

//通讯录,里面有person类,总人数
struct contact
{
    struct person *person;
    int count;  //人数
};


enum{
    OPEN_INSERT=1,
    OPEN_PRINT,
    OPEN_DELETE,
    OPEN_SEARCH,
    OPEN_SAVE,
    OPEN_LOAD
};

//''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
//''''''''''''''''''''接口层''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
//插入
int person_insert(struct person **pperson,struct person *ps){ 
    /*如果传入struct person *person,一开始person指向的是NULL,那么形参改变不会影响实参。
    参考值传递交换两个数。因此要传入的是二级指针struct person **pperson
    */
    if ( ps == NULL ) return -1;
    LIST_INSERT(ps,*pperson);
    return 0;
}


//删除
int person_delete(struct person **pperson,struct person *ps){
     /*如果传入struct person *person,最后person指向的是NULL,那么形参改变不会影响实参。
    参考值传递交换两个数。因此要传入的是二级指针struct person **pperson
    */  
    if ( ps == NULL ) return -1;
    LIST_REMOVE(ps,*pperson);
    return 0;
}


//查找
struct person* person_search(struct person *person,const char *name){
    struct person *item = NULL;
    for (item=person;item != NULL ;item=item->next){
        if (!strcmp(item->name , name)){
            break;;
        }
    }
    return item;
}

//遍历
int person_traversal(struct person *person){
    struct person *item = NULL;
    for (item=person;item != NULL ;item=item->next){
        INFO("name: %s,phone: %s\n",item->name,item->phone);
    }   
    return 0;
}

//保存文件
int save_file(struct person *person,const char *filename){
    FILE *fp=fopen(filename,"w");
    if (fp == NULL) return -1;
    struct person *item=NULL;
    for (item=person;item!=NULL;item=item->next){
        fprintf(fp,"name: %s,phone: %s\n",item->name,item->phone);
        fflush(fp);//fprintf是将数据存在缓冲区内,通过fflush刷新到磁盘中
    }

    fclose(fp);
}

//解析文本内的通讯信息
int parser_token(char *buffer,int length,char *name,char *phone){
    if (buffer == NULL ) return -1;
    if (length <MIN_TOKEN_LENGTH) return -2;  //文件结尾默认有一个文件结束标识符,大小不会超过5个字节

    //name: qiuxiang,telephone: 98765678123
    int i=0,j=0,status=0;

    //读取 name: qiuxiang
    for (i=0;buffer[i]!=',';i++){
        if (buffer[i]==' '){
            status=1;
        }
        else if(status==1){
            //将buffer[i]赋值给name[j],而后j++
            name[j++]=buffer[i];
        }
    }

    //读取 telephone: 98765678123
    status=0;
    j=0;
    for (;i<length;i++){
         if (buffer[i]==' '){
            status=1;
        }
        else if(status==1){
            //将buffer[i]赋值给name[j],而后j++
            phone[j++]=buffer[i];
        }       
    }

    INFO("file token : %s --> %s\n", name, phone);

    return 0;
}
//加载文件
//若使用struct person *person,刚开始空时候指向的是NULL,无法更改,因此要用二级指针
int load_file(struct person **pperson,int *count,const char *filename){
    FILE *fp=fopen(filename,"r");
    if (fp == NULL ) return -1;

    //feof():侦测是否读取到了文件尾,如果已到文件尾则返回非零值,其他情况返回 0
    while (!feof(fp)){
        char buffer[BUFFER_LENGTH]={0};

        //fgets(str,n,fp):从 fp 所指文件中读入 n-1 个字符放入 str 为起始地址的空间内;如果在未读满 n-1 个字符时,则遇到换行符或一个 EOF 结束本次读操作,并已 str 作为函数值返回.
        fgets(buffer,BUFFER_LENGTH,fp);

        int length=strlen(buffer);
        INFO("legth :%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;

        //void *memcpy(void *str1, const void *str2, size_t n) 从存储区 str2 复制 n 个字节到存储区 str1
        memcpy(p->name,name,NAME_LENGTH);
        memcpy(p->phone,phone,PHONE_LENGTH);

        person_insert(pperson,p);

        (*count)++;
    }
    fclose(fp);
    return 0;
}

//''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
//''''''''''''''''''''业务逻辑层''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

int insert_entry(struct contact *cts){
    if (cts ==NULL) return -1;  //输入空信息错误

    struct person *p=(struct person*)malloc(sizeof(struct person));
    if (p == NULL) return -2;  //内存分配错误

    //name
    INFO ("Please Input Name:\n");
    scanf("%s",p->name);

    //phone
    INFO ("Please Input Phone:\n");
    scanf("%s",p->phone);

    //add to person
    if (0 !=person_insert(&cts->person,p)){
        free(p);
        return -3;
    }
    cts->count++;

    INFO("Insert Success\n");
    return 0;
}

int print_entry(struct contact *cts){
    //打印通讯录中的信息
    if (cts == NULL ) return -1;

    person_traversal(cts->person);
}

int delete_entry(struct contact *cts){
    //删除通讯录中某个人的信息
    if (cts == NULL ) return -1;

    INFO("Please Input Name:\n");
    char name[NAME_LENGTH] ={0};
    scanf("%s",name);

    //判断输入的name是否存在通讯录中
    struct person *ps=person_search(cts->person,name);
    if (ps == NULL) {
        INFO("Person don't Exit\n");
        return -2;
    }

    //删除
    person_delete(&cts->person,ps);
    free(ps);

    return 0;
}

int search_entry(struct contact *cts){
    //查找通讯录中某个人的信息
    if (cts == NULL ) return -1;

    INFO("Please Input Name:\n");
    char name[NAME_LENGTH] ={0};
    scanf("%s",name);

    //判断输入的name是否存在通讯录中
    struct person *ps=person_search(cts->person,name);
    if (ps == NULL) {
        INFO("Person don't Exit\n");
        return -2;
    }

    INFO("name: %s, phone: %s",ps->name,ps->phone);

    return 0;
}

int save_entry(struct contact *cts){
    //将通讯录保存
    if (cts==NULL) return -1;

    INFO("Please Input Save Filename:\n");
    char filename[NAME_LENGTH]={0};
    scanf("%s",filename);

    save_file(cts->person,filename);
}

int load_entry(struct contact *cts){
    //加载文件中的通讯录
    if(cts==NULL)  return -1;

    INFO("Please Input Load Filename:\n");
    char filename[NAME_LENGTH]={0};
    scanf("%s",filename);

    load_file(&cts->person,&cts->count,filename);
}

void menu_info() {

	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");

}


int main(){
    struct contact *cts =(struct contact *)malloc(sizeof(struct contact));
    if(cts == NULL) return -1;

    memset(cts,0,sizeof(struct contact));  //初始化cts为0
    while(1){
        menu_info();
        int select=0;
        scanf("%d",&select);
        switch (select)
        {
            case OPEN_INSERT:
                insert_entry(cts);
                break;
            case OPEN_PRINT:
                print_entry(cts);
                break;
            case OPEN_DELETE:
                delete_entry(cts);
                break;
            case OPEN_SEARCH:
                search_entry(cts);
                break;
            case OPEN_SAVE:
                save_entry(cts);
                break;
            case OPEN_LOAD:
                load_entry(cts);
                break;
            default:
                goto exit; 

        }

    }

    exit:
        return 0;
        free(cts);
}


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
好的,以下是一个简单的用Java设计通讯录保存信息的示例: ```java import java.awt.*; import java.awt.event.*; import javax.swing.*; import java.io.*; import java.util.*; public class ContactList extends JFrame implements ActionListener { private JTextField nameField, phoneField, searchField; private JTextArea displayArea; private JButton addButton, searchButton, saveButton, loadButton; private ArrayList<Contact> contacts; public ContactList() { super("Contact List"); contacts = new ArrayList<Contact>(); // 设置界面布局 JPanel inputPanel = new JPanel(new GridLayout(3, 2)); inputPanel.add(new JLabel("Name:")); nameField = new JTextField(); inputPanel.add(nameField); inputPanel.add(new JLabel("Phone:")); phoneField = new JTextField(); inputPanel.add(phoneField); addButton = new JButton("Add"); addButton.addActionListener(this); inputPanel.add(addButton); inputPanel.add(new JLabel("")); JPanel searchPanel = new JPanel(new FlowLayout()); searchField = new JTextField(20); searchPanel.add(searchField); searchButton = new JButton("Search"); searchButton.addActionListener(this); searchPanel.add(searchButton); JPanel displayPanel = new JPanel(new BorderLayout()); displayArea = new JTextArea(15, 30); displayArea.setEditable(false); JScrollPane scrollPane = new JScrollPane(displayArea); displayPanel.add(scrollPane, BorderLayout.CENTER); JPanel filePanel = new JPanel(new FlowLayout()); saveButton = new JButton("Save"); saveButton.addActionListener(this); filePanel.add(saveButton); loadButton = new JButton("Load"); loadButton.addActionListener(this); filePanel.add(loadButton); JPanel mainPanel = new JPanel(new BorderLayout()); mainPanel.add(inputPanel, BorderLayout.NORTH); mainPanel.add(searchPanel, BorderLayout.CENTER); mainPanel.add(displayPanel, BorderLayout.SOUTH); mainPanel.add(filePanel, BorderLayout.EAST); add(mainPanel); pack(); setVisible(true); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } public void actionPerformed(ActionEvent e) { if (e.getSource() == addButton) { String name = nameField.getText(); String phone = phoneField.getText(); Contact c = new Contact(name, phone); contacts.add(c); displayContacts(); } else if (e.getSource() == searchButton) { String query = searchField.getText(); ArrayList<Contact> results = searchContacts(query); displayContacts(results); } else if (e.getSource() == saveButton) { saveContacts(); } else if (e.getSource() == loadButton) { loadContacts(); displayContacts(); } } private void displayContacts() { displayArea.setText(""); for (Contact c : contacts) { displayArea.append(c.getName() + " " + c.getPhone() + "\n"); } } private void displayContacts(ArrayList<Contact> list) { displayArea.setText(""); for (Contact c : list) { displayArea.append(c.getName() + " " + c.getPhone() + "\n"); } } private ArrayList<Contact> searchContacts(String query) { ArrayList<Contact> results = new ArrayList<Contact>(); for (Contact c : contacts) { if (c.getName().contains(query) || c.getPhone().contains(query)) { results.add(c); } } return results; } private void saveContacts() { try { ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("contacts.dat")); out.writeObject(contacts); out.close(); } catch (IOException e) { e.printStackTrace(); } } private void loadContacts() { try { ObjectInputStream in = new ObjectInputStream(new FileInputStream("contacts.dat")); contacts = (ArrayList<Contact>)in.readObject(); in.close(); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } } public static void main(String[] args) { new ContactList(); } } class Contact implements Serializable { private String name; private String phone; public Contact(String name, String phone) { this.name = name; this.phone = phone; } public String getName() { return name; } public String getPhone() { return phone; } } ``` 这个程序使用了Swing库实现了一个简单的GUI界面,包括输入框、按钮、文本区域等。通讯录数据以一个包含联系人姓名和电话号码的Contact类的形式保存,使用ArrayList来管理多个联系人。程序实现了添加联系人、搜索联系人、保存联系人、载入联系人等功能,并将数据以序列化的形式保存到文件中。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

闲谈社

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值