在游戏中采用原型模式之后,它愉快地提高了我的代码的可维护性。
但是,我开始担心当实际对象变得更加细节化时, 我倾向于将相应的原型编码为更像实际对象的副本。
(注意代码中的#v.2)
class Prototype{ //aka. "fake object"
public: Vec3 position;
//bool isShootable; //(#v.2) //#X
//float delayBetweenShoot; //(#v.2)
//float hp; //(#v.2)
};
class Actual{ //aka. "actual object"
public: PhysicObject* rigidBody=nullptr;
//bool isShootable; //(#v.2) //#X
//float delayBetweenShoot; //(#v.2)
//float hp; //(#v.2)
};
int main(){ // simplify
Prototype prototype;
prototype.position = Vec3(1,2,3);
//^ end prototype creation
//v these lines are inside a function, but it simply works like this
PhysicObject* phy=new PhysicObject(prototype.position);
Actual actual;
actual.rigidBody=phy;
//actual.isShootable =prototype.isShootable; //(#v.2) #Y
//actual.delayBetweenShoot=prototype.delayBetweenShoot; //(#v.2)
//actual.hp =prototype.hp; //(#v.2)
gameLogic.add(actual); //roughly speaking
}
有两个不良信号(#v.2): -
1. Prototype
vs Actual
(#X)中的重复代码
2.繁琐的田野复制。 (#Y)
因此,我认为有些事情会出错 这种模式自然会导致新的可维护性问题。
在实际情况中,actual2
包含另一个actual1
要采用Prototype Pattern,我在另一个对应的prototype2
内使用相应的prototype1
: -
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;
};
我认为将重复部分封装到名为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)
};
然而,它可能会增加Prototype
和Actual
之间的不利凝聚力(即胶水或强关系)。因此,它可能再次导致另一个新的可维护性问题。
答案 0 :(得分:1)
您可以通过从原型中继承“实际值”来避免不必要的重复。例如:
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) {
...
}
...
}
你的直觉是正确的,因为通过将“共同”字段组合在一起,可以增加这些字段之间的耦合。在某种意义上,在耦合和代码重复之间存在权衡。由您决定什么是最适合您应用的可接受的折衷方案。
答案 1 :(得分:1)
可能使用不同类型(随后的双重簿记)并不是对该设计模式的最佳解释。
请参阅下面的方法,该方法避免了双簿保存并仍然具有基本思想的好处 - 预先配置一个样本或模板对象,然后使用该实例初始化许多其他实例。
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
实例的生命周期。