我最近对(重新)学习编程感兴趣,所以我已经学习了C ++,因为它是一种常用的语言。但是,我遇到了障碍,我怀疑我的解决方案是否是最好的解决方法。 我有一个相对复杂的类(对我来说无论如何),有大约20个变量,为了简化,它们被分成4组。它还有一个在对象初始化期间调用的父类。
但是,我不需要将它们设置为所有对象中的默认值以外的值,因此我设置了各种不同的构造函数重载以考虑所有可能的组合(总共8个构造函数)。因此,为了防止编写重复代码,我编写了一些私有函数,只在构造函数中调用,它们将变量设置为创建新对象时赋值的值。
这是解决此问题的最佳方法吗?我还想过将这些变量分组到类或结构中,但只是觉得这样不必要地复杂,在各种构造函数重载期间调用相关函数时应该这样做。如果这不是最佳的,那么解决这个问题的最佳方法是什么?为什么?
我可以提供有关我的问题的更详细的描述,但它是一个相当大的文本墙(我首先写了一个,但它已经太过分了。)提前感谢您的意见。
根据要求,这是类定义(武器)。父类(Item)已经定义并按预期工作,因此我不会粘贴它,因此人们不必阅读大量文本。
武器类定义:
class Weapon: public Item {
public:
// Default constructor
Weapon();
// Full constructor
Weapon(unsigned GenericID, bool NameFlag, double EquipLoad, double EquipLoadperAmmo, unsigned short ModesNo, Mode* pModes, unsigned short CooldownType, double CooldownDuration, unsigned short CooldownShot, double CooldownPeriod, unsigned short ReloadType, unsigned short ReloadStyle, double ReloadTime, unsigned short MaxMagazine, unsigned short MaxAmmunition, unsigned short StartEnergy, unsigned short MaxEnergy);
// Constructor for Weapons without Cooldown System
Weapon(unsigned GenericID, bool NameFlag, double EquipLoad, double EquipLoadperAmmo, unsigned short ModesNo, Mode* pModes, unsigned short ReloadType, unsigned short ReloadStyle, double ReloadTime, unsigned short MaxMagazine, unsigned short MaxAmmunition, unsigned short CurrentEnergy, unsigned short MaxEnergy);
// Constructor for Weapons without Reload System
Weapon(unsigned GenericID, bool NameFlag, double EquipLoad, double EquipLoadperAmmo, unsigned short ModesNo, Mode* pModes, unsigned short CooldownType, double CooldownDuration, unsigned short CooldownShot, double CooldownPeriod, unsigned short MaxMagazine, unsigned short MaxAmmunition, unsigned short CurrentEnergy, unsigned short MaxEnergy);
// Constuctor for Weapons without Energy System
Weapon(unsigned GenericID, bool NameFlag, double EquipLoad, double EquipLoadperAmmo, unsigned short ModesNo, Mode* pModes, unsigned short CooldownType, double CooldownDuration, unsigned short CooldownShot, double CooldownPeriod, unsigned short ReloadType, unsigned short ReloadStyle, double ReloadTime, unsigned short MaxMagazine, unsigned short MaxAmmunition);
// Constructor for Weapons without Cooldown nor Reload System
Weapon(unsigned GenericID, bool NameFlag, double EquipLoad, double EquipLoadperAmmo, unsigned short ModesNo, Mode* pModes, unsigned short MaxMagazine, unsigned short MaxAmmunition, unsigned short CurrentEnergy, unsigned short MaxEnergy);
// Constructor for Weapons without Cooldown nor Energy System
Weapon(unsigned GenericID, bool NameFlag, double EquipLoad, double EquipLoadperAmmo, unsigned short ModesNo, Mode* pModes, unsigned short ReloadType, unsigned short ReloadStyle, double ReloadTime, unsigned short MaxMagazine, unsigned short MaxAmmunition);
// Constructor for Weapons without Reload nor Energy System
Weapon(unsigned GenericID, bool NameFlag, double EquipLoad, double EquipLoadperAmmo, unsigned short ModesNo, Mode* pModes, unsigned short CooldownType, double CooldownDuration, unsigned short CooldownShot, double CooldownPeriod, unsigned short MaxMagazine, unsigned short MaxAmmunition);
// Constructor for Weapons without Cooldown, Reload nor Energy System
Weapon(unsigned GenericID, bool NameFlag, double EquipLoad, double EquipLoadperAmmo, unsigned short ModesNo, Mode* pModes, unsigned short maxMagazine, unsigned short MaxAmmunition);
~Weapon();
void m_print();
/*Edited public get and set functions for each variable as they are not relevant*/
private:
// Ubiquitous variables
unsigned short WepGenericID = 0;
unsigned short WepVariantID = 0;
unsigned short WepSkinID = 0;
double EquipLoad = 0;
double EquipLoadperAmmo = 0;
unsigned short ModesNo = 1;
Mode* pModes = NULL;
unsigned short MaxAmmunition = 0;
unsigned short CurrentAmmunition = 0;
unsigned short MaxMagazine = 0;
unsigned short CurrentMagazine = 0;
// Cooldown System variables
bool WeaponCooldown = false;
unsigned short CooldownType = 0;
double CooldownDuration = 0;
unsigned short CooldownAction = 0;
double CooldownPeriod = 0;
// Reload System variables
unsigned short ReloadType = 0;
unsigned short ReloadStyle = 0;
double ReloadTime = 0;
// Energy System variables
unsigned short CurrentEnergy = 0;
unsigned short MaxEnergy = 0;
//Constructor Auxiliary Functions
void m_setGeneralWeapon(double EquipLoad, double EquipLoadperAmmo, unsigned short ModesNo, Mode* pModes, unsigned short MaxMagazine, unsigned short MaxAmmunition);
void m_setCooldownSystem(unsigned short CooldownType, double CooldownDuration, unsigned short CooldownAction, double CooldownPeriod);
void m_setReloadSystem(unsigned short ReloadType, unsigned short ReloadStyle, double ReloadTime);
void m_setEnergySystem(unsigned short StartEnergy, unsigned short MaxEnergy);
void m_setWeaponIDs();
void m_WepNameDecisionTree();
string m_searchName();
};
项目父类定义
class Item {
public:
Item();
Item(unsigned GenericID);
Item(unsigned GenericID, bool NameFlag);
~Item();
void m_setCustomName();
private:
unsigned GenericID = 0;
unsigned short GenCategoryID = 0;
unsigned short GenSubCategoryID = 0;
bool NameFlag = false;
string ItemName = "Missingno";
unsigned long InstanceID = 0;
};
答案 0 :(得分:2)
为子系统制作单独的类。
使用构建器/工厂模式创建武器:
你也可以分开弹药,只留下几个真正的成员。通过这种方式,您可以使用更模块化的方法构建所有内容,从而使您可以更轻松地扩展或修改功能
答案 1 :(得分:1)
我发现Weapon
API发布的一个问题是构造函数采用了松散类型的 lot 参数,这使得使用它们的代码难以理解和验证。例如,假设您(或您的开发人员)已使用构造函数API将此行添加到您的代码库中:
Weapon bfg(id, true, 3.0, 5.0, 6, modesPtr, COOLDOWN_QUICK, 5.0, 3, 4, RELOAD_SLOW, RELOAD_ANYTIME, 3.8, 12, 14);
阅读这一行,很难看出大多数数字的含义。如果一个参数被意外省略,或者两个参数的顺序被颠倒了,那么(或者你或编译器)可能不会明显存在错误;相反,在某些游戏测试人员(或客户?)提交错误报告之前,您可能不会发现错误,这是一种发现错误的昂贵且耗时的方法。
因此,我建议将你必须传递给构造函数的参数数量减少到尽可能少的数量。例如,编写上述内容的另一种方法可能是:
Weapon bfg(id, true);
bfg.SetEquipLoad(3.0);
bfg.SetEquipLoadPerAmmo(5.0);
bfg.SetModes(6, modesPtr);
bfg.SetCooldownType(COOLDOWN_QUICK);
[...]
不可否认,它更冗长(并且它确实可以忘记设置你应该设置的东西),但至少当你看到它时,很明显3.0
适用于{{ 1}}设置和EquipLoad
适用于5.0
设置,而不是相反。即,您不必经常在.h文件和代码之间来回查看,以试图找出每个值所指的内容。
请注意,每个set-method都应该获取产生有用结果所需的所有参数;例如,如果在没有指定EquipLoadPerAmmo
的情况下指定EquipLoad
没有任何意义,那么您也可以通过一次调用来设置这两者:
EquipLoadPerAmmo
...因此编码器错误地设置一个而忽略设置另一个则变得不可能(即编译时错误)。
至于处理冗长以最小化冗余代码,下一步是将上面的代码包装在一个函数中,这样对于任何给定的武器类型,只有一个创建它的地方,例如:
bfg.SetEquipLoadAndEquipLoadPerAmmo(3.0, 5.0);
现在,只要想要创建新的BFG枪,其余代码就可以调用Weapon MakeBFG(unsigned id)
{
Weapon bfg(id, true);
bfg.SetEquipLoad(3.0);
bfg.SetEquipLoadPerAmmo(5.0);
bfg.SetModes(6, modesPtr);
bfg.SetCooldownType(COOLDOWN_QUICK);
[...]
return bfg;
}
。
除此之外,我同意另一张海报 - 你的班级似乎正在处理许多不同的事情,如果你能找到一种方法将它分解成多个较小的类(并非所有这些都需要暴露)通过公共API;私有类很棒),这可能会帮助您管理代码的整体复杂性;如果您认为将来要继续添加新功能/行为,那么这样做尤其有用,因为如果您将所有内容保存在一个类中,那么当您尝试制作时,该类的复杂性将很快失控。它支持越来越多不同的行为/用例。