Programming paradigms

The Partridge Family were neither partridges nor a family. Discuss.
albinopapa
Posts: 4373
Joined: February 28th, 2013, 3:23 am
Location: Oklahoma, United States

Re: Programming paradigms

Post by albinopapa » March 11th, 2016, 12:47 am

Ok, I have a little better understanding of relationships. Composition is "has a" relationship, so yes, it's a member of an object. And to say that an object inherits from it is kind of misleading. If you want to inherit something from that component, you either have to use accessor methods such as Get or the members of the component will have to be public, like Vec2 being a component with public members because a ship 'has a' position and 'has a' velocity.

In the chili framework, Game has a graphics component, input component and a sound component. Game inherits the abilities of these components through accessor functions, like gfx.PutPixel, kbd.KeyIsPressed and sound.Play, but it isn't a child of any of those classes.

Without realizing it, chili has been teaching composition and using inheritance this whole time. If you have gone through the Intermediate lessons, where he specifically talks about inheritance he says do it when it makes sense. It wouldn't make sense really for Game to be a child of Sound, Keyboard, Mouse and D3DGraphics. They have nothing in common.

Addressing Lux's example, inheriting attributes like that is still inheritance, albeit, a clever way of precisely selecting who/what to inherit from.
If you think paging some data from disk into RAM is slow, try paging it into a simian cerebrum over a pair of optical nerves. - gameprogrammingpatterns.com

albinopapa
Posts: 4373
Joined: February 28th, 2013, 3:23 am
Location: Oklahoma, United States

Re: Programming paradigms

Post by albinopapa » March 11th, 2016, 1:32 am

Here's an example of composition as inheritance usng Lux's example.

Code: Select all

class Damage
{
public:
	Damage(float DamageValue)
		:
		val(DamageValue)
	{}
	operator float()
	{
		return val;
	}
private:
	float val;
};
class Visible
{
public:
	Visible(bool CanBeSeen)
		:
		seeable(CanBeSeen)
	{}
	operator bool()
	{
		return seeable;
	}
	
private:
	bool seeable;
};
class Moveable
{
public:
	Moveable(float xStart, float yStart)
		:
		xPosition(xStart),
		yPosition(yStart),
		xVelocity(0.0f),
		yVelocity(0.0f)
	{}
	void Update(float DeltaTime)
	{
		xPosition += (xVelocity * DeltaTime);
		yPosition += (yVelocity * DeltaTime);
	}
	void SetVelocity(float XVel, float YVel)
	{
		xVelocity = XVel;
		yVelocity = YVel;
	}
	void GetVelocity(float &XVel, float &YVel)
	{
		XVel = xVelocity;
		YVel = yVelocity;
	}
private:
	float xPosition, yPosition, xVelocity, yVelocity;
};
class Ship
{
public:
	Ship()
		:
		move(400.0f, 300.0f),
		damage(20.0f),
		visible(true),
		health(100.0f)
	{}
	void Update(float DeltaTime)
	{
		float vx, vy;
		move.GetVelocity(vx, vy);
		if (vx <= 0.0f || vy <= 0.0f)
		{
			vx += 1.0f;
			vy += 1.0f;
		}
		move.SetVelocity(vx, vy);
		move.Update(DeltaTime);
	}
	Visible IsVisible()const
	{
		return visible;
	}
	const Damage &GetDamage()const
	{
		return damage;
	}
	void TakeDamage(Damage DamageComponent)
	{
		health -= DamageComponent;
	}
private:
	Moveable move;
	Visible visible;
	Damage damage;
	float health;
};
class Ninja
{
public:
	Ninja() :
		move(400.0f, 300.0f),
		damage(20.0f),
		health(100.0f)
	{}
	void Update(float DeltaTime)
	{
		float vx, vy;
		move.GetVelocity(vx, vy);
		vx += 1.0f;
		move.SetVelocity(vx, vy);
		move.Update(DeltaTime);
	}
	const Damage &GetDamage()const
	{
		return damage;
	}
	void TakeDamage(Damage DamageComponent)
	{
		health -= DamageComponent;
	}



private:
	Moveable move;
	Damage damage;
	float health;
};

Ship player;
std::vector<Ship> enemy_ships;
Ninja ninja;

void Go()
{
	// pretend collision check is being done
	for (auto &enemy : enemy_ships)
	{
		// Ships might be cloaked so we need to check for visibilty
		if (enemy.IsVisible())
		{
			enemy.TakeDamage(player.GetDamage());
		}		
	}
	// Player can't know where ninja is attacking from because Ninja doesn't 
	// have that attribute so the player will probably take damage from Ninja
	player.TakeDamage(ninja.GetDamage());

	float dt = 1.0f / 60.0f;
	player.Update(dt);
	for (auto &enemy : enemy_ships)
	{
		enemy.Update(dt);
	}

}
Ship has all three attributes, Ninja has two of the three.
If you think paging some data from disk into RAM is slow, try paging it into a simian cerebrum over a pair of optical nerves. - gameprogrammingpatterns.com

