员工管理项目开发日志
Day 1: 类的设计
1.工作内容:
(1) 需求分析:
设计一个客户端软件,利用文件处理方式实现对公司人员进行管理,具有增加数据、更新数据、查询数据、删除数据以及文件读写的功能。员工信息包括:工号、姓名、年龄、岗位(岗位分为三种:销售员Salesman、销售经理SalesManager经理Manager)、销售额(Manager没有这个指标)
(2) 类的设计与要求:
类的设计要求:利用抽象类定义好接口,利用继承做好代码的复用。
(3) 用户接口设计:
要求给用户提供一个统一的接口来实现具体的管理功能(只给用户提供一个函数来使用,在这个函数中让用户选择数字标号代表的功能,再调用对应的函数实现对应的功能),让用户选择具体功能完成对应操作(对员工数据的增删改查)。
(4) 小组作业:
首先,通过小组讨论,主要对项目中所要用的类和可能用到的功能进行了分析,汇总得到以下内容,设置了抽象类 empployee,以及纯虚函数chang()和display()来修改和展示员工信息,还设置子类以及继承方式。
2.问题与解决方案
问题1: 所用到的类较少,没有考虑到将操作功能,操作的界面以及文件的输入输出写成类, 问题2: 在容器的选择上,组员提出了vector< set >嵌套的想法,但是可行度低,没有实际意义, 这种嵌套结构并不能很好地处理数据。 问题3:在容器中计划存放对象,但是考虑到体现多态性,在容器中应该存放指针,存放的空间也 需要考虑到。
解决方案: (1)基于类的思想,添加了操作类,界面操作类,以及文件IO类,通过类来实现管理系统
(2)数据的处理方面,通过vector容器来存放,在容器中存放父类指针,实现多态性,此外把数 据存放在堆空间里面,并进行空间的回收与释放。
Day 2: 项目框架代码实现
1.工作内容:
通过小组合作,在前一天的设计上对员工信息管理系统进行代码实现,每个组员写一 一个类,并由组长进行合并与类之间的连接,在功能的设计方面,安排两名组员来写文件的读写 功能,其他组员分别负责员工信息的修改,添加,以及标记待解雇,删除标记人员的功能的实现。 (1)父类和子类的部分代码如下
#pragma once
#include<iostream>
using namespace std;
//员工类,基类
class Employee {
protected:
int m_ID;//工号
string m_Name;//姓名
int m_Age;//年龄
string m_Position;//岗位
string m_Status;//状态
public:
Employee(int id, string name, int age, string position, string status)
: m_ID(id), m_Name(name), m_Age(age), m_Position(position), m_Status(status) {
}
virtual ~Employee()=default;
// 虚函数,用于显示员工信息
virtual void display() const = 0;
virtual void change() = 0;
virtual string toFileString() const = 0;
int getId() const { return m_ID; }
string getName() const { return m_Name; }
int getAge() const { return m_Age; }
string getStatus() const { return m_Status; }
};
#pragma once
#include"employee.h"
#include<iostream>
#include<string>
#include <iomanip>
class Salesman :public Employee
{
protected:
double m_Sales;
public:
// 正确构造函数:初始化基类+派生类特有成员
Salesman(int id, string name, int age, string position, string status, double sales)
: Employee(id, name, age, position, status), // 必须调用基类构造函数
m_Sales(sales) // 初始化派生类特有成员
{
}
virtual~Salesman() {}
int getId()const
{
return m_ID;
}
string getStatus() const { return m_Status; }
virtual void display() const override {
cout << setw(4) << left << m_ID
<< setw(8) << left << m_Name
<< setw(6) << left << m_Age
<< setw(9) << left << m_Position
<< setw(8) << left << m_Status
<< setw(10) << left << m_Sales << endl; // 显示销售额
}
virtual void change(){
cout << "=====修改销售员信息=====" << endl;
int num;
cout << "请输入想要改变的信息:1姓名, 2年龄,3销售额 ,4岗位状态" << endl;
cin >> num;
switch (num) {
case(1):
cout << "请输入新姓名:";
cin >> m_Name; // 直接修改基类的protected成员
break;
case(2):
cout << "请输入年龄:";
cin >> m_Age; // 直接修改基类的protected成员
break;
case(3):
cout << "请输入新销售额:";
cin >> m_Sales; // 直接修改基类的protected成员
break;
case(4):
cout << "请输入新岗位状态:";
cin >> m_Status; // 直接修改基类的protected成员
break;
case(0):break;
default:
cout << "输入错误!请输入1-4的整数。" << endl;
break;
}
};
string toFileString() const override {
// 格式:工号,姓名,年龄,岗位,状态,工资(字段用逗号分隔,便于读取时拆分)
return to_string(m_ID) + ","
+ m_Name + ","
+ to_string(m_Age) + ","
+ m_Position + ","
+ m_Status + ","
+ to_string(m_Sales);
}
};
(2)部分关键功能设计如下: 修改员工信息的方法,主要通过父类指针去调用子类方法来实现,这里采用auto自动推导类型,更加方便处理,展示员工信息的方法也是基于此种方法实现的。
//方法4:修改员工信息的入口方法
void changeInf() {
// 修改员工信息的入口方法
int targetId;
cout << "请输入要修改的员工工号:";
cin >> targetId;
// 遍历容器查找目标员工
for (auto emp : v) {
if (emp->getId() == targetId)
{ // 调用基类的getId()方法
emp->change(); // 多态c调用:自动执行对应子类的updateInfo()
cout << "修改已完成";
return; // 找到后立即返回
}
}
// 若遍历完未找到
cout << "未找到工号为 " << targetId << " 的员工!" << endl;
}
2.问题与解决方案:
第二天的主要问题在功能的设计上,在设计员工信息的修改时候,若修改员工的职位,则会改变员工的类别,因此需要重新创建一个类对象,把创建后的新的覆盖原来的对象,以及其他功能或设计的细节上的考虑。
解决方案:功能的重新设计与细节的添加
void changePos() {
cout << "请输入要修改职位的员工工号:" << endl;
int target, type;
cin >> target;
// 遍历容器查找目标员工(用迭代器便于删除)
auto it = v.begin();
bool found = false;
while (it != v.end()) {
if ((*it)->getId() == target) {
found = true;
// 1. 保存原员工信息
Employee* oldEmp = *it;
string name = oldEmp->getName(); //将原来的数据取出来 准备给新对象
int age = oldEmp->getAge(); // 需 Employee 有 getAge()
string status = oldEmp->getStatus(); // 需 Employee 有 getStatus()
// 2. 输入新职位类型
cout << "请输入想修改的职位:1.销售员 2.销售经理 3.经理" << endl;
cin >> type;
// 3. 根据类型创建新职位对象(替换原对象)
Employee* newEmp = nullptr;
switch (type) {
case 1:
newEmp = new Salesman(target, name, age, "销售员", status, 0);
break; // 添加 break 防止穿透
case 2:
newEmp = new SaleManager(target, name, age, "销售经理", status, 0);
break;
case 3:
newEmp = new Manager(target, name, age, "经理", status);
break;
default:
cout << "输入错误!请输入1-3的整数。" << endl;
return; // 输入错误时直接退出,不修改
}
// 4. 替换容器中的元素(删除原对象,添加新对象)
delete oldEmp; // 释放原对象内存,避免泄漏
*it = newEmp; // 用新对象替换原位置的指针
cout << "职位修改成功!" << endl;
return; // 完成后退出函数
}
++it;
}
在经理类的change()函数中,不提供修改销售额的功能,而其他两个子类正常提供
virtual void change()
{
cout << "=====修改经理信息=====" << endl;
int num;
cout << "请输入想要改变的信息:1姓名, 2年龄 ,3岗位状态";
cin >> num;
switch (num) {
case(1):
cout << "请输入新姓名:";
cin >> m_Name; // 直接修改基类的protected成员
break;
case(2):
cout << "请输入年龄:";
cin >> m_Age; // 直接修改基类的protected成员
break;
case(3):
cout << "请输入新岗位状态:";
cin >> m_Status; // 直接修改基类的protected成员
break;
case(0):break;
default:
cout << "输入错误!请输入1-4的整数。" << endl;
}
}
Day3 :项目优化与展示
1.工作内容:
基于昨天发现的问题,在改进项目后通过多次对信息管理系统各功能的模拟,发现其中的bug并进行优化项目,部分功能展示如下
2.问题与解决方案:
(1) 在界面操作类里面出现了一些小bug,比如输入功能标号,与实际出现的功能不一致的情况, 其次在操作的时候,输入员工职位或者状态这些文字内容时,考虑到实际操作的情况,应该采用 一些标号来替代文字方便用户操作。
(2) 在验证方面,由于会出现一些错误输入,所以应该考虑到这些异常的情况,设置一些错误提示或者异常处理。
解决方案:对信息管理的各种操作进行优化,并且参考其他小组的项目优势,加到自己的项目里面。
(1) 添加表头
(2)添加各种错误验证:
(3)用数字代替职位和状态的输入,方便用户操作
项目总结
作为学习C++基础后的第一个小型项目,这种信息管理系统既能用到之前学习的C++基础,又能很好地通过类的运用和容器的结合,让新手能够串联起C++基础阶段学到的各种知识,再通过分析各种bug和优化自己的项目,来提升自己的语言思想与代码能力,毕竟眼过千遍,不如手过一遍。