文章目录

  • 问题描述
  • 参考解答
    • 整体架构
    • UML图
    • 整体代码
      • MySQL-C-API封装
      • Model层
      • Dao层
      • Service层
      • Controller层
      • View层
      • 主程序
    • 测试环境
    • 测试流程
    • 测试效果
    • 有待完善之处
    • 项目打包链接

问题描述

根据依赖倒置的原则,编写一个能够实现以下功能的学生管理系统
- 增加一条学生信息
- 删除一条学生信息
- 修改一条学生信息
- 查询一条/全部学生信息要注意依赖抽象而不是依赖具体!

参考解答

整体架构

首先是对C为MySQL提供的API的封装,属于底层实现,不需要考虑学生管理系统等等,只要考虑到读写数据即可。

最后是对整个学生管理系统的封装,是一种MVC设计模式,本项目中包括了Model、Dao、Service、Controller、View这几层:

  • Model层

    模型层,定义学生管理系统要用到的具体对象的模型,这里就是根据学生表的字段封装出学生类Stu

  • Dao层

    数据访问层,规定学生管理系统数据访问的方式,提供数据访问的接口(抽象类)

  • Service层

    服务层,是对Dao层接口的具体实现,可以有多种实现,比如这里提供的就是用MySQL访问的接口

  • Controller层

    请求转接层,根据请求调用Service层进行处理

  • View层

    界面层,用于展示界面,将前端请求发送给Controller层处理

UML图

学生管理系统的UML图如下:

如果感觉不清晰可以到生成这张图片的源网站去查看☛点这里(需要翻墙)

整体代码

MySQL-C-API封装

还是沿用了上次作业的代码,并进行了一些修正如下:

  • 将上次封装中的所有coutcerr输出信息删除
  • 对query的返回值进行了修正,用return 查询到的记录数替代原本的return 0表示查询成功
  • 对exec的返回值进行了修正,用return 受影响的行数替代原本的return 0表示写入成功
  • 对query查询到的数据的展现方式进行了修正,用vector<vector<string>>类型的引用参数_res来存储查询到的数据
    注:本来是想用map<int, vector<string>>类型的引用参数来存储的,但是考虑到map容器的访问结果可能会和存储的顺序不符,因此改用了vector<vector<string>>类型

以下是具体代码:

MyMySQL.h文件

#ifndef MYMYSQL_H
#define MYMYSQL_H#include "mysql.h"  // 需要有MYSQL等结构体的定义
#include <string>
#include <vector>using namespace std;class MyMySQL
{public:MyMySQL();MyMySQL(const char* host, const char* user, const char* password, const char* db, unsigned int port);int MyMySQL_connect(const char * host, const char * user, const char * password, const char * db, unsigned int port);int MyMySQL_query(const char * sql, vector<vector<string>>& _res, const char * q = "gbk");int MyMySQL_execute(const char * sql, const char * q = "gbk");void MyMySQL_close();
private:void MyMySQL_init();MYSQL conn;      // MySQL结构体int init_state;  // 初始化状态 —— 0代表未初始化、1代表已初始化int conn_state;  // 连接状态 —— 0代表未连接、1代表已连接
};#endif // !MYMYSQL_H

MyMySQL.cpp文件

