I\'m facing with the following design problem:
TL;TD need to determine if Hero(class) can use specific object while there\'s many heroes implementations
I have
I'm thinking about using Visitor Pattern.
Something like:
Hero * h;
Item * i;
Visitor v;
// Assign some values
h.accept(v,i);
Then the visitor looks like:
visit(sword *p, warrior * w){
w.use(p);
}
visit(wand *p, wizard * w){
w.use(p);
}
visit(bow *p, archer * w){
w.use(p);
}
Any suggestions regard to this idea?
You could use dynamic_cast<>()
to determine the type of your Hero pointer, like so:
Hero *h;
Weapon * i;
// do something assign values
if(dynamic_cast<HeroSubClass> != nullptr)
h->use(i);
where HeroSubClass
is the particular subclass of Hero
that you want to check for (Warrior
, etc.). If your Hero *
is not a pointer to an object of class HeroSubClass
, dynamic_cast
will return nullptr
, if it is, it will return a HeroSubClass *
.
Alternatively, you could just check the type of the Weapon *
within use()
for each HeroSubClass, and perhaps print out something like "Warrior cannot use Staff" if it's an object of the wrong class.
You can use enum
values to define weapon and hero's specific types.
enum class HERO_TYPE{
WARRIOR,
ARCHER,
WIZARD
}
class Hero: public Entity{
public:
Hero( std::string name, Gender gender, double damage, Point2d* location, HERO_TYPE type );
~Hero();
virtual void move(int x, int y);
virtual void damage(Entity* other); // Override
virtual bool use(Potion* _potion);
virtual bool use(Weapon* _weapon) = 0;
virtual bool use(ShieldArmor* _shieldArmor) = 0;
virtual bool use(BodyArmor* _bodyArmor) = 0;
private:
std::string name;
Gender gender;
Weapon* weapon;
ShieldArmor* shield_armor;
BodyArmor* body_armor;
HERO_TYPE type; //define in subclasses.
};
and do same for weapons.
enum class WEAPON_TYPE{
SWORD,
CROSSBOW,
WAND
}
class Weapon: public Item{
public:
Weapon(double damage, Point2d* location, WEAPON_TYPE type);
virtual ~Weapon();
virtual double getDamage() const;
virtual const Point2d* getLocation() const;
virtual const std::string toString() const;
WEAPON_TYPE get_type() { return this->type; }//getter
private:
Point2d* location;
double damage;
WEAPON_TYPE type;
};
Now you can specify weapons for hero classes.
void Hero::use(Weapon *i){
if(!checkWeapon(i->get_type())) return;
//...code...
}
bool Hero::checkWeapon(WEAPON_TYPE t){
switch(this->type){
case HERO_TYPE::WARRIOR:{
if(t == WEAPON_TYPE::SWORD)
return true;
}break;
case HERO_TYPE::ARCHER:{
if(t == WEAPON_TYPE::CROSSBOW)
return true;
}break;
//..all cases..
}
return false;//no hero-weapon matching
}
I have simplified the example by removing everything that isn't necessary and generalized to the concept of Item
. A weapon is a subclass of Item, as is a potion, a wand, a flux capacitor, whatever. The use
method does whatever the Item
does to target
. A weapon will attempt to hit and damage target
. A healing potion will heal target
. A flux capacitor will either send target
back in time or zap the expletive deleted out of them with 1.21 gigawatts.
But everything is seen through the lens of Item
. The invoking classes doesn't know what the item is, does, or what it did to target
. target
doesn't even know what was used on it, they just feel the effects. Nobody knows nothin' about the other objects outside of a simple, generic interface.
class Item
{
public:
enum types
{
whole lot of types go here.
They are fairly broad categories, like knife, sword, two handed sword,
healing potion, wand, etc.
};
types getType()
{
return type;
}
virtual bool use(Entity * target) = 0;
private:
types type;
};
class Hero: public Entity{
public:
Hero(std::set<Item::type> & usable): usableItems(usable)
~Hero();
bool use(Item* item,
Entity * target)
{
// this is the magic. If the item's type is in the list of usable items,
// the item is used on the target. They exact type of item or target
// is not known. Polymorphism will take care of everything from here
if (usableItems.find(item->getType()) != usableItems.end())
{
return item->use(target);
}
return false;
}
private:
std::set<Item::type> & usableItems;
};
The point is the main classes are incredibly stupid. They simply provide a framework for much more detailed objects to do the detailed work. VorpalSword
uses generic methods inherited from Weapon
and Item
to see if it hit target
, not knowing that target
is in fact a HugeRedDragon
instance, and if it hit assigns damage and then does the specific things a VorpalSword
does like checking for lopped off limbs.
And all Hero saw was item->use(target)
.