c++ - 使用继承修复大型类
问题描述
在我看来,我的课太大太复杂了,我想减少它。我可以以这种方式使用继承吗,因为我创建 InitCar 类只是为了继承它并且不会显式使用此类的对象。
在重构之前。人员和许可证不是我自己的课程,我无法更改它们。
class Car
{
public:
void Move();
void SpeedUp();
void SpeedDw();
//More other
private:
int speed = 0;
std::string name;
int id = 0;
People owner; // not my own class
License license; // not my own class
void InitCarFromConfig()
{
//Here I read the data from the file
}
void InitOwner()
{
//Here I init the People owner
}
void InitInspection()3
{
//Here I init the License license
}
};
重构后
class InitCar
{
protected:
std::string name;
int id = 0;
People owner; // not my own class
License license; // not my own class
void InitCarFromConfig()
{
//Here I read the data from the file
}
void InitOwner()
{
//Here I init the People owner
}
void InitInspection()
{
//Here I init the License license
}
};
class Car : InitCar
{
public:
void Move()
{
InitOwner();
}
void SpeedUp();
void SpeedDw();
//More other
private:
int speed = 0;
};
这种继承的使用是否可以接受,是否可能存在性能问题?
解决方案
尽管确实可以使用继承来减少类的大小并减少其他类的代码重复(也可以继承InitCar
类似的基本功能),但这实际上不是一个好主意。
在功能方面,它确实有效,它确实减少了代码重复和类大小。然而,这是一个糟糕的设计,因为它使用了错误的继承,并且打破了“干净代码”的概念。
当你创建一个类时,你创建了一个代表某物的实体。继承在这些实体之间创建关系。说Car
继承InitCar
就是说这Car
是一种InitCar
,这在逻辑上没有意义,因为InitCar
它只是一个辅助类。
如果基本类型是一个实际的实体,例如Vehicle
,并且您有多个车辆,则可以解决此问题。但是,您InitClass
专门用于拆分您的代码,因此它实际上并没有生成 a Vehicle
,重命名它不会修复设计。
组合优于继承
“干净代码”中的一个众所周知的概念,即最好将帮助类的功能作为变量保存在类中,而不是从基类继承。它既可以更灵活地切换实现,又不会滥用继承的目的:
class Car {
public:
void Move();
void SpeedUp();
void SpeedDw();
//More other
private:
int speed = 0;
std::string name;
int id = 0;
People owner;
License license;
HelperClass helper; // new class for initing..
void InitCarFromConfig()
{
//data = helper.InitCarFromConfig();
}
void InitOwner()
{
//owner = helper.InitOwnerForCar(param);
}
void InitInspection()
{
//data = helper.InitInspection(param);
}
};
现在我们简单地将我们的调用委托给辅助类(它的名字只是一个存根,你应该有一个与它的作用相匹配的名字,或者可能是几个类)。所以我们确实节省了一些空间,我们没有滥用继承和类型,而且我们现在实际上在实现上具有灵活性,因为现在我们可以替换帮助器的实例并获得新的逻辑。
我们如何初始化助手?通常通过构造函数接收是最好的主意。但是如果你真的想的话,你可以在类中创建。
适合您的案例的最佳设计
但它是这里最好的设计吗?实际上没有。因为类中真正的问题是类中Init_
方法的存在。
在创建类时,重要的是当构造函数完成运行时,类被完全初始化。如果不是,我们会产生几个问题:
- 冒着使用类的风险,并且某些属性/方法未完成使用,导致错误
- 课程的复杂性增加了,使维护变得更加困难(这是您指出的问题)。
- 限制类的用户在创建时的灵活性,从而限制使用类的设计选项。例如,创建测试将是一场噩梦。
- 多线程使用的风险很大,并且更难处理
相反,我们可以做的是从构造函数接收我们需要操作的所有数据,并简单地存储它:
public:
Car(std::string name, id, People owner, License license);
现在又来了一个问题:如果难以执行初始化怎么办。毕竟你有 3 种方法来初始化你的类,所以对用户来说可能并不容易。这就是工厂设计模式的出现。我们将创建一个名为CarFactory
(或其他)的类,并使用它来创建我们的类。在其中,它将具有初始化类数据的所有逻辑:
class CarFactory {
public:
Car* CreateCar(params_from_user) {
// init data
return new Car(data);
}
};
我们用这个完成了什么:
- 我们做得
Car
更小、更简单 - 我们为用户提供了更多关于如何使用的选项
Car
- 我们保留了
Init
之前的选项,以帮助创建Car
- 我们的代码更易于查看和维护,因为它在逻辑上是分开的,并且类很小
Car
在构造函数调用后完全初始化
推荐阅读
- javascript - 1 个字母后,搜索字段将我踢出输入字段
- r - R Flexdashboard - 在标题中添加一个中断
- java - IB TWS API 在 EClientSocket 上调用 eDisconnect() 时出现 SocketException,发生在 java.io.DataInputStream.readInt(EClientSocket)
- mongodb - 为什么MongoDB支持事务?
- node.js - 节点js,当包含“include”和“where:array []”选项时,使用偏移量和限制对findAndCountAll进行Sequelize不起作用
- image - 有没有办法创建在点击或单击时翻转到另一侧(另一页)的图像?
- google-api - Google Calendar API 删除 410 错误是什么意思
- reactjs - 无法编译 React Native 应用程序
- r - 是否有任何函数可以让我将此列表转换为一组向量?
- java - 需要显式类路径的 Gradle 插件和不需要的 Gradle 插件有什么区别?