设计模式(五)三个设计原则 单一职责、里氏替换原则、合成原则

面向对象编程中,常见的设计原则有六个:

单一职责原则、开闭原则、合成原则、里氏替换原则、依赖倒转原则、接口原则、迪米特法则。

这里只介绍三个,下一篇文章介绍另三个。

单一职责原则

这个很简单,就是一个类的职责只做一类型的事情。注意,是一类型,而不是一件事。

比如上一篇文章中的EmployeeManage类,它的作用就是管理职工,所以里面可以有:初始化职工信息、查询职工、添加职工、删除职工等接口,这些接口属于同一类型,都是对员工的管理。

如果员工需要换等级呢?这个接口当然也可以!

那什么类型的接口不行呢?

比如说获取指定员工的薪水,有些人为了访问方便,直接在这个类里加一个接口,传入员工姓名,返回他的薪水。这就违反了单一职责原则。

程序设计中,这条原则,一定要时刻牢记!重中之重!

程序设计之初,还感觉不太出来,随着程序功能的扩展,尤其是维护的人员有变动的时候,不遵循这条原则的代码,会显得非常换乱不堪,根本没法维护。

里氏替换原则

这个原则可以说是面向对象设计和多态基石。

用人话翻译一下就是:所有用父类的地方,都可以用其子类替代。但是用子类的地方,不一定能用父类替换。

很容易理解,子类是对父类的派生,其功能肯定是大于等于父类的,所以子类的地方不能用父类替换。

为什么一定要强调父类的地方可以用子类替代呢?

这就要从你准备继承的时候说起,父类和子类是不是真的有继承的关系?这个关系,不是语法上的,而是功能上的。

后面和合成原则一起给出例子。

合成原则

这个原则很有用!

这个原则用人话说就是:对于复杂功能的类,尽量用组合,不要用继承。

什么意思呢?

还是用前面那个职工等级的例子来说,其实那个例子写得太过简单,我们不妨稍微扩展一下:

1、员工有部门,部门对于大公司来说,是一个独立的个体,有部门名称、部门预算等等;

2、职级不仅和工资有关,也和员工的权限有关,不同职级,权限不同。

初学面向对象,尤其是C++还可以多重继承,很多人可能会想到如下实现方式:

有个部门类Department;还有有职级类Grade,Grade派生Grade1、Grade2等不同的职级;最后才是职工类Employee,对其也进行派生Employee1、Employee2,不同的职工,多继承自Department核对应的具体Grade类。

这是我在工作中见到的真实例子,虽然不是职工、部门,但功能与这个几乎一致,而且是一个工作了20年+的老工程师写的。看到代码的那一刻,我差点一口老血喷出五米远。

首先,这么设计,就不满足里氏替换原则。

所有涉及到部门的地方,都能用职工替换掉?代码上逻辑可能没错误,但是实际功能,根本不是这么回事。部门的预算,你怎么用员工替换?部门领导换人,怎么用员工替换?

这种设计,是典型的为了面向对象而面向对象,仅仅只是了解了面向对象的语法知识,而根本不知道其实际内涵。

很可惜,现在的软件市场上,尽是这种人。某种语言的偏门语法用得贼溜,以此来炫技,到几万甚至几千行的功能设计时,写出了一坨翔。

上面的功能,应该怎么做呢?其实想想合成原则,等级,说白了只是员工的一个属性而已,这个属性会随着员工能力的提升而变动。另外,部门,也只是员工的一个属性,大一点的公司,员工换部门的事情常有发生。除了这些,员工还有一些基本信息,比如姓名、性别、联系方式等等,这些,才是独属于这个员工不变的,部门、职级等都会改变。

如果把部门和等级当做员工的一个成员变量,多个类合成在一起,组成员工类,是不是更好?

这就是合成原则的内涵。

看下面的代码,还是基于之前的员工功能,做一些扩展优化:

#pragma once

#include <iostream>
#include <list>
#include <string>
#include "iomanip"


class Employee;
/**
 * @brief 部门类
*/
class Department
{
public:
	Department(const std::string& strName) :m_strName(strName) {}

	/// @brief 获取部门名称
	std::string GetName() const { return m_strName; }

	void AddEmpplyee(const std::string& strNum); 

protected:
	friend class DepartmentManage;
	std::list<const Employee*> m_lstEmployee;  //部门的所有员工
	std::string m_strName;  //部门名称
};
/// @brief 部门管理类
class DepartmentManage
{
public:
	static DepartmentManage& GetInstance() {
		static DepartmentManage cInstance;
		return cInstance;
	}

