@~~D4RR3N~~ I'll need a different example (something practical, real world) where you're actually extending some class. A single class/object is fine like you have there.

Printable View
@~~D4RR3N~~ I'll need a different example (something practical, real world) where you're actually extending some class. A single class/object is fine like you have there.
Isn't a monster extending an enemy class a good example for inheritance?
Instagib doesn't originally inherit from Shock Rifle?
Yes @~~D4RR3N~~ and @UT-Sniper-SJA94, both good examples of inheritance.
So let's consider the SuperShockRifle. It's an extension of the ShockRifle, which extends TournamentWeapon, which extends Weapon, which extends Inventory, which extends Actor, which extends Object. And so you have all these methods and properties (hundreds!) which are inherited through each class, and at least a handful of `Super` calls within some method to call the same method within the parent class. It takes way too much effort to form a mental model of everything happening there. You're constantly going back and forth from class to class and tracing the code backwards, forwards, sideways, etc.
Now let's consider the SuperShockRifle as a simple object (or module) which doesn't inherit anything. Instead it would import a handful of other modules to produce the desired functionality. Each module should do one simple, specific, predictable thing which always works exactly as intended. So instead of going back and forth repeatedly between the SuperShockRifle's ancestors, you have something short and sweet and very flat/straightforward. All you would need to know is what kind of inputs the imported modules expect for their exported methods. And you know these other modules will always work as intended since nothing within them can be overridden.
Beyond that, composition rather than inheritance has the added benefit of maximizing code reuse and minimizing bugs. If you've ever looked at the source behind all of UT's weapons, you'd notice there's actually a ton of duplicate code. In many weapons, you'll see code in one weapon that almost exactly matches some code in another weapon, despite each weapon extending TournamentWeapon (and so on). This is usually a sign of doing something wrong. Plus, if you realize something needs to be changed within said common code, you'd have to update many different files rather than just one. (I ran into this many times with NewNet and it got rather annoying.) Of course you can have special objects/classes containing static methods for common functionality, and that's a step in the right direction, but you'll run into some pretty silly issues when you try to mix and match paradigms like that.
I'd be glad to put together some pseudo-code as an example for what I'm talking about if anyone is curious.
Doesn't it take more effort to be writing specific logic for each individual?
If you could, please post the pseudo-code, cuz I think I'm getting the idea.
If you want methods inherited to act different wouldn't you use virtual methods, and override?
.
Here, some sample polymorphism pseudocode.
Much easier when you got a class tree, if the tree is properly structured, you can save up lots of code.
Code:class Animal
{
//Variables
int Age;
Animal* Parent; //Directly created from (doesn't count second parent)
//Default constructor
Animal( Animal* InParent=NULL)
: Age(0)
, Parent(InParent)
{};
//Functions
bool WasMadeBy( Animal* Other)
{
return Other == Parent;
}
//Virtual functions (can be overriden in child classes)
virtual bool SpeciesRequiresQueen()
{
return false;
}
virtual bool IsChildOf( Animal* Other)
{
return WasMadeBy( Other);
}
};
Code:class Insect : public Animal
{
int Legs; //LOL
};
class Ant : public Insect
{
//Variables
bool bQueen;
//Constructor
Ant( Ant* InParent=NULL )
: Animal( InParent) //Call super constructor
, bQueen(false)
, Legs(6)
{
if ( !InParent->bQueen ) //Parent not a queen
Error(); //Shut down program, unexpected behaviour
}
//Functions
void BecomeQueen()
{
bQueen = true;
}
//Animal interface (virtual function override)
bool SpeciesRequiresQueen()
{
return true;
}
};
Code:class Mammal : public Animal
{
Mammal* Parent2;
//Constructor
Mammal( Mammal* InParent=NULL, Mammal* InParent2=NULL)
: Animal( InParent)
, Parent2(InParent2)
{};
//Animal interface (virtual function override)
bool IsChildOf( Animal* Other)
{
return WasMadeBy( Other) || Parent2 == Other;
}
}
Now I get it... I'm really used to reading Higor's codes...