设计模式:原型模式的理解与应用
原型模式
介绍
-
性能优良:原型模式是在内存二进制流的复制,要比直接new一个对象性能好,特别是在一个循环体内产生大量的对象时,原型模式可以更好地体现其优点。
-
逃避构造函数的约束:这既是优点也是缺点,直接在内存中复制,构造函数是不会执行的,因此减少了约束,需要在实际应用时进行权衡考虑。
-
资源优化场景,类初始化需要消化非常多的资源,这个资源包括数据、硬件资源等。
-
性能和安全要求的场景,通过new产生一个对象需要非常烦琐的数据准备或访问权限。
-
有的时候,创建对象时,构造函数的参数很多,而自己又不完全的知道每个参数的意义,就可以使用原型模式来创建一个新的对象,不必去理会创建的过程。
-
当我们的对象类型不是开始就能确定的,而这个类型是在运行期确定的话,那么我们通过这个类型的对象克隆出一个新的对象比较容易一些。
-
当一个类的实例只能有几个不同状态组合中的一种时;建立相应数目的原型并克隆它们可能比每次用合适的状态手工实例化该类更方便一些。
-
我自己感觉,和拷贝构造相比,隐藏了拷贝的细节,把拷贝构造进行了封装。
理解
-
当类中没有定义任何的构造函数时,编译器会默认提供一个无参构造函数且函数体为空
-
当类中没有定义任何的拷贝构造函数时,编译器会默认提供一个拷贝构造函数,进行成员变量之间的拷贝;但是这个拷贝操作是 浅拷贝
浅拷贝(shallowCopy) :只是增加了一个指针指向已存在的内存地址深拷贝(deepCopy) :是增加了一个指针并且申请了一个新的内存,使这个增加的指针指向这个新的内存
实现
-
定义人物技能属性有:战斗力、飞行距离、防御力
-
创建人物本体,该人物可以分身,即自我复制,但是最多只能复制三个
-
复制后的分身各属性为本体的 80%,同时每次分身都会导致本体降低 10% 的属性,但不会影响之前已有的分身(分身作为独立的个体),即使本体属性提升也不会影响分身
/**
* @brief 人物技能属性类
*/
class CSkillAttributes
{
private:
int m_combatPower; // 战斗力
int m_defensePower; // 防御力
int m_fightDistance; // 分行距离
public:
/**
* @brief 构造函数
* @param[in] combatPower 战斗力值
* @param[in] defensePower 防御力值
* @param[in] fightDistance 飞行距离
* @return None
*/
CSkillAttributes (int combatPower, int defensePower, int fightDistance):
m_combatPower(combatPower),
m_defensePower(defensePower),
m_fightDistance(fightDistance)
{
}
/**
* @brief 获取战斗力
* @return 战斗力
*/
int getCombatPower()
{
return m_combatPower;
}
/**
* @brief 设置战斗力
* @param[in] combatPower 战斗力值
* @return None
*/
void setCombatPower(int combatPower)
{
m_combatPower = combatPower;
}
/**
* @brief 获取防御力
* @return 防御力
*/
int getDefensePower()
{
return m_defensePower;
}
/**
* @brief 设置防御力
* @param[in] defensePower 防御力值
* @return None
*/
void setDefensePower(int defensePower)
{
m_defensePower = defensePower;
}
/**
* @brief 获取飞行距离
* @return 飞行距离
*/
int getFightDistance()
{
return m_fightDistance;
}
/**
* @brief 设置飞行距离
* @param[in] fightDistance 飞行距离
* @return None
*/
void setFightDistance(int fightDistance)
{
m_fightDistance = fightDistance;
}
};
2、定义人物信息类,技能属性作为指针,在需要的时候分配内存,其中拷贝构造函数为私有,目前是防止越过克隆限制直接进行拷贝构造。
/**
* @brief 人物信息
*/
class CCharacter
{
private:
bool isSelf; // 是否为本体
char m_name[50]; // 人物姓名
int m_numLimit; // 分身次数
CSkillAttributes *pSkillAttributes; // 技能属性
public:
/**
* @brief 构造函数
* @param[in] name 人物
* @param[in] combatPower 战斗力值
* @param[in] defensePower 防御力值
* @param[in] fightDistance 飞行距离
* @return None
*/
CCharacter(const char *name, int combatPower, int defensePower, int fightDistance)
{
memset(m_name, 0, sizeof(m_name));
memcpy(m_name, name, strlen(name));
isSelf = true;// 标记本体
printf("\e[%d;%dm%s[%s]\e[0m\n", 1, 31, "构造人物信息", m_name);
m_numLimit = 0;
pSkillAttributes = new CSkillAttributes(combatPower, defensePower, fightDistance);
}
private:
/**
* @brief 拷贝构造函数
* @param[in] src 类
* @return None
*/
CCharacter(CCharacter &src)
{
printf("\e[%d;%dm%s\e[0m\n", 1, 32, "复制人物信息");
pSkillAttributes = new CSkillAttributes(
src.pSkillAttributes->getCombatPower() * 0.8,
src.pSkillAttributes->getDefensePower() * 0.8,
src.pSkillAttributes->getFightDistance() * 0.8);// 深度拷贝
sprintf(m_name, "%s分身%d", src.m_name, src.m_numLimit);
isSelf = false; // 标记分身
/* 本身为原来的 90% 的属性 */
src.pSkillAttributes->setCombatPower(src.pSkillAttributes->getCombatPower() * 0.9);
src.pSkillAttributes->setDefensePower(src.pSkillAttributes->getDefensePower() * 0.9);
src.pSkillAttributes->setFightDistance(src.pSkillAttributes->getFightDistance() * 0.9);
}
public:
/**
* @brief 设置技能属性
* @param[in] combatPower 战斗力值
* @param[in] defensePower 防御力值
* @param[in] fightDistance 飞行距离
* @return None
*/
void setSkillAttributes(int combatPower, int defensePower, int fightDistance)
{
pSkillAttributes->setCombatPower(combatPower);
pSkillAttributes->setDefensePower(defensePower);
pSkillAttributes->setFightDistance(fightDistance);
}
/**
* @brief 技能信息显示
* @return None
*/
void dispaly(void)
{
printf("人物: %s\n", m_name);
printf("战斗力: %d\n", pSkillAttributes->getCombatPower());
printf("防御力: %d\n", pSkillAttributes->getDefensePower());
printf("飞行距离: %d\n", pSkillAttributes->getFightDistance());
printf("\n");
}
/**
* @brief 人物克隆
* @return 克隆后的人物信息
*/
virtual CCharacter *clone()
{
if (m_numLimit < 3)
{
m_numLimit++;
printf("\e[%d;%dm%s\e[0m\n", 1, 36, "克隆人物信息");
return new CCharacter(*this);
}
else
{
return NULL;
}
}
};
3、构造并克隆人物实现
int main()
{
CCharacter *pCharacter = new CCharacter("孙悟空", 2000, 1000, 18000);
pCharacter->dispaly();
CCharacter *pArrCharacter[5] = {NULL};
for (int i = 0; i < 5; i++)
{
pArrCharacter[i] = pCharacter->clone();
if (pArrCharacter[i] != NULL)
{
pArrCharacter[i]->dispaly();
}
else
{
printf("\e[%d;%dm%s\e[0m\n", 1, 31, "分身失败");
break;
}
}
printf("--------------分身完成后---------------\n");
pCharacter->dispaly();
for (int i = 0; i < 5; i++)
{
if (pArrCharacter[i] != NULL)
{
pArrCharacter[i]->dispaly();
}
}
pCharacter->setSkillAttributes(1800, 900, 15000);
printf("--------------本体属性提升后---------------\n");
pCharacter->dispaly();
for (int i = 0; i < 5; i++)
{
if (pArrCharacter[i] != NULL)
{
pArrCharacter[i]->dispaly();
}
}
return 0;
}
4、运行结果
构造人物信息[孙悟空]
人物: 孙悟空
战斗力: 2000
防御力: 1000
飞行距离: 18000克隆人物信息
复制人物信息
人物: 孙悟空分身1
战斗力: 1600
防御力: 800
飞行距离: 14400克隆人物信息
复制人物信息
人物: 孙悟空分身2
战斗力: 1440
防御力: 720
飞行距离: 12960克隆人物信息
复制人物信息
人物: 孙悟空分身3
战斗力: 1296
防御力: 648
飞行距离: 11664分身失败
--------------分身完成后---------------
人物: 孙悟空
战斗力: 1458
防御力: 729
飞行距离: 13122人物: 孙悟空分身1
战斗力: 1600
防御力: 800
飞行距离: 14400人物: 孙悟空分身2
战斗力: 1440
防御力: 720
飞行距离: 12960人物: 孙悟空分身3
战斗力: 1296
防御力: 648
飞行距离: 11664--------------本体属性提升后---------------
人物: 孙悟空
战斗力: 1800
防御力: 900
飞行距离: 15000人物: 孙悟空分身1
战斗力: 1600
防御力: 800
飞行距离: 14400人物: 孙悟空分身2
战斗力: 1440
防御力: 720
飞行距离: 12960人物: 孙悟空分身3
战斗力: 1296
防御力: 648
飞行距离: 11664
5、从运行结果来看,受功能点 3 的影响,经过最大次数的分身后,分身的属性个不一致,甚至孙悟空的技能属性比第一个分身还低(完全能被分身打败替换啊,哈哈哈),因此需要调整功能点3的要求:
复制后的分身各属性为本体的 80%,同时每次分身都会导致本体降低 10% 的属性,本体的属性降低会影响所有分身,即无论本体属性是多少,分身的属性均为本体当前属性的 80%
/**
* @brief 拷贝构造函数
* @param[in] src 类
* @return None
*/
CCharacter(CCharacter &src)
{
printf("\e[%d;%dm%s\e[0m\n", 1, 32, "复制人物信息");
pSkillAttributes = src.pSkillAttributes; // 浅拷贝
sprintf(m_name, "%s分身%d", src.m_name, src.m_numLimit);
isSelf = false; // 标记分身
/* 本身为原来的 90% 的属性 */
src.pSkillAttributes->setCombatPower(src.pSkillAttributes->getCombatPower() * 0.9);
src.pSkillAttributes->setDefensePower(src.pSkillAttributes->getDefensePower() * 0.9);
src.pSkillAttributes->setFightDistance(src.pSkillAttributes->getFightDistance() * 0.9);
}
/**
* @brief 设置技能属性
* @param[in] combatPower 战斗力值
* @param[in] defensePower 防御力值
* @param[in] fightDistance 飞行距离
* @return None
*/
void setSkillAttributes(int combatPower, int defensePower, int fightDistance)
{
if (isSelf) // 只有本体才能改变属性
{
pSkillAttributes->setCombatPower(combatPower);
pSkillAttributes->setDefensePower(defensePower);
pSkillAttributes->setFightDistance(fightDistance);
}
}
/**
* @brief 技能信息显示
* @return None
*/
void dispaly(void)
{
int combatPower = pSkillAttributes->getCombatPower(); // 战斗力
int defensePower = pSkillAttributes->getDefensePower(); // 防御力
int fightDistance = pSkillAttributes->getFightDistance(); // 分行距离
if (!isSelf)
{
/* 分身的属性为本体的 80% */
combatPower *= 0.8;
defensePower *= 0.8;
fightDistance *= 0.8;
}
printf("人物: %s\n", m_name);
printf("战斗力: %d\n", combatPower);
printf("防御力: %d\n", defensePower);
printf("飞行距离: %d\n", fightDistance);
printf("\n");
}
6、运行结果,从结果来看,孙悟空的分身属性都一致,且均比本体属性要低
构造人物信息[孙悟空]
人物: 孙悟空
战斗力: 2000
防御力: 1000
飞行距离: 18000克隆人物信息
复制人物信息
人物: 孙悟空分身1
战斗力: 1440
防御力: 720
飞行距离: 12960克隆人物信息
复制人物信息
人物: 孙悟空分身2
战斗力: 1296
防御力: 648
飞行距离: 11664克隆人物信息
复制人物信息
人物: 孙悟空分身3
战斗力: 1166
防御力: 583
飞行距离: 10497分身失败
--------------分身完成后---------------
人物: 孙悟空
战斗力: 1458
防御力: 729
飞行距离: 13122人物: 孙悟空分身1
战斗力: 1166
防御力: 583
飞行距离: 10497人物: 孙悟空分身2
战斗力: 1166
防御力: 583
飞行距离: 10497人物: 孙悟空分身3
战斗力: 1166
防御力: 583
飞行距离: 10497--------------本体属性提升后---------------
人物: 孙悟空
战斗力: 1800
防御力: 900
飞行距离: 15000人物: 孙悟空分身1
战斗力: 1440
防御力: 720
飞行距离: 12000人物: 孙悟空分身2
战斗力: 1440
防御力: 720
飞行距离: 12000人物: 孙悟空分身3
战斗力: 1440
防御力: 720
飞行距离: 12000
疑问
1、对于拷贝构造函数而言,可以不定义该函数,使用C++类默认特殊的拷贝构造函数(浅拷贝),不定义拷贝构造函数也能实现上述第五点功能变更的要求,那为啥还需要定义拷贝构造函数呢?
定义拷贝构造函数可以实现默认拷贝构造函数不具备的功能,如分身降低本体 10% 的属性功能,当然这个功能可以在main函数中重新实现;这属于功能划分不明确,没有隐藏实现细节等
2、通过以下代码也能实现人物分身功能,为什么需要定义克隆函数(virtual CCharacter *clone())呢?
int main()
{
CCharacter *pCharacter = new CCharacter("孙悟空", 2000, 1000, 18000);
pCharacter->dispaly();
CCharacter arrCharacter[3];
for (int i = 0; i < 3; i++)
{
arrCharacter[i] = *pCharacter;
arrCharacter[i].dispaly();
}
...
return 0;
}
这属于功能划分不明确,定义克隆函数的目的也是为了隐藏实现细节,如三次分身限制等,减少克隆分身的复杂性
/**
* @brief 设置技能属性
* @param[in] combatPower 战斗力值
* @param[in] defensePower 防御力值
* @param[in] fightDistance 飞行距离
* @return None
*/
void setSkillAttributes(int combatPower, int defensePower, int fightDistance)
{
if (isSelf) // 只有本体才能改变属性
{
pSkillAttributes->setCombatPower(combatPower);
pSkillAttributes->setDefensePower(defensePower);
pSkillAttributes->setFightDistance(fightDistance);
}
}
由于这是浅拷贝,若不加限制,则通过克隆分身也能修改本体的属性,而分身和本体属性不同,若是权限放开,则会引起不必要的误解
本文来自博客园,作者:大橙子疯,转载请注明原文链接:https://www.cnblogs.com/const-zpc/p/16364437.html