#include "pch.h"#include "MyMySQL.h"
#include <iostream>
#include <string>using namespace std;/**  功能:*      MyMySQL的缺省构造函数*  参数:*      void*  返回值:*      无*  说明:*      只是初始化了conn,并没有连接到某个数据库*  创建时间:*      2020-04-01 10:59:20*  作者:*      Excious**/
MyMySQL::MyMySQL():init_state(1), conn_state(0)
{mysql_init(&this->conn);
}/**  功能:*      MyMySQL的构造函数*  参数:*      host        in    主机*      user        in    用户名*      password    in    密码*      db          in    要连接的数据库*      port        in    端口*  返回值:*      无*  说明:*      使用成员函数MyMySQL_connect尝试连接(里面附带着初始化过程)*  创建时间:*      2020-04-01 14:36:46*  作者:*      Excious**/
MyMySQL::MyMySQL(const char * host, const char * user, const char * password, const char * db, unsigned int port):init_state(0), conn_state(0)
{this->MyMySQL_connect(host, user, password, db, port);
}/**  功能:*      [连接/重新连接]到某个数据库*  参数:*      host        in    主机*      user        in    用户名*      password    in    密码*      db          in    要连接的数据库*      port        in    端口*  返回值:*      -1    连接失败*      0     连接成功*  说明:*      如果conn尚未进行初始化,会对其进行初始化,再重新进行连接*      如果conn已经连接上某个数据库,会先将其关闭,再重新初始化,再重新进行连接*      如果重新连接失败,则之前的连接状态也会被清除,并且将会关闭连接*  创建时间:*      2020-04-01 14:19:03*  作者:*      Excious**/
int MyMySQL::MyMySQL_connect(const char * host, const char * user, const char * password, const char * db, unsigned int port)
{// 根据状态进行相应的预处理if (!this->init_state)      // 未初始化{this->MyMySQL_init();}else if (this->conn_state)  // 已初始化,并已连接某数据库{this->MyMySQL_close();this->MyMySQL_init();}// 尝试连接到某个数据库int flag = 0;if (!mysql_real_connect(&this->conn, host, user, password, db, port, NULL, 0)){this->MyMySQL_close();flag = -1;}else{this->conn_state = 1;}return flag;
}/**  功能:*      执行select语句*  参数:*      sql     in     要执行的select语句*       _res    out    vector<vector<string>>的查询结果集合*      q       in     读取时的编码字符集(缺省值为"gbk")*  返回值:*      -2      尚未连接*      -1      查询失败*      >=0     查询成功,返回查询到的数据个数*  说明:*      前三行代码设置了读取时的字符集(目前感觉读取字符集utf8的表,最靠谱的还是gbk)*  创建时间:*      2020-04-10 15:54:05*  作者:*      Excious**/
int MyMySQL::MyMySQL_query(const char * sql, vector<vector<string>>& _res, const char * q)
{string q_s("set names ");q_s += q;mysql_query(&this->conn, q_s.c_str());// 尚未初始化、连接if (!this->init_state || !this->conn_state){return -2;}// 尝试执行SQL-select语句if (mysql_query(&this->conn, sql))  // 执行失败{return -1;}// 执行成功MYSQL_RES* res = mysql_store_result(&this->conn);MYSQL_ROW row;vector<string> temp;int num = mysql_num_fields(res);int record_num = mysql_num_rows(res);while ((row = mysql_fetch_row(res)) != NULL){temp.clear();for (int i = 0; i < mysql_num_fields(res); i++){temp.push_back(row[i] != NULL ? row[i] : "NULL");}_res.push_back(temp);}mysql_free_result(res);  //释放内存return record_num;
}/**  功能:**  参数:*      sql    in    要执行的update语句、insert语句、delete语句*      q      in    读取时的编码字符集(缺省值为"gbk")*  返回值:*      -2      尚未连接*      -1      执行失败*      >=0     执行成功,返回受影响的行数*  说明:*      前三行代码设置了写入时的字符集(目前感觉写入字符集utf8的表,最靠谱的还是gbk)*  创建时间:*      2020-04-01 15:25:46*  作者:*      Excious**/
int MyMySQL::MyMySQL_execute(const char * sql, const char * q)
{string q_s("set names ");q_s += q;mysql_query(&this->conn, q_s.c_str());// 尚未初始化、连接if (!this->init_state || !this->conn_state){return -2;}// 尝试执行if (mysql_query(&this->conn, sql))  // 执行失败{return -1;}// 执行成功int aff_num = mysql_affected_rows(&conn);mysql_commit(&this->conn);  // 提交事务return aff_num;
}/**  功能:*      关闭连接conn*  参数:*      void*  返回值:*      void*  说明:*      如果初始化状态(init_state)为0,则不会关闭连接*      如果初始化状态(init_state)为1,则对conn进行关闭,并将init_state、conn_state调整至0*  创建时间:*      2020-04-01 14:13:35*  作者:*      Excious**/
void MyMySQL::MyMySQL_close()
{if (this->init_state){mysql_close(&this->conn);this->init_state = 0;this->conn_state = 0;}
}/**  功能:*      初始化连接conn,是连接数据库前的准备*  参数:*      void*  返回值:*      void*  说明:*      如果初始化状态(init_state)为1,则不会进行操作*      如果初始化状态(init_state)为0,则对conn进行初始化,并将init_state调整至1*  创建时间:*      2020-04-01 14:00:58*  作者:*      Excious**/
void MyMySQL::MyMySQL_init()
{if (!this->init_state){mysql_init(&this->conn);this->init_state = 1;}
}

Model层

Model层定义了学生类Stu,除了构造函数、析构函数外,暂时没有成员函数,只包含成员数据int snostring sname,这和数据库中学生表的字段相一致。

Stu.h文件

#include <string>using namespace std;class Stu
{public:Stu();Stu(int _sno, string _sname);~Stu();int getSno()const;string getSname()const;void setSno(int _sno);void setSname(string _sname);
private:int sno;string sname;
};#endif // !STU_H

Stu.cpp文件