	/// @brief 查找某个部门
	Department* GetDepartment(const std::string& strName) const {
		auto iter_find = std::find_if(m_lstDepartment.begin(), m_lstDepartment.end(),
			[strName](const std::unique_ptr<Department>& p) { return strName == p->m_strName; });

		if (m_lstDepartment.end() == iter_find)
		{
			//实际项目中此处要添加报错处理
			return nullptr;
		}
		return iter_find->get();	
	}

	Department* AddDepartment(const std::string& strName)
	{
		Department* pNew = const_cast<Department*>(GetDepartment(strName));
		if (!pNew)  //部门不存在,添加
		{
			m_lstDepartment.push_back(std::make_unique<Department>(strName));
			pNew = m_lstDepartment.back().get();
		}
		return pNew;
	}
private:
	std::list<std::unique_ptr<Department>> m_lstDepartment;
};


/**
 * @brief 职级类
*/
class Grade
{
public:
	virtual ~Grade() = default;

	/**
	 * 初始化数据
	*/
	virtual void InitData() = 0;

	/// @brief 获取薪水
	double GetSalary(double dRatio) const {
		return m_dPay_Base + m_dPay_Performance * dRatio;
	}

	/// @brief 修改数据库的权限
	bool Authority_ChangeDB() const { return m_bChangeDB; }

	std::string GetName() const { return m_strName; }

protected:
	bool m_bChangeDB{ false };
	double m_dPay_Base;   //基本工资
	double m_dPay_Performance;
	std::string m_strName;  //职级名称,作为职级的唯一标识(也可以用int标识)
};
/**
 * 职级1
*/
class Grade1 : public Grade
{
public:
	void InitData() override {
		m_bChangeDB = false;
		m_dPay_Base = 5000;
		m_dPay_Performance = 3000;
		m_strName = "P1";
	}
};
/**
 * 职级2
*/
class Grade2 : public Grade
{
public:
	void InitData() override {
		m_bChangeDB = true;
		m_dPay_Base = 8000;
		m_dPay_Performance = 5000;
		m_strName = "P2";
	}
};
/**
 *@brief 职级管理类
*/
class GradeManage
{
public:
	static GradeManage& GetInstance() {
		static GradeManage cInstance;
		return cInstance;
	}

	void InitData()
	{
		//实际项目中,此处极可能是用配置文件实现
		m_lstGrade.push_back(std::make_unique<Grade1>());
		m_lstGrade.back()->InitData();

		m_lstGrade.push_back(std::make_unique<Grade2>());
		m_lstGrade.back()->InitData();
	}

	const Grade* GetGrade(const std::string& strName) const{
		auto iter_find = std::find_if(m_lstGrade.begin(), m_lstGrade.end(),
			[strName](const std::unique_ptr<Grade>& p) {return strName == p->GetName(); });
		if (iter_find == m_lstGrade.end())
		{
			//此处需要添加报错
			return nullptr;
		}
		return (*iter_find).get();
	}

	//缺少添加、删除和修改职级的接口

private:
	std::list<std::unique_ptr<Grade>> m_lstGrade;
};


/**
 * @brief 员工类
*/
class Employee
{
public:
	Employee(const std::string& strName, const std::string& strNum):m_strName(strName), m_strNumber(strNum) {}

	/// @brief 设置职级
	bool SetGrade(const std::string& strName) {
		m_pGrade = GradeManage::GetInstance().GetGrade(strName);
		return (m_pGrade != nullptr);
	}

	/// @brief 设置部门
	bool SetDepartment(const std::string& strName) {
		Department* p = DepartmentManage::GetInstance().GetDepartment(strName);
		if (!p)  //无对应部门
			return false;
		p->AddEmpplyee(m_strNumber);
		m_pDepartment = p;
		return true;
	}

	std::string GetName() const { return m_strName; }
	std::string GetNumber() const{ return m_strNumber; }
	double GetSalary(double dRatio) {return m_pGrade->GetSalary(dRatio);}
	const Department* GetDepartment() const { return m_pDepartment; }

protected:
	friend class EmployeeManage;
	std::string m_strName;  //姓名
	std::string m_strNumber;  //员工编号(员工的唯一标志)
	const Grade* m_pGrade{ nullptr };  //等级
	const Department* m_pDepartment{ nullptr };  //所属部门
};