albinopapa
Posts: 4373
Joined: February 28th, 2013, 3:23 am
Location: Oklahoma, United States

Re: Programming paradigms

Post by albinopapa » March 11th, 2016, 1:37 am

Here's an inheritance version

Code: Select all

class Damage
{
public:
	Damage(float DamageValue)
		:
		val(DamageValue)
	{}
	operator float()
	{
		return val;
	}
protected:
	float val;
};
class Visible
{
public:
	Visible(bool CanBeSeen)
		:
		seeable(CanBeSeen)
	{}
	operator bool()
	{
		return seeable;
	}
	
protected:
	bool seeable;
};
class Moveable
{
public:
	Moveable(float xStart, float yStart)
		:
		xPosition(xStart),
		yPosition(yStart),
		xVelocity(0.0f),
		yVelocity(0.0f)
	{}
	virtual void Update(float DeltaTime) = 0;
	void SetVelocity(float XVel, float YVel)
	{
		xVelocity = XVel;
		yVelocity = YVel;
	}
	void GetVelocity(float &XVel, float &YVel)
	{
		XVel = xVelocity;
		YVel = yVelocity;
	}
protected:
	float xPosition, yPosition, xVelocity, yVelocity;
};
class Ship :public Damage, public Visible, public Moveable
{
public:
	Ship()
		:
		Damage(20.0f),
		Visible(true),
		Moveable(400.0f, 300.0f),
		health(100.0f)
	{}
	void Update(float DeltaTime)
	{
		if (xVelocity<= 0.0f || yVelocity <= 0.0f)
		{
			xVelocity += 1.0f;
			yVelocity += 1.0f;
		}
		xPosition += (xVelocity * DeltaTime);
		yPosition += (yVelocity * DeltaTime);
	}
	void TakeDamage(Damage DamageComponent)
	{
		health -= DamageComponent;
	}
private:
	float health;
};
class Ninja :public Damage, public Moveable
{
public:
	Ninja() :
		Damage(20.0f),
		Moveable(400.0f, 300.0f),
		health(100.0f)
	{}
	void Update(float DeltaTime)
	{
		xVelocity += 1.0f;

		xPosition += (xVelocity * DeltaTime);
		yPosition += (yVelocity * DeltaTime);
	}
	void TakeDamage(Damage DamageComponent)
	{
		health -= DamageComponent;
	}
private:
	float health;
};
Ship player;
std::vector<Ship> enemy_ships;
Ninja ninja;
void Go()
{
	// pretend collision check is being done
	for (auto &enemy : enemy_ships)
	{
		// Ships might be cloaked so we need to check for visibilty
		if (enemy)
		{
			enemy.TakeDamage(player);
		}		
	}
	// Player can't know where ninja is attacking from because Ninja doesn't 
	// have that attribute so the player will probably take damage from Ninja
	player.TakeDamage(ninja);

	float dt = 1.0f / 60.0f;
	player.Update(dt);
	for (auto &enemy : enemy_ships)
	{
		enemy.Update(dt);
	}

}
Notice how operator bool from Visibility and operator float from Damage are inherited and now you just just do if(enemy) and the inherited operator bool will return seeable from the Visible parent and pass ninja to player.TakeDamage because inherits from Damage and health -= DamageComponent works because of the operator float Damage defines.
If you think paging some data from disk into RAM is slow, try paging it into a simian cerebrum over a pair of optical nerves. - gameprogrammingpatterns.com

albinopapa
Posts: 4373
Joined: February 28th, 2013, 3:23 am
Location: Oklahoma, United States

Re: Programming paradigms

Post by albinopapa » March 11th, 2016, 1:47 am

Both cases work, just wanted to be a little clearer and share what I have uncovered. Haven't really tried the inheritance through composition in any of my projects so I don't know how scale-able it is as of yet. The idea is: changes to implementation inside of the component shouldn't affect more than the object that uses it, where with inheritance changes to the base class could mean that any child class that also inherits those changes would affect everywhere that uses the child class including all other children of the parent class.

I think the 'is a' for inheritance and 'has a' for composition is the best guide one could use when determining which to use.
If you think paging some data from disk into RAM is slow, try paging it into a simian cerebrum over a pair of optical nerves. - gameprogrammingpatterns.com

Post Reply