#include "pch.h"
#include "Stu.h"Stu::Stu() { }/** 功能:*     Stu类的构造函数*  参数:*     sno      in    学号*      sname    in    姓名*  返回值:*        无*  创建时间:*       2020-04-10 10:55:37*    作者:*     Excious**/
Stu::Stu(int _sno, string _sname):sno(_sno), sname(_sname)
{}Stu::~Stu() { }int Stu::getSno() const { return this->sno; }
string Stu::getSname() const { return this->sname; }
void Stu::setSno(int _sno) { this->sno = _sno; }
void Stu::setSname(string _sname) { this->sname = _sname; }

Dao层

这里的Dao层只提供数据访问的接口,不负责实现,是一个抽象类。

IStuDao.h文件

#ifndef ISTUDAO_H
#define ISTUDAO_H#include "Stu.h"
#include <vector>using namespace std;class IStuDao
{public:IStuDao() { };virtual ~IStuDao() { };virtual int stuInsert(const Stu& _stu) = 0;virtual int stuDelete(const int _sno) = 0;virtual int stuUpdate(const int _sno, const Stu& _stu) = 0;virtual int stuSelect(const int _sno, Stu& _res) = 0;virtual int stuSelectAll(vector<Stu>& _resset) = 0;
};#endif // !ISTUDAO_H

Service层

Service层是对Dao层接口的具体实现,可以有多种Service层实现,这里就是MySQL的Service实现。

StuServiceMySQL.h文件

#ifndef STUSERVICEMYSQL_H
#define STUSERVICEMYSQL_H#include "IStuDao.h"
#include "MyMySQL.h"class StuServiceMySQL :public IStuDao
{public:StuServiceMySQL();virtual ~StuServiceMySQL();virtual int stuInsert(const Stu& _stu);virtual int stuDelete(const int _sno);virtual int stuUpdate(const int _sno, const Stu& _stu);virtual int stuSelect(const int _sno, Stu& _res);virtual int stuSelectAll(vector<Stu>& _resset);
private:MyMySQL* pmysql;
};#endif // !STUSERVICEMYSQL_H

StuServiceMySQL.cpp文件

#include "pch.h"
#include "StuServiceMySQL.h"/**   功能:*     StuServiceMySQL的构造函数*   参数:*     void*   返回值:*        无*  说明:*     用new出来的MyMysQL对象初始化成员指针pmysql,析构函数中delete释放* 创建时间:*       2020-04-10 15:43:47*    作者:*     Excious**/
StuServiceMySQL::StuServiceMySQL():pmysql(new MyMySQL("localhost", "tester", "test", "cpp_homework", 3306))
{}/**    功能:*     StuServiceMySQL的析构函数*   参数:*     void*   返回值:*        无*  说明:*     释放成员指针pmysql*   创建时间:*       2020-04-10 15:51:39*    作者:*     Excious**/
StuServiceMySQL::~StuServiceMySQL()
{if (this->pmysql != NULL)delete this->pmysql;
}/**    功能:*     插入一条学生记录*   参数:*     _stu    in    学生对象(待插入)*  返回值:*        0     插入成功*     -1    插入失败* 创建时间:*       2020-04-10 15:13:13*    作者:*     Excious**/
int StuServiceMySQL::stuInsert(const Stu & _stu)
{string sql("insert into stu_info values(" + to_string(_stu.getSno()) + ", \'" + _stu.getSname() + "\')");if (this->pmysql->MyMySQL_execute(sql.c_str()) < 0){return -1;}return 0;
}/**    功能:*     删除一条学生记录(根据学号)*   参数:*     _sno    in    学生学号(待删除)*  返回值:*        0     删除成功*     -1    删除失败* 创建时间:*       2020-04-10 15:21:41*    作者:*     Excious**/
int StuServiceMySQL::stuDelete(const int _sno)
{string sql("delete from stu_info where sno = " + to_string(_sno));if (this->pmysql->MyMySQL_execute(sql.c_str()) < 0){return -1;}return 0;
}/**    功能:*     更新一条学生记录(根据学号)*   参数:*     _sno    in    学生学号(待更新)*      _stu    in    学生对象(更新)*   返回值:*        0     更新成功*     -1    更新失败* 创建时间:*       2020-04-10 15:23:24*    作者:*     Excious**/
int StuServiceMySQL::stuUpdate(const int _sno, const Stu & _stu)
{string sql("update stu_info set sno = " + to_string(_stu.getSno()) + ", sname = \'" + _stu.getSname() + "\' where sno = " + to_string(_sno));if (this->pmysql->MyMySQL_execute(sql.c_str()) < 0){return -1;}return 0;
}/**    功能:*     查询一条学生记录(根据学号)*   参数:*     _sno    in     学生学号*        _res    out    查询结果——单个学生对象*    返回值:*        0     查询成功(找到记录)*     -1    查询失败(找不到记录)*    创建时间:*       2020-04-10 15:27:15*    作者:*     Excious**/
int StuServiceMySQL::stuSelect(const int _sno, Stu & _res)
{string sql("select * from stu_info where sno = " + to_string(_sno));vector<vector<string>> q_res;if (this->pmysql->MyMySQL_query(sql.c_str(), q_res) <= 0){return -1;}_res = Stu(stoi(q_res[0][0]), q_res[0][1]);return 0;
}/**    功能:*     查询所有学生记录*   参数:*     _sno       in     学生学号*     _resset    out    查询结果——所有学生对象构成的vector容器*  返回值:*        查询到的记录数*    说明:*     对于传入的参数_resset,会对其进行clear操作* 创建时间:*       2020-04-10 15:29:18*    作者:*     Excious**/
int StuServiceMySQL::stuSelectAll(vector<Stu>& _resset)
{string sql("select * from stu_info");vector<vector<string>> q_res;if (this->pmysql->MyMySQL_query(sql.c_str(), q_res) < 0){return -1;}_resset.clear();for (vector<vector<string>>::const_iterator it = q_res.begin(); it != q_res.end(); ++it){_resset.push_back(Stu(stoi((*it)[0]), (*it)[1]));}return _resset.size();
}

