《編碼規範和測試方法——C/C++版》作業 ·008——編寫一個符合依賴倒置原則的簡單學生管理系統

問題描述

根據依賴倒置的原則,編寫一個能夠實現以下功能的學生管理系統
- 增加一條學生信息
- 刪除一條學生信息
- 修改一條學生信息
- 查詢一條/全部學生信息

要注意依賴抽象而不是依賴具體!

參考解答

整體架構

首先是對C爲MySQL提供的API的封裝,屬於底層實現,不需要考慮學生管理系統等等,只要考慮到讀寫數據即可。

最後是對整個學生管理系統的封裝,是一種MVC設計模式,本項目中包括了Model、Dao、Service、Controller、View這幾層:

  • Model層

    模型層,定義學生管理系統要用到的具體對象的模型,這裏就是根據學生表的字段封裝出學生類Stu

  • Dao層

    數據訪問層,規定學生管理系統數據訪問的方式,提供數據訪問的接口(抽象類)

  • Service層

    服務層,是對Dao層接口的具體實現,可以有多種實現,比如這裏提供的就是用MySQL訪問的接口

  • Controller層

    請求轉接層,根據請求調用Service層進行處理

  • View層

    界面層,用於展示界面,將前端請求發送給Controller層處理

UML圖

學生管理系統的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)
{
	// 無操作直接return
	if (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. 啓動程序
    1
  2. 查詢全部初始數據
    2
  3. 查詢一個已經存在的學生
    3
  4. 更新一個已經存在的學生
    4
  5. 刪除一個已經存在的學生
    5
  6. 增加一個不存在的學生
    6
  7. 增加一個學號已經存在的學生
    7
  8. 刪除一個不存在的學生
    8
  9. 更新一個不存在的學生
    9
  10. 查詢一個不存在的學生
    10
  11. 查詢全部最終數據
    11
  12. 退出程序
    這裏本來可以實現按完直接退出程序的,但是我使用VS2017創建的“控制檯應用程序”,包含了pch.h文件。如果創建空項目可以實現直接退出,自動關閉控制檯窗口。
    12

有待完善之處

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

  • 因爲用戶有可能無意中在輸入時給出了超出數量的輸入,因此很有可能導致下一次操作時出現一些意料之外的問題。

    應該是可以通過清空輸入區緩存來實現的,但是VS2017中cin.clear()cin.sync()fflush(stdin)等方法均無效,暫時也沒有找到容易實現、安全穩定的清空方法,可能實現出來會有一點點麻煩,以後再修改吧。

    另外一種方法就是換成圖形化界面,用成熟的輸入控件去實現接收輸入。

  • 控制檯需要手動調整至全屏,程序界面不能自適應控制檯窗口大小,在非全屏狀態下界面的展示很混亂。

項目打包鏈接

本項目的代碼文件、測試視頻打包到了百度雲

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章