C++ 原型模式会导致“之间的代码重复”;“实际对象”;及;原型;
在将原型模式应用到游戏中后,它很好地提高了代码的可维护性 然而,我开始担心,当实际对象变得更加细节化时,C++ 原型模式会导致“之间的代码重复”;“实际对象”;及;原型;,c++,maintainability,prototype-pattern,C++,Maintainability,Prototype Pattern,在将原型模式应用到游戏中后,它很好地提高了代码的可维护性 然而,我开始担心,当实际对象变得更加细节化时, 我倾向于对对应的原型进行编码,使其更像实际对象的副本 (注意#代码中的v.2) 有两个坏信号(#v.2):- 1.原型中的重复代码与实际中的重复代码(#X) 2.乏味的字段复制。(#Y) 因此,我认为有些事情开始出错。 这种模式自然会导致新的可维护性问题 在实际情况中,actual2包含另一个actual1 为了采用原型模式,我在另一个对应的原型1中使用了相应的原型2:- class Act
我倾向于对对应的原型进行编码,使其更像实际对象的副本 (注意#代码中的v.2) 有两个坏信号(#v.2):-
1.原型中的重复代码与实际中的重复代码(#X)
2.乏味的字段复制。(#Y) 因此,我认为有些事情开始出错。
这种模式自然会导致新的可维护性问题 在实际情况中,
actual2
包含另一个actual1
为了采用原型模式,我在另一个对应的
原型1
中使用了相应的原型2
:-
class Actual1{
//... some fields e.g. A1 a1; A2 a2; A3 a3;
};
class Actual2{
//... some other field e.g. B1 B2 B3
Actual1 actual1;
};
class Prototype1{
//... some fields e.g. very similar to A1 A2 A3
};
class Prototype2{
//... some other field e.g. very similar to B1 B2 B3
Prototype1 prototype1;
};
问题
- (1) 原型模式创建新的可维护性问题是否常见李>
- (2) 如果是,如何避免李>
- (3) 如果没有,我错在哪里(特别是编码风格)。。。或者这种趋势(重复代码)根本不是问题(即我只是恐慌)
Settings
的单一结构中可能是个好主意
class Settings{
bool isShootable;
float delayBetweenShoot;
float hp;
};
class Prototype{ //aka. "fake object"
public: Vec3 position;
Settings settings; //(#v.2)
};
class Actual{ //aka. "real object"
public: PhysicObject* rigidBody=nullptr;
Settings settings; //(#v.2)
};
然而,它可能会增加
原型
和实际
之间的不利内聚(即粘合或强关系)。因此,它可能会再次导致另一个新的可维护性问题。您可以通过从原型中将“实际值”子类化来避免不必要的重复。例如:
struct Prototype {
bool isShootable = false;
float delayBetweenShoot = DEFAULT_DELAY;
float hp = DEFAULT_HP;
Vec3 position = STARTING_POSITION;
...
};
struct Actual : Prototype {
PhysicObject* rigidBody;
Actual() : Prototype(), rigidBody(new PhysicObject(position)) {}
}
int main() {
Actual actual;
// now you can access these methods
if (actual.isShootable) {
...
}
...
}
你的直觉是正确的,通过将“公共”字段组合在一起,你可以增加这些字段之间的耦合。从某种意义上讲,耦合和代码复制之间存在一种折衷。由您决定什么是最适合您的应用程序的可接受折衷方案。您可以通过从原型中将“实际值”子类化来避免不必要的重复。例如:
struct Prototype {
bool isShootable = false;
float delayBetweenShoot = DEFAULT_DELAY;
float hp = DEFAULT_HP;
Vec3 position = STARTING_POSITION;
...
};
struct Actual : Prototype {
PhysicObject* rigidBody;
Actual() : Prototype(), rigidBody(new PhysicObject(position)) {}
}
int main() {
Actual actual;
// now you can access these methods
if (actual.isShootable) {
...
}
...
}
你的直觉是正确的,通过将“公共”字段组合在一起,你可以增加这些字段之间的耦合。从某种意义上讲,耦合和代码复制之间存在一种折衷。由您决定什么是最适合您的应用程序的可接受折衷方案。可能使用不同的类型(以及随后的双重簿记)并不是该设计模式的最佳解释 请参阅下面的方法,它避免了双重簿记,并且仍然保留了基本思想的优点—预先配置一次示例或模板对象,然后使用该实例初始化许多其他实例
class A {
int32_t id;
bool shootable;
bool moveable;
bool destructable;
public:
// A template instance specific constructor.
A(bool shoot, bool move, bool destruct)
: id(-1)
, shootable(shoot)
, moveable(move)
, destructable(destruct)
{
}
// One or more "real" instance constructors.
A(int32_t idval, const A& source)
: id(idval)
, shootable(source.shootable)
, moveable(source.moveable)
, destructable(source.destructable)
{
}
// ...
};
int main(int argc, const char *argv[])
{
A kind(true,false,true);
A instance0(1,kind);
A instance1(2,kind);
return 0;
}
作为上述想法的一种变体,您还可以存储对模板实例的引用,并使用2种类型
class UnitType
{
int32_t hp;
bool ranged;
//...
};
class Unit
{
int32_t id;
const UnitType *type;
// more data
public:
Unit(int32_t idval, const UnitType* unitType)
: id(idval)
, type(unitType)
{
}
//...
};
当然,
UnitType
实例不应该被实例写入。还有一次,例如,currentHp
,您需要处理另一种形式的复制。另外,您需要确保UnitType
模板实例的使用寿命超过使用它的每个Unit
实例的使用寿命。可能使用不同的类型(以及随后的双重簿记)并不是该设计模式的最佳解释
请参阅下面的方法,它避免了双重簿记,并且仍然保留了基本思想的优点—预先配置一次示例或模板对象,然后使用该实例初始化许多其他实例
class A {
int32_t id;
bool shootable;
bool moveable;
bool destructable;
public:
// A template instance specific constructor.
A(bool shoot, bool move, bool destruct)
: id(-1)
, shootable(shoot)
, moveable(move)
, destructable(destruct)
{
}
// One or more "real" instance constructors.
A(int32_t idval, const A& source)
: id(idval)
, shootable(source.shootable)
, moveable(source.moveable)
, destructable(source.destructable)
{
}
// ...
};
int main(int argc, const char *argv[])
{
A kind(true,false,true);
A instance0(1,kind);
A instance1(2,kind);
return 0;
}
作为上述想法的一种变体,您还可以存储对模板实例的引用,并使用2种类型
class UnitType
{
int32_t hp;
bool ranged;
//...
};
class Unit
{
int32_t id;
const UnitType *type;
// more data
public:
Unit(int32_t idval, const UnitType* unitType)
: id(idval)
, type(unitType)
{
}
//...
};
当然,
UnitType
实例不应该被实例写入。还有一次,例如,currentHp
,您需要处理另一种形式的复制。另外,您需要确保UnitType
模板实例的使用寿命超过使用它的每个Unit
实例的使用寿命。您能解释一下您对“原型模式”的理解吗?@Joel Cornett IMHO,“原型模式”是使用“虚拟、简化、重量轻、易于复制的对象”缓存我真正想要创建的对象的设置。我将其视为不同类和函数的可通过的复杂枚举-例如,无需创建实际的游戏对象。(如果有错,请纠正我):)也许我没有抓住重点,但我看不到这一切的好处。与实际对象有一个“模板实例”(类型相同)相比,不是这样,然后使用该实例初始化新实例,然后通过向新实例添加唯一数据来进一步配置新实例。另外,整个“原型”命名听起来有点像JavaScript程序员到C++;你能解释一下你对“原型模式”的理解吗?@Joel Cornett IMHO,“原型模式”是指使用“虚拟、简化、轻量级、易于复制对象”来缓存我真正想要创建的对象的设置。我将其视为不同类和函数的可通过的复杂枚举-例如,无需创建实际的游戏对象。(如果有错,请纠正我):)也许我没有抓住重点,但我看不到这一切的好处。与实际对象有一个“模板实例”(类型相同)相比,不是这样,然后使用该实例初始化新实例,然后通过向新实例添加唯一数据来进一步配置新实例。另外,整个“原型”命名听起来有点像JavaScript程序员到C++;感谢我对你的“权衡”一词印象深刻。这里有一点担心:我认为实际
不是原型
,例如原型
可能有位置
,但实际
没有位置
。例如,我可以允许设置