Controller层

收集从View层发送来的请求,通过相应的Service层进行调用()。

StuController.h文件

#ifndef STUCONTROLLER_H
#define STUCONTROLLER_H#include "IStuDao.h"class StuController
{public:StuController();StuController(IStuDao* _pStuDao);~StuController();int stuAdd(const Stu& _stu);int stuDel(const int _sno);int stuUpd(const int _sno, const Stu& _stu);int stuQueBySno(const int _sno, Stu& _res);int stuQueAll(vector<Stu>& _resset);
private:IStuDao* pStuDao;
};#endif // !STUCONTROLLER_H

StuController.cpp文件

#include "pch.h"
#include "StuController.h"StuController::StuController() { }/**   功能:*     StuController的构造函数* 参数:*     pStuDao    in    Dao层抽象类指针* 返回值:*        无*  说明:*     使用new XXX()的方式传入参数*     不能以类似 &XXX()的方式传入参数*        因为在析构函数阶段会进行delete* 创建时间:*       2020-04-10 11:31:11*    作者:*     Excious**/
StuController::StuController(IStuDao * _pStuDao):pStuDao(_pStuDao)
{}/**    功能:*     StuController的析构函数* 参数:*     void*   返回值:*        无*  说明:*     释放成员指针p*    创建时间:*       2020-04-10 17:56:09*    作者:*     Excious**/
StuController::~StuController()
{if (this->pStuDao != NULL)delete this->pStuDao;
}int StuController::stuAdd(const Stu & _stu) { return this->pStuDao->stuInsert(_stu); }
int StuController::stuDel(const int _sno) { return this->pStuDao->stuDelete(_sno); }
int StuController::stuUpd(const int _sno, const Stu & _stu) { return this->pStuDao->stuUpdate(_sno, _stu); }
int StuController::stuQueBySno(const int _sno, Stu & _res) { return this->pStuDao->stuSelect(_sno, _res); }
int StuController::stuQueAll(vector<Stu>& _resset) { return this->pStuDao->stuSelectAll(_resset); }

View层

View层用于展示前端界面,接受用户请求,发送给Controller层进行处理,将处理结果进行展示,与用户进行交互。

为了让请求指令更加容易理解,我在类中定义了一个枚举类型enum CMD.

StuView.h文件

#ifndef STUVIEW_H
#define STUVIEW_H#include "StuController.h"
#include "StuServiceMySQL.h"class StuView
{public:StuView();~StuView();void run();
private:enum CMD { CMD_Add, CMD_Del, CMD_Upd, CMD_Que_one, CMD_Que_all, CMD_Exit, CMD_NULL };StuController* pstucon;static string business[6];void initGraph()const;CMD getCMD()const;int execCMD(CMD cmd);int stuViewAdd();int stuViewDel();int stuViewUpd();int stuViewQue_one();int stuViewQue_all();void showQueRes(const Stu& _res)const;void showQueRes(const vector<Stu>& _resset)const;
};#endif // !STUVIEW_H

StuView.cpp文件

