Шаблон прототипа вызывает повторение кода между «фактическим объектом» и «прототип»

После принятия Образец прототипа в игру, это приятно улучшило удобство сопровождения моего кода.

Тем не менее, я начал бояться, что когда фактический объект стать более детализированными,
Я склонен кодировать соответствующий прототип стать более похожим на копию фактический объект.

(примечание № 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 против Actual (#ИКС)
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)
};

Однако это может увеличить неблагоприятное сцепление (то есть клей или сильную связь) между Prototype а также Actual, Таким образом, это может снова привести к новой проблеме ремонтопригодности.

0

Решение

Вы можете избежать ненужного дублирования, подклассифицируя свои «фактические данные» из прототипа. Например:

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

Другие решения

Вероятно, использование разных типов (с последующей двойной бухгалтерией) — не лучшая интерпретация этого шаблона проектирования.

Ниже приведен подход, который позволяет избежать двойного учета и по-прежнему обладает преимуществами основной идеи — предварительно сконфигурировать образец или шаблон объекта, а затем использовать этот экземпляр для инициализации многих других экземпляров.

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 Например, используя его.

1

По вопросам рекламы [email protected]