Subscribe to EdwinNameless' Fake Blog        RSS Feed
-----

Yet Another Polymorphism Example.

Icon Leave Comment
Hi.

My name's Edwin Nameless.

Well, actually, no, it's not my name. But it's a cool pseudo, really, isn't it.

I live in Ireland. But I am not Irish. English is not even my mother tongue.

And I browse a coding website on a Friday night, when everybody's having fun in the pub.

So there you go, you have it, I am not really myself here: I am a Java developer, and spend most of my time in the C++ forum, and my favourite language is Ruby. How twisted is that? Well, I'm getting tired of Java (10 years of it, that's a long time), and you can't quite say this openly. Yet. It's still quite fashionable, but it's not as heretic as 4 or 5 years ago to say that you don't really like Java. So I have been learning Ruby and C++ these last few months. Hoping to get a job in the Ruby world (if that even exists in Ireland), and hoping to satisfy this twisted fascination I have for C++.

Anyway, I was working on a personal project (nothing really concrete, just hoping to put something sensible together) in C++ to learn more about the language when I came across DIC (seems to be the place where people end up anyway); something about polymorphism that was nagging me: I couldn't get it to work. In Java, easy stuff, but in C++, I just couldn't get it to work...

I have now learnt more about C++, and yeah, I now know how to use polymorphism in C++, ta very much. So, without further ado, here is my little contribution to the beautiful world of polymorphism, inspired by this post.

#include <iostream>

struct data {
  int type;
  int life;
  int arrows;
  int experience;
  int groinks;
};

class UnitData {
public:
  UnitData(){};
  virtual ~UnitData(){};
  virtual void init(data d);
  virtual void attack() = 0;
private:
  int maxLife;
  int experience;
};

class OrcUnitData: public UnitData {
public:
  OrcUnitData(){};
  virtual ~OrcUnitData(){};
  void init(data d);
  virtual void attack();
private:
  int groinks;
};

class ElfishUnitData: public UnitData {
 public:
  ElfishUnitData(){};
  virtual ~ElfishUnitData(){};
  void init(data d);
  virtual void attack();
private:
  int arrows;
};

void UnitData::init(data d) {
  maxLife = d.life;
  experience = d.experience;
}

void OrcUnitData::init(data d) {
  UnitData::init(d);
  groinks = d.groinks;
}

void ElfishUnitData::init(data d) {
  UnitData::init(d);
  arrows = d.arrows;
}

void OrcUnitData::attack() {
  std::cout << "Orc attacking!!!!" << std::endl;
  for (int i = 0; i < groinks; i++) {
	std::cout << "Groink! ";
  }
  std::cout << std::endl;
}

void ElfishUnitData::attack() {
  std::cout << "Elf attacking!!!!" << std::endl;
  for (int i = 0; i < arrows; i++) {
	std::cout << "Swiiish! ";
  }
  std::cout << std::endl;
}

int main() {

  UnitData* units[2];

  // Read file a create struct (or whatever type) "data"
  // Go through "data" records, and create UnitData accordingly.
  data nastyOrc;
  nastyOrc.type= 1;
  nastyOrc.life = 50;
  nastyOrc.groinks = 150;
  nastyOrc.experience = 2;

  data coolElf;
  coolElf.type= 2;
  coolElf.life = 120;
  coolElf.arrows = 15;
  coolElf.experience = 12;

  data datalist[2] = { nastyOrc, coolElf };
  for (int i = 0; i < 2; i++) {
	//  Here, you'd consider using the Factory pattern
	UnitData* d;
	if (datalist[i].type == 1) {
	  d = new OrcUnitData;
	} else if (datalist[i].type == 2) {
	  d = new ElfishUnitData;
	}

	// From now on, the class type decides
	// Notice how from here on, we don't use if ... else anymore.

	d->init(datalist[i]);
	units[i] = d;
  }

  for (int i = 0; i < 2; i++) {
	units[i]->attack();
  }

  return 0;
}



Here, the struct data is really a stub for data pulled from somewhere else (a file, a db, wherever), so it doesn't matter how it's initiated. What is interesting to see is that when calling attack() in the main, you don't care whether the unit is orcish or elfish, you just call attack on the instance, and it calls the right one on the right class.

As NickDMax (and in fairness, he probably knows more than I do about C++) pointed out, you cannot avoid using if ... else when creating the instance. Of course not. You need it to call the right constructor to use the right type; however, you'd probably use a factory pattern to wrap this if ... else so that if a new type is needed, then you only need to change it in one place. The advantages of the polymorphic approach really outweigh the apparent complexity you're introducing (note the use of the word “apparent”): the behaviour for a given type is located within the type, and not spread out through the whole code base. Complexity is actually reduced, as people maintaining the code base is not faced with gazillions of if ... else littering the app and making the code litterally impossible to read: they know that whatever needs to change for an Orc is in the OrcUnitData class, and if they need to change the Orcs' behaviour (if they're a bit too nasty to the poor elves, for instance), you don't need to check all the code with the obvious risk of missing a if ... else somewhere.

0 Comments On This Entry

 

About...

This is my fake blog. 'Cause I also have a real blog with real stuff in it.

August 2014

S M T W T F S
      1 2
3456789
10111213141516
17181920212223
24252627282930
31      

Recent Entries

Recent Comments

Search My Blog

0 user(s) viewing

0 Guests
0 member(s)
0 anonymous member(s)