#include "pch.h"
#include "StuView.h"
#include <iostream>
#include <iomanip>
#include <string>
#include <conio.h>string StuView::business[6] = { "[1]增加学生", "[2]删除学生", "[3]更新学生", "[4]查询学生", "[5]查询全部", "[0]退出系统" };/** 功能:*     StuView类的缺省构造函数*    参数:*     void*   返回值:*        无*  说明:*     new出Dao层的特定实现,比如这里就用MySQL的实现*        如果要修改MySQL的数据表,比如改为从其他表中读取,请到StuServiceMySQL类的缺省构造函数进行修改*     如果要修改数据的读取来源,比如改为从Oracle读取,请将new StuServiceMySQL()换成其他实现类对象*  创建时间:*       2020-04-10 19:52:03*    作者:*     Excious**/
StuView::StuView():pstucon(new StuController(new StuServiceMySQL()))
{}/**    功能:*     StuView类的缺省构造函数*    参数:*     void*   返回值:*        无*  说明:*     delete掉成员指针*    创建时间:*       2020-04-10 20:18:35*    作者:*     Excious**/
StuView::~StuView()
{if (this->pstucon != NULL)delete this->pstucon;
}/**    功能:*     运行学生管理系统*   参数:*     void*   返回值:*        void*   说明:*     设置了控制台的背景色,监听按键信息调用相应函数* 创建时间:*       2020-04-10 20:13:24*    作者:*     Excious**/
void StuView::run()
{string progress("");for (int i = 0; i < 6; ++i){cout << setw(90) << "正在启动学生管理系统:|";progress += (i ? "██" : "");cout << progress;cout << setw(20 - i * 4) << "|" << endl;Sleep(1000);system("cls");}system("color 9F");this->initGraph();CMD cmd;while (cmd = this->getCMD(), cmd != CMD_Exit){this->execCMD(cmd);Sleep(50);  // 降低CPU占用}system("cls");
}/**    功能:*     绘制初始主菜单界面*  参数:*     void*   返回值:*        void*   创建时间:*       2020-04-10 22:07:38*    作者:*     Excious**/
void StuView::initGraph() const
{cout << setfill(' ') << right << setw(104) << "欢迎来到学生管理系统" << endl;cout << setfill('*') << setw(189) << "" << endl;for (int i = 0; i < sizeof(business) / sizeof(string); ++i){cout << "****" << setfill(' ') << setw(185) << "****" << endl;cout << left << setw(89) << "****";cout << business[i];cout << right << setw(89) << "****";}cout << "****" << setw(185) << "****" << endl;cout << setfill('*') << setw(189) << "" << endl;
}/**    功能:*     接收主菜单按键消息,返回相应指令*    参数:*     void*   返回值:*        CMD_Exit       退出系统*        CMD_Add        增加学生*        CMD_Del        删除学生*        CMD_Upd        更新学生*        CMD_Que_one    查询学生*        CMD_Que_all    查询全部*        CMD_NULL       无操作* 创建时间:*       2020-04-10 22:14:33*    作者:*     Excious**/
StuView::CMD StuView::getCMD() const
{if (_kbhit())  // 如果有按键消息{switch (_getch())  // 因为可以直接return,就不写break了{case '0':return CMD_Exit;case '1':return CMD_Add;case '2':return CMD_Del;case '3':return CMD_Upd;case '4':return CMD_Que_one;case '5':return CMD_Que_all;default:break;}}return CMD_NULL;
}/**    功能:*     处理前端页面(初始主菜单)的请求* 参数:*     cmd    in    前端界面得到的请求* 返回值:*        0     功能正常结束*       -1    执行异常结束*       -2    预判无效请求*   说明:*     对于数据的展现还是直接输出来实现的*      如果有其他展示要求、结果集要求,可以在特定的case下修改具体展示方式* 创建时间:*       2020-04-10 23:22:17*    作者:*     Excious**/
int StuView::execCMD(CMD cmd)
{// 无操作直接returnif (cmd == CMD_NULL){return 0;}system("cls");// 执行相应命令int flag = 0;switch (cmd){case CMD_Add:flag = this->stuViewAdd();break;case CMD_Del:flag = this->stuViewDel();break;case CMD_Upd:flag = this->stuViewUpd();break;case CMD_Que_one:flag = this->stuViewQue_one();break;case CMD_Que_all:flag = this->stuViewQue_all();break;default:break;}system("cls");this->initGraph();return flag;
}/**    功能:*     界面接收增加学生的请求,并发送给成员指针处理,并进行反馈* 参数:*     void*   返回值:*        -2    学生已经存在*       -1    增加学生异常*       0     增加学生成功*   创建时间:*       2020-04-11 01:38:27*    作者:*     Excious**/
int StuView::stuViewAdd()
{HWND hwnd = GetConsoleWindow();  // 获取控制台窗口句柄int _sno;string _sname;Stu _res;cout << "请输入要增加的学生的学号和姓名(以空格分开):";cin >> _sno >> _sname;if (!this->pstucon->stuQueBySno(_sno, _res)){MessageBox(hwnd, L"该学号已被使用!", L"提示", MB_ICONERROR | MB_OK);return -2;}if (this->pstucon->stuAdd(Stu(_sno, _sname))){MessageBox(hwnd, L"增加学生信息失败!", L"提示", MB_ICONERROR | MB_OK);return -1;}MessageBox(hwnd, L"增加学生信息成功!", L"提示", MB_ICONINFORMATION | MB_OK);return 0;
}/**    功能:*     界面接收删除学生的请求,并发送给成员指针处理,并进行反馈* 参数:*     void*   返回值:*        -2    不存在的学生*       -1    删除学生异常*       0     删除学生成功*   创建时间:*       2020-04-11 01:45:18*    作者:*     Excious**/
int StuView::stuViewDel()
{HWND hwnd = GetConsoleWindow();  // 获取控制台窗口句柄int _sno;Stu _res;cout << "请输入要删除的学生的学号:";cin >> _sno;if (this->pstucon->stuQueBySno(_sno, _res)){MessageBox(hwnd, L"不存在该学生!", L"提示", MB_ICONERROR | MB_OK);return -2;}if (this->pstucon->stuDel(_sno)){MessageBox(hwnd, L"删除学生信息失败!", L"提示", MB_ICONERROR | MB_OK);return -1;}MessageBox(hwnd, L"删除学生信息成功!", L"提示", MB_ICONINFORMATION | MB_OK);return 0;
}/**    功能:*     界面接收更新学生的请求,并发送给成员指针处理,并进行反馈* 参数:*     void*   返回值:*        -2    不存在的学生*       -1    更新学生异常*       0     更新学生成功*   创建时间:*       2020-04-11 01:48:09*    作者:*     Excious**/
int StuView::stuViewUpd()
{HWND hwnd = GetConsoleWindow();  // 获取控制台窗口句柄int _sno;int _sno_2;string _sname;Stu _res;cout << "请输入要更新的学生的学号、更新后学生的学号和姓名(以空格分开三者):";cin >> _sno >> _sno_2 >> _sname;if (this->pstucon->stuQueBySno(_sno, _res)){MessageBox(hwnd, L"不存在该学生!", L"提示", MB_ICONERROR | MB_OK);return -2;}if (this->pstucon->stuUpd(_sno, Stu(_sno_2, _sname))){MessageBox(hwnd, L"更新学生信息失败!", L"提示", MB_ICONERROR | MB_OK);return -1;}MessageBox(hwnd, L"更新学生信息成功!", L"提示", MB_ICONINFORMATION | MB_OK);return 0;
}/**    功能:*     界面接收查询学生的请求,并发送给成员指针处理,并进行反馈* 参数:*     void*   返回值:*        -1    查询学生异常 / 不存在的学生*      0     查询学生成功*   创建时间:*       2020-04-11 01:52:13*    作者:*     Excious**/
int StuView::stuViewQue_one()
{HWND hwnd = GetConsoleWindow();  // 获取控制台窗口句柄int _sno;Stu _res;cout << "请输入要查询的学生的学号:";cin >> _sno;system("cls");if (this->pstucon->stuQueBySno(_sno, _res)){MessageBox(hwnd, L"查询学生信息失败(查询异常或不存在该学生)!", L"提示", MB_ICONERROR | MB_OK);return -1;}MessageBox(hwnd, L"查询学生信息成功!\n点击确定后查看具体信息", L"提示", MB_ICONINFORMATION | MB_OK);this->showQueRes(_res);return 0;
}/**    功能:*     界面接收查询所有学生的请求,并发送给成员指针处理,并进行反馈*   参数:*     void*   返回值:*        -1      查询全部学生异常*       >=0     查询全部学生成功,并返回结果集的记录数* 创建时间:*       2020-04-11 01:52:13*    作者:*     Excious**/
int StuView::stuViewQue_all()
{HWND hwnd = GetConsoleWindow();  // 获取控制台窗口句柄vector<Stu> _resset;if (this->pstucon->stuQueAll(_resset) < 0){MessageBox(hwnd, L"查询学生信息失败(查询异常)!", L"提示", MB_ICONERROR | MB_OK);return -1;}MessageBox(hwnd, L"查询学生信息成功!\n点击确定后查看具体信息", L"提示", MB_ICONINFORMATION | MB_OK);this->showQueRes(_resset);return _resset.size();
}/**    功能:*     显示查询单个学生的输出结果*  参数:*     _res    in    查询到的学生*   返回值:*        void*   说明:*     展现方式是在控制台上进行输出,如果有需要修改展现方式,请在此函数内进行修改*    创建时间:*       2020-04-11 00:14:15*    作者:*     Excious**/
void StuView::showQueRes(const Stu& _res) const
{cout << setfill(' ') << right << setw(74) << "+" << setfill('-') << setw(17) << "+" << setw(25) << "+" << endl;cout << setfill(' ') << setw(74) << "|" << setw(17) << "学号 |" << setw(25) << "姓名 |" << endl;cout << setw(74) << "+" << setfill('-') << setw(17) << "+" << setw(25) << "+" << endl;cout << setfill(' ') << setw(74) << "|" << setfill(' ') << setw(15) << _res.getSno() << " |" << setw(23) << _res.getSname() << " |" << endl;cout << setw(74) << "+" << setfill('-') << setw(17) << "+" << setw(25) << "+" << endl;cout << endl;cout << setfill(' ') << setw(104) << "按下任意键返回主菜单" << endl;_getch();
}/**    功能:*     显示查询全部学生的输出结果*  参数:*     _resset    in    查询到的学生的集合* 返回值:*        void*   说明:*     展现方式是在控制台上进行输出,如果有需要修改展现方式,请在此函数内进行修改*    创建时间:*       2020-04-11 00:17:23*    作者:*     Excious**/
void StuView::showQueRes(const vector<Stu>& _resset) const
{cout << setfill(' ') << right << setw(74) << "+" << setfill('-') << setw(17) << "+" << setw(25) << "+" << endl;cout << setfill(' ') << setw(74) << "|" << setw(17) << "学号 |" << setw(25) << "姓名 |" << endl;cout << setw(74) << "+" << setfill('-') << setw(17) << "+" << setw(25) << "+" << endl;for (vector<Stu>::const_iterator it = _resset.begin(); it != _resset.end(); ++it){cout << setfill(' ') << setw(74) << "|" << setw(15) << it->getSno() << " |" << setw(23) << it->getSname() << " |" << endl;}cout << setw(74) << "+" << setfill('-') << setw(17) << "+" << setw(25) << "+" << endl;cout << endl;cout << setfill(' ') << setw(104) << "按下任意键返回主菜单" << endl;_getch();system("cls");this->initGraph();
}

主程序

main.cpp文件

#include "pch.h"#include <iostream>
#include "StuView.h"using namespace std;int main()
{StuView* sys = new StuView();Sleep(1000);sys->run();delete sys;return 0;
}

测试环境

  1. 将MySQL给C提供的API引入到项目中(方法可以参考上次作业)
  2. 主机为loaclhost、端口号为3306的MySQL数据库中,有一个用户名为tester、密码为test的用户,在该数据库中有一个字符集为utf8、排序规则为utf8_general_cicpp_homework数据库,此数据库下有一个stu_info表,初始数据如下图所示:
  3. 控制台调整至全屏状态,屏幕宽度为189,即一行可以容纳最多189个英文字符

测试流程

测试流程如下:

正常测试非法测试

启动程序
查询全部初始数据
查询学生(成功)
更新学生(成功)
删除学生(成功)
增加学生(成功)
增加学生(失败)
删除学生(失败)
更新学生(失败)
查询学生(失败)
查询全部最终数据
退出程序

测试效果

下面用gif来展示具体效果
Tips:在gif的右下角有按键显示,可以观察程序运行时的按键消息

  1. 启动程序
  2. 查询全部初始数据
  3. 查询一个已经存在的学生
  4. 更新一个已经存在的学生
  5. 删除一个已经存在的学生
  6. 增加一个不存在的学生
  7. 增加一个学号已经存在的学生
  8. 删除一个不存在的学生
  9. 更新一个不存在的学生
  10. 查询一个不存在的学生
  11. 查询全部最终数据
  12. 退出程序
    这里本来可以实现按完直接退出程序的,但是我使用VS2017创建的“控制台应用程序”,包含了pch.h文件。如果创建空项目可以实现直接退出,自动关闭控制台窗口。

有待完善之处

程序在以下方面还有待完善

  • 因为用户有可能无意中在输入时给出了超出数量的输入,因此很有可能导致下一次操作时出现一些意料之外的问题。

    应该是可以通过清空输入区缓存来实现的,但是VS2017中cin.clear()cin.sync()fflush(stdin)等方法均无效,暂时也没有找到容易实现、安全稳定的清空方法,可能实现出来会有一点点麻烦,以后再修改吧。

    另外一种方法就是换成图形化界面,用成熟的输入控件去实现接收输入。

  • 控制台需要手动调整至全屏,程序界面不能自适应控制台窗口大小,在非全屏状态下界面的展示很混乱。

项目打包链接

本项目的代码文件、测试视频打包到了百度云

  • 链接:https://pan.baidu.com/s/1x01p6iwlrIcFlh6Piz3wzw
  • 提取码:hd1p

《编码规范和测试方法——C/C++版》作业 ·008——编写一个符合依赖倒置原则的简单学生管理系统相关推荐

  1. 《编码规范和测试方法——C/C++版》作业 ·003——宏定义作用整理、设计删除数组元素的函数

    文章目录 一.宏定义(#define)的作用 1.普通宏 2.带参宏 [注]:特殊用法 3.其他用法和注意点 二.实际问题(函数设计) 1.原题 2.题解参考 (1).面向过程的设计 (2).面向对象 ...

  2. 《编码规范和测试方法——C/C++版》学习笔记 ·001

    文章目录 一.编码规范的目的 二.相关知识补充 1.32位机下C++中各变量占用的字节数 2.按位操作 三.规范事项 1.把常量放在==左边 2.按位位移操作的右操作数必须小于操作的位数 3.不要对有 ...

  3. 《编码规范和测试方法——C/C++版》作业 ·007——C++引入MySQL给C的API并简单封装

    文章目录 问题描述 参考解答 API引入 API测试 一些问题 封装代码 测试环境 测试代码 测试结果 问题描述 使用MySQL为C提供的API,封装出一个访问MySQL数据库的类.要求至少能实现如下 ...

  4. 《编码规范和测试方法——C/C++版》作业 ·006——设计模式 · 模板方法

    文章目录 一.设计模式-模板方法 1.简单介绍 2.框架演示 3.实际案例 一.设计模式-模板方法 1.简单介绍 简单来说,就是先在父类FFF中定义好一个函数AAA,然后这个函数AAA代表了一个操作M ...

  5. 《编码规范和测试方法——C/C++版》作业 ·005——设计一组员工类

    问题描述 Cola公司的雇员分为以下若干类:ColaEmployee :所有员工总的父类属性:员工的姓名,员工的生日月份方法:getSalary(int month) 根据参数月份来确定工资,如果该月 ...

  6. 《编码规范和测试方法——C/C++版》作业 ·004——设计一个顺序表

    问题描述 实现一个能够实现自动扩容ArrayList类的封装(不使用C++容器),主要至少具有增加.删除.求长的功能 参考解答 ArrayList.h文件 #ifndef ARRAYLIST_H #d ...

  7. 《编码规范和测试方法——C/C++版》作业 ·002——函数返回地址、static关键词用法整理

    文章目录 一.函数返回地址的情形 1.函数返回值为指针 二.static关键字用法整理 1.static全局变量 2.static局部变量 3.static函数 4.类的static成员数据 5.类的 ...

  8. 《编码规范和测试方法——C/C++版》学习笔记 ·002

    文章目录 一.相关知识补充 1.switch接受的参数类型 2.内存分区与数据存放 3.变量的作用域与生命期 4.调试开关的使用 二.规范事项 1.杂项 (1).for循环语句中只出现影响循环控制的变 ...

  9. 《设计模式之禅(第三版)》 摘录篇-------依赖倒置原则

    依赖倒置原则((Dependence Inversion Principle,DIP)) 原始定义:High level modules should not depend upon low leve ...

最新文章

  1. java中http解析url,java url 编码(解析http请求汉语言地址 )
  2. 智能计算机科学的奠基人,【编注】神经网络算法奠基人之一沃尔特·皮茨的传奇故事...
  3. linux 关闭自动升级,开启关闭Centos的自动更新(转)
  4. java biginteger log_java – BigInteger:计算可伸缩方法中的小数位数
  5. 物维管理、楼控系统、安防系统、巡检管理、电子巡更、门禁管理、变配电、给排水、防盗报警、消防报警、电梯监视、智能楼宇、物业管理、报修管理、维保管理、工单管理、物料管理、审批、租赁管理、楼宇管理、房产管理
  6. python访问oracle_用Python操作Oracle
  7. mysql 直接删表空间文件_oracle删除(释放)数据文件/表空间流程
  8. JDBC batch批处理Statement executeBatch 具体解释
  9. asp.net网站后台退出后,点后退按钮仍能进,如何安全退出
  10. SoapUI接口测试实例(webservice接口)
  11. 均值,期望和加权平均数理解和区分
  12. mysql 创建utf-8数据库_mysql 创建utf-8数据库
  13. 飞秋FeiQ2013特色功能列表
  14. 幼麟棋牌进入房间逻辑分析
  15. xxd航模电调电路图
  16. java_重写hashCode
  17. nginx根据url中的参数进行转发
  18. Qt中Q_NULLPTR的作用
  19. 计算机学什么怎么学会的,学电脑先学什么 新手怎么学电脑
  20. 制作MMORPG游戏需要储备哪些门子技术

热门文章

  1. 报表软件公司悬赏 BUG,100块钱1个的真实用意
  2. 深入了解帆软报表系统的启动过程三
  3. 树莓派3代linux,树莓派 3B 入门 ARMv8 Arch Linux
  4. 新闻列表页flex_使用css3的Flex布局实现列表展示
  5. 百度地图添加自定义shp图层_GIS当中使用uDig打开shp图层,并查看数据结果
  6. table合并单元格_element ui el-table 合并单元格
  7. 分子排列不同会导致_武汉理工《Matter》:构筑晶体内跨尺度分子筛单晶反应器...
  8. Tr A 矩阵快速幂
  9. Python从list删除元素
  10. Python——三元表达式