原型模式导致“实际对象”和“原型”之间的代码重复

时间:2016-12-17 03:35:32

标签: c++ maintainability prototype-pattern

在游戏中采用原型模式之后,它愉快地提高了我的代码的可维护性。

但是,我开始担心当实际对象变得更加细节化时, 我倾向于将相应的原型编码为更像实际对象的副本。

(注意代码中的#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;
};

问题

  • (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)
};

然而,它可能会增加PrototypeActual之间的不利凝聚力(即胶水或强关系)。因此,它可能再次导致另一个新的可维护性问题。

2 个答案:

答案 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实例的生命周期。