class EmployeeManage
{
public:
	static EmployeeManage& GetInstance() {
		static EmployeeManage cInstance;
		return cInstance;
	}

	/**
	 * @brief 添加员工
	 * @param strName 员工姓名
	 * @param strGrade 职级
	 * @param strDepartment 部门
	*/
	void AddEmployee(const std::string& strName, const std::string& strGrade, const std::string& strDepartment) {
		std::string strNum = std::to_string(++m_iCount);
		while (strNum.size() < 9)
			strNum = "0" + strNum;
		m_lstEmployee.push_back(std::make_unique<Employee>(strName, strNum));
		m_lstEmployee.back()->SetGrade(strGrade);
		m_lstEmployee.back()->SetDepartment(strDepartment);
	}

	/// @brief 查找职工
	Employee* GetEmployee(const std::string& strNum) {
		auto iter_find = std::find_if(m_lstEmployee.begin(), m_lstEmployee.end(),
			[strNum](const std::unique_ptr<Employee>& p) {return strNum == p->m_strName; });
		if (iter_find == m_lstEmployee.end())
			return nullptr;
		return iter_find->get();
	}

	const std::list<std::unique_ptr<Employee>>& GetAllEmployee() const { return m_lstEmployee; }

protected:
	EmployeeManage() = default;

private:
	std::list<std::unique_ptr<Employee>> m_lstEmployee;
	int m_iCount{ 100 };
};


void Department::AddEmpplyee(const std::string& strNum) {
	const Employee* p = EmployeeManage::GetInstance().GetEmployee(strNum);
	if (p)
		m_lstEmployee.push_back(p);
	else
	{
		//此处添加报错信息
	}
}


void InitDepartment()
{
	DepartmentManage& rManage = DepartmentManage::GetInstance();
	rManage.AddDepartment("财务部");
	rManage.AddDepartment("研发部");
	rManage.AddDepartment("综管部");
	rManage.AddDepartment("测试部");
	rManage.AddDepartment("财务部");
}

int main()
{
	using std::cout;
	InitDepartment();
	GradeManage::GetInstance().InitData();


	EmployeeManage& rManage = EmployeeManage::GetInstance();
	rManage.AddEmployee("David", "P1", "财务部");
	rManage.AddEmployee("Lucas", "P1", "测试部");
	rManage.AddEmployee("Tom", "P2", "研发部");

	auto printInfo = [&rManage](const std::string& strName, double dRatio){
		Employee* p = rManage.GetEmployee(strName);
		if (p)
		{
			cout << "姓名:" << std::setw(8) << p->GetName();
			cout << ",编号:" << p->GetNumber();
			cout << ", 所属部门:" << p->GetDepartment()->GetName();
			cout << ", 薪资:" << p->GetSalary(dRatio) << "\n";
		}
		else
		{
			std::cout << "【Error】未找到员工“" << strName << "”的信息!\n";
		}	
	};

	printInfo("David", 1.1);
	printInfo("Lucas", 0.8);
	printInfo("Tom", 0.8);
	cout << "------------------------------------------------------\n";
	rManage.GetEmployee("David")->SetGrade("P2");
	printInfo("David", 1);

	system("pause");
	return 0;
}

运行结果如下:

这段代码略上,接近300行,但不难,可以说非常简单。

首先,定义了一个部门类Department和部门管理类DepartmentManage,部门类Department中有一个list,list中存放的是当前部门的职工信息。但是需要注意,职工的创建和管理并不是由部门类实现。为什么?原因有二:1、员工的创建和管理不属于部门的职能,本着单一职能原则,所以不能放在这里;2、员工和部门并没有必然的关系,员工可以换部门。

接着便是定义职级类和职级管理类,职级类有三个:Grade、Grade1、Grade2,当然,如果仅仅是本例中的功能,职级类完全不需要搞得这么复杂,甚至不需要职级类都行。但后面的例子会对职级进行扩展,慢慢就会发现定义成类的好处了。

最后定义的是员工类和员工管理类,管理类负责员工的创建、查找等。员工类里有等级和部门,注意,这里面用的是指针。为何如此?有两个原因:1、直接用类对象的话比较耗空间;2、万一部门发生一些变化,比如部门的名称换了、领导换了,所以员工岂不是都要跟着改?

这段程序稍稍有一些设计的意味在里面,比如员工类里面存放的是部门和职级的指针而不是对象等等,但是还有许多可以修改完善的地方,大的结构已经大差不差。如果有更好的设计,欢迎交流。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值