Press "Enter" to skip to content

Composition Over Inheritance Design Pattern in C#

Last updated on June 22, 2022

This post will demonstrate the difference between using inheritance and using composition.

 

NOTE: The implementation of composition in this example is extremely simple. In a real project, you might want to use the Strategy Pattern, the Command pattern, or another technique, to implement the different behavior.

 

 

What is “prefer composition over inheritance”?

Sometimes, a project needs several classes that appear to be similar, but have different behavior.

In object-oriented programming, we will often handle this with inheritance. We create a base class. Then, we create sub-classes that inherit from the base class, and have the properties and functions that are unique to the sub-class.

But, that can sometimes lead to messy code.

An alternative is to use “composition”, to have a single class that can be “composed” to handle the different behaviors.

 

Why you would want to use it

If you were writing a game, you might have different types of monsters. The different types of monsters could be able to do different attacks – biting, kicking, or punching.

One way to build this would be to create a base Monster class, and create sub-classes for BitingMonster, KickingMonster, and PunchingMonster – with each sub-class handling the details for the different way of fighting.

But, what do you do if you have a new monster that needs to bite and kick?

Using inheritance, you might create a BitingKickingMonster. That could inherit from Monster, BitingMonster, or KickingMonster.

 

Inheritance versions

Let’s pretend you work at a game programming company.

You’re building a new game, and you create a Monster class, with two properties – HitPoints and AttackDamage. The code might look like this:

namespace Engine.CompositionOverInheritance.Inheritance_BEFORE
{
    public class Monster
    {
        public int HitPoints { get; set; }
        public int AttackDamage { get; set; }
        public Monster(int hitPoints, int attackDamage)
        {
            HitPoints = hitPoints;
            AttackDamage = attackDamage;
        }
    }
}

 

Then, your boss tells you the game needs to have different types of monsters. One type will attack by biting, the second by kicking, and the third by punching. So, you make the Monster class into a base class, and create three new sub-classes from it: BitingMonster, KickingMonster, and PunchingMonster.

Now, you have these classes:

Monster.cs

namespace Engine.CompositionOverInheritance.Inheritance_AFTER
{
    public class Monster
    {
        public int HitPoints { get; set; }
        public Monster(int hitPoints)
        {
            HitPoints = hitPoints;
        }
    }
}

 

BitingMonster.cs

namespace Engine.CompositionOverInheritance.Inheritance_AFTER
{
    public class BitingMonster : Monster
    {
        public int BiteDamage { get; set; }
        public BitingMonster(int hitPoints, int biteDamage)
            : base(hitPoints)
        {
            BiteDamage = biteDamage;
        }
    }
}

 

KickingMonster.cs

namespace Engine.CompositionOverInheritance.Inheritance_AFTER
{
    public class KickingMonster : Monster
    {
        public int KickDamage { get; set; }
        public KickingMonster(int hitPoints, int kickDamage)
            : base(hitPoints)
        {
            KickDamage = kickDamage;
        }
    }
}

 

PunchingMonster.cs

namespace Engine.CompositionOverInheritance.Inheritance_AFTER
{
    public class PunchingMonster : Monster
    {
        public int PunchDamage { get; set; }
        public PunchingMonster(int hitPoints, int punchDamage)
            : base(hitPoints)
        {
            PunchDamage = punchDamage;
        }
    }
}

 

The next day, your boss tells you they need new types of monsters in the game – ones that can do different combinations of biting, kicking, and punching. Because this is getting complex, you also build a Factory class, to create the different types of monster objects.

If you continue with making sub-classes, you could end up with this code:

 

Monster.cs

namespace Engine.CompositionOverInheritance.Inheritance_AFTER
{
    public class Monster
    {
        public int HitPoints { get; set; }
        public Monster(int hitPoints)
        {
            HitPoints = hitPoints;
        }
    }
}

 

BitingKickingMonster.cs

namespace Engine.CompositionOverInheritance.Inheritance_AFTER
{
    public class BitingKickingMonster : BitingMonster
    {
        public int KickDamage { get; set; }
        public BitingKickingMonster(int hitPoints, int biteDamage, int kickDamage)
            : base(hitPoints, biteDamage)
        {
            KickDamage = kickDamage;
        }
    }
}

 

BitingKickingPunchingMonster.cs

namespace Engine.CompositionOverInheritance.Inheritance_AFTER
{
    public class BitingKickingPunchingMonster : BitingMonster
    {
        public int KickDamage { get; set; }
        public int PunchDamage { get; set; }
        public BitingKickingPunchingMonster(int hitPoints, int biteDamage, int kickDamage, int punchDamage)
            : base(hitPoints, biteDamage)
        {
            KickDamage = kickDamage;
            PunchDamage = punchDamage;
        }
    }
}

 

BitingMonster.cs

namespace Engine.CompositionOverInheritance.Inheritance_AFTER
{
    public class BitingMonster : Monster
    {
        public int BiteDamage { get; set; }
        public BitingMonster(int hitPoints, int biteDamage)
            : base(hitPoints)
        {
            BiteDamage = biteDamage;
        }
    }
}

 

BitingPunchingMonster.cs

namespace Engine.CompositionOverInheritance.Inheritance_AFTER
{
    public class BitingPunchingMonster : BitingMonster
    {
        public int PunchDamage { get; set; }
        public BitingPunchingMonster(int hitPoints, int biteDamage, int punchDamage)
            : base(hitPoints, biteDamage)
        {
            PunchDamage = punchDamage;
        }
    }
}

 

KickingMonster.cs

namespace Engine.CompositionOverInheritance.Inheritance_AFTER
{
    public class KickingMonster : Monster
    {
        public int KickDamage { get; set; }
        public KickingMonster(int hitPoints, int kickDamage)
            : base(hitPoints)
        {
            KickDamage = kickDamage;
        }
    }
}

 

KickingPunchingMonster.cs

namespace Engine.CompositionOverInheritance.Inheritance_AFTER
{
    public class KickingPunchingMonster : KickingMonster
    {
        public int PunchDamage { get; set; }
        public KickingPunchingMonster(int hitPoints, int kickDamage, int punchDamage)
            : base(hitPoints, kickDamage)
        {
            PunchDamage = punchDamage;
        }
    }
}

 

PunchingMonster.cs

namespace Engine.CompositionOverInheritance.Inheritance_AFTER
{
    public class PunchingMonster : Monster
    {
        public int PunchDamage { get; set; }
        public PunchingMonster(int hitPoints, int punchDamage)
            : base(hitPoints)
        {
            PunchDamage = punchDamage;
        }
    }
}

 

MonsterFactory.cs

using System;
namespace Engine.CompositionOverInheritance.Inheritance_AFTER
{
    public class MonsterFactory
    {
        public enum MonsterType
        {
            Horse, // BitingKickingMonster
            Orc, // BitingKickingPunchingMonster
            Crocodile, // BitingMonster
            MikeTyson, // BitingPunchingMonster
            Camel, // KickingMonster
            Kangaroo, // KickingPunchingMonster
            MantisShrimp //PunchingMonster
        }
        public static Monster CreateMonster(MonsterType monsterType)
        {
            switch(monsterType)
            {
                case MonsterType.Horse:
                    return new BitingKickingMonster(10, 5, 5);
                case MonsterType.Orc:
                    return new BitingKickingPunchingMonster(10, 5, 5, 5);
                case MonsterType.Crocodile:
                    return new BitingMonster(10, 5);
                case MonsterType.MikeTyson:
                    return new BitingPunchingMonster(10, 5, 5);
                case MonsterType.Camel:
                    return new KickingMonster(10, 5);
                case MonsterType.Kangaroo:
                    return new KickingPunchingMonster(10, 5, 5);
                case MonsterType.MantisShrimp:
                    return new PunchingMonster(10, 5);
                default:
                    throw new ArgumentException();
            }
        }
    }
}

If you use a factory class to instantiate objects, it might look like this – instantiating the objects with the required sub-class.

 

TestMonsterFactory.cs

using Engine.CompositionOverInheritance.Inheritance_AFTER;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace TestEngine.CompositionOverInheritance.Inheritance
{
    [TestClass]
    public class TestMonsterFactory
    {
        [TestMethod]
        public void Test_BitingMonster()
        {
            Monster crocodile = MonsterFactory.CreateMonster(MonsterFactory.MonsterType.Crocodile);
            Assert.IsTrue(crocodile is BitingMonster);
        }
        [TestMethod]
        public void Test_BitingKickingMonster()
        {
            Monster horse = MonsterFactory.CreateMonster(MonsterFactory.MonsterType.Horse);
            Assert.IsTrue(horse is BitingMonster);
            // This test will fail, because we cannot inherit from multiple base classes.
            Assert.IsTrue(horse is KickingMonster);
        }
    }
}

 

Notice that we run into a problem when a new class needs to inherit from more than one base class. For example, the BitingKickingMonster inherits from BitingMonster, and duplicates the KickDamage property – because it could not also inherit from KickingMonster.

If these classes were in a real program, and not a simple demonstration, there would probably be much more duplication of properties and functions.

There is another problem. If we try to determine the “type” of an object, to determine what attacks it can perform, we can only check against the single base class – not the second class.

 

After you finish creating all these classes, your boss sends you an email. Now, you need to have monsters that can also attack by spitting.

Cobras will bite and spit, camels will kick and spit, etc.

Instead of creating more sub-classes, you decide to try using composition.

 

Composition version

The definition of “Composition” is, “combining parts or elements to form a whole”.

In this case, we will have a single Monster class, and “compose” it with the appropriate attack behavior for each type of monster.

 

Monster.cs

using System;
using System.Collections.Generic;
namespace Engine.CompositionOverInheritance.Composition_BEFORE
{
    public class Monster
    {
        public enum AttackType
        {
            Biting,
            Kicking,
            Punching
        }
        public int HitPoints { get; set; }
        public Dictionary<AttackType, int> AttackTypes { get; set; }
        // These replace the functionality of checking an object's "type",
        // to see if it "is" a certain datatype (KickingMonster, BitingMonster, etc.)
        public bool CanBite => AttackTypes.ContainsKey(AttackType.Biting);
        public bool CanKick => AttackTypes.ContainsKey(AttackType.Kicking);
        public bool CanPunch => AttackTypes.ContainsKey(AttackType.Punching);
        public int BiteDamage
        {
            get
            {
                if(CanBite)
                {
                    return AttackTypes[AttackType.Biting];
                }
                throw new NotSupportedException("This monster cannot bite.");
            }
        }
        public int KickDamage
        {
            get
            {
                if(CanKick)
                {
                    return AttackTypes[AttackType.Kicking];
                }
                throw new NotSupportedException("This monster cannot kick.");
            }
        }
        public int PunchDamage
        {
            get
            {
                if(CanPunch)
                {
                    return AttackTypes[AttackType.Punching];
                }
                throw new NotSupportedException("This monster cannot punch.");
            }
        }
        public Monster(int hitPoints)
        {
            HitPoints = hitPoints;
            AttackTypes = new Dictionary<AttackType, int>();
        }
        public void AddAttackType(AttackType attackType, int amountOfDamage)
        {
            AttackTypes[attackType] = amountOfDamage;
        }
    }
}

 

With this Monster class, when we create a new Monster object, we “compose” its Attack options by calling the AddAttackType() function – with the AttackType, and the amount of damage the monster does with this attack.

I added a few more properties (CanBite, CanKick, and CanPunch), to make it easy to know what types of attacks a monster can perform.

This one class also has all the Damage properties in it (BiteDamage, KickDamage, and PunchDamage).

With these six properties, we can compose the Monster object to attack however we want – which we do in the MonsterFactory class below.

 

MonsterFactory.cs

using System;
namespace Engine.CompositionOverInheritance.Composition_BEFORE
{
    public class MonsterFactory
    {
        public enum MonsterType
        {
            Horse, // BitingKickingMonster
            Orc, // BitingKickingPunchingMonster
            Crocodile, // BitingMonster
            MikeTyson, // BitingPunchingMonster
            Camel, // KickingMonster
            Kangaroo, // KickingPunchingMonster
            MantisShrimp //PunchingMonster
        }
        public static Monster CreateMonster(MonsterType monsterType)
        {
            Monster monster;
            switch(monsterType)
            {
                case MonsterType.Horse:
                    monster = new Monster(10);
                    monster.AddAttackType(Monster.AttackType.Biting, 5);
                    monster.AddAttackType(Monster.AttackType.Kicking, 5);
                    break;
                case MonsterType.Orc:
                    monster = new Monster(10);
                    monster.AddAttackType(Monster.AttackType.Biting, 5);
                    monster.AddAttackType(Monster.AttackType.Kicking, 5);
                    monster.AddAttackType(Monster.AttackType.Punching, 5);
                    break;
                case MonsterType.Crocodile:
                    monster = new Monster(10);
                    monster.AddAttackType(Monster.AttackType.Biting, 5);
                    break;
                case MonsterType.MikeTyson:
                    monster = new Monster(10);
                    monster.AddAttackType(Monster.AttackType.Biting, 5);
                    monster.AddAttackType(Monster.AttackType.Punching, 5);
                    break;
                case MonsterType.Camel:
                    monster = new Monster(10);
                    monster.AddAttackType(Monster.AttackType.Kicking, 5);
                    break;
                case MonsterType.Kangaroo:
                    monster = new Monster(10);
                    monster.AddAttackType(Monster.AttackType.Kicking, 5);
                    monster.AddAttackType(Monster.AttackType.Punching, 5);
                    break;
                case MonsterType.MantisShrimp:
                    monster = new Monster(10);
                    monster.AddAttackType(Monster.AttackType.Punching, 5);
                    break;
                default:
                    throw new ArgumentException();
            }
            return monster;
        }
    }
}

 

This is a little more complex than the previous factory, because this is where we “compose” the Monster object, to act like a BitingMonster object (or, whatever attacks the monster can perform).

 

TestMonsterFactory.cs

using Engine.CompositionOverInheritance.Composition_BEFORE;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace TestEngine.CompositionOverInheritance.Composition
{
    [TestClass]
    public class TestMonsterFactory
    {
        [TestMethod]
        public void Test_BitingMonster()
        {
            Monster crocodile = MonsterFactory.CreateMonster(MonsterFactory.MonsterType.Crocodile);
            Assert.IsTrue(crocodile.CanBite);
            Assert.IsFalse(crocodile.CanKick);
            Assert.IsFalse(crocodile.CanPunch);
        }
        [TestMethod]
        public void Test_BitingKickingMonster()
        {
            Monster horse = MonsterFactory.CreateMonster(MonsterFactory.MonsterType.Horse);
            Assert.IsTrue(horse.CanBite);
            Assert.IsTrue(horse.CanKick);
            Assert.IsFalse(horse.CanPunch);
        }
    }
}

 

In these unit tests, we can use the CanBite, CanKick, and CanPunch properties to simulated checking for the “type”, like we would in the inheritance version.

 

With the composition method, when our boss tells us to add a new “Spit” attack, instead of creating new classes, we would only need to:

  1. Add a new “Spitting” value to the AttackType enum in Monster.cs
  2. Add a new “CanSpit” property to Monster.cs
  3. Add a new “SpitDamage” property to Monster.cs

Then, MonsterFactory.cs can create monsters that can use the new “spit” attack.

The code might look like this:

 

Monster.cs

using System;
using System.Collections.Generic;
namespace Engine.CompositionOverInheritance.Composition_AFTER
{
    public class Monster
    {
        public enum AttackType
        {
            Biting,
            Kicking,
            Punching,
            Spitting
        }
        public int HitPoints { get; set; }
        public Dictionary<AttackType, int> AttackTypes { get; set; }
        // These replace the functionality of checking an object's "type",
        // to see if it "is" a certain datatype (KickingMonster, BitingMonster, etc.)
        public bool CanBite => AttackTypes.ContainsKey(AttackType.Biting);
        public bool CanKick => AttackTypes.ContainsKey(AttackType.Kicking);
        public bool CanPunch => AttackTypes.ContainsKey(AttackType.Punching);
        public bool CanSpit => AttackTypes.ContainsKey(AttackType.Spitting);
        public int BiteDamage
        {
            get
            {
                if(CanBite)
                {
                    return AttackTypes[AttackType.Biting];
                }
                throw new NotSupportedException("This monster cannot bite.");
            }
        }
        public int KickDamage
        {
            get
            {
                if(CanKick)
                {
                    return AttackTypes[AttackType.Kicking];
                }
                throw new NotSupportedException("This monster cannot kick.");
            }
        }
        public int PunchDamage
        {
            get
            {
                if(CanPunch)
                {
                    return AttackTypes[AttackType.Punching];
                }
                throw new NotSupportedException("This monster cannot punch.");
            }
        }
        public int SpitDamage
        {
            get
            {
                if(CanSpit)
                {
                    return AttackTypes[AttackType.Spitting];
                }
                throw new NotSupportedException("This monster cannot spit.");
            }
        }
        public Monster(int hitPoints)
        {
            HitPoints = hitPoints;
            AttackTypes = new Dictionary<AttackType, int>();
        }
        public void AddAttackType(AttackType attackType, int amountOfDamage)
        {
            AttackTypes[attackType] = amountOfDamage;
        }
    }
}

 

MonsterFactory.cs

using System;
namespace Engine.CompositionOverInheritance.Composition_AFTER
{
    public class MonsterFactory
    {
        public enum MonsterType
        {
            Horse, // BitingKickingMonster
            Orc, // BitingKickingPunchingMonster
            Crocodile, // BitingMonster
            MikeTyson, // BitingPunchingMonster
            Cobra, // BitingSpittingMonster
            Camel, // KickingSpittingMonster
            Kangaroo, // KickingPunchingMonster
            MantisShrimp //PunchingMonster
        }
        public static Monster CreateMonster(MonsterType monsterType)
        {
            Monster monster;
            switch(monsterType)
            {
                case MonsterType.Horse:
                    monster = new Monster(10);
                    monster.AddAttackType(Monster.AttackType.Biting, 5);
                    monster.AddAttackType(Monster.AttackType.Kicking, 5);
                    break;
                case MonsterType.Orc:
                    monster = new Monster(10);
                    monster.AddAttackType(Monster.AttackType.Biting, 5);
                    monster.AddAttackType(Monster.AttackType.Kicking, 5);
                    monster.AddAttackType(Monster.AttackType.Punching, 5);
                    break;
                case MonsterType.Crocodile:
                    monster = new Monster(10);
                    monster.AddAttackType(Monster.AttackType.Biting, 5);
                    break;
                case MonsterType.MikeTyson:
                    monster = new Monster(10);
                    monster.AddAttackType(Monster.AttackType.Biting, 5);
                    monster.AddAttackType(Monster.AttackType.Punching, 5);
                    break;
                case MonsterType.Cobra:
                    monster = new Monster(10);
                    monster.AddAttackType(Monster.AttackType.Biting, 5);
                    monster.AddAttackType(Monster.AttackType.Spitting, 5);
                    break;
                case MonsterType.Camel:
                    monster = new Monster(10);
                    monster.AddAttackType(Monster.AttackType.Kicking, 5);
                    monster.AddAttackType(Monster.AttackType.Spitting, 5);
                    break;
                case MonsterType.Kangaroo:
                    monster = new Monster(10);
                    monster.AddAttackType(Monster.AttackType.Kicking, 5);
                    monster.AddAttackType(Monster.AttackType.Punching, 5);
                    break;
                case MonsterType.MantisShrimp:
                    monster = new Monster(10);
                    monster.AddAttackType(Monster.AttackType.Punching, 5);
                    break;
                default:
                    throw new ArgumentException();
            }
            return monster;
        }
    }
}

 

Notice that the Camel can now kick and spit. There is also a new monster: the cobra, which can bite and spit.

To give the Camel a new attack ability, we only needed to add one line (line 53).

To add the cobra, we only needed to add it to the enum, and add a new “case” (starting at line 45) to create the cobra – by composing a monster that can bite and spit.

 

 

Where I’ve found it useful

There are two common situations when you would want to consider using composition, instead of inheritance: when you need to do multiple inheritance, and when your sub-classes start to have their own sub-classes.

These are big indicators that the composition might be a better choice.

You might also combine object composition with the Strategy Design Pattern. Instead of composing the object with property values (the attack type and damage), you could also compose it by configuring how it will perform its actions.

 

All my design pattern lessons

Source code for my design pattern lessons

28 Comments

  1. Sean Meredith
    Sean Meredith November 8, 2016

    Hey! I am new(ish) to computer programming, and wanted to tell you how awesome you are!

    You should really feel special. I’m 100% of the time the guy who finds useful content and just leeches it up without telling the author how much I appreciate it. But this is just too awesome! Thanks!

    • Scott Lilly
      Scott Lilly November 8, 2016

      Thank you. It’s great to hear these are helping people.

  2. Joshua
    Joshua November 16, 2016

    I really enjoy the fact that the biting punching monster is Mike Tyson. But these tutorials are really helpful, thank you for writing them.

    • Jon
      Jon August 12, 2020

      This was so funny and clever, had to pause for a moment when I noticed it.

  3. GoHawksDani
    GoHawksDani October 4, 2017

    Nice post! Eric Lippert wrote about a similar topic to some extent. It was about how should we compose our classes if we have different types with some similar attributes, but some unique too. It’s not the same topic, but for some extent it’s relevant (but maybe just because it’s based on RPG topic too :D).
    If you guys would like to check it out, you can find it here:
    https://ericlippert.com/?s=Wizards+and+warriors&submit=Search
    (hope it’s not a problem that I linked 🙂 )

    • Scott Lilly
      Scott Lilly October 4, 2017

      Thanks!

      Thanks for sharing the link. Eric’s series of posts is great – which is not a surprise (he writes a lot of great stuff). It goes into many of the things you might need to think about if you try to use inheritance, or use composition over inheritance. There are things to consider for each technique.

      PS: It’s never a problem to link to high-quality articles on other sites 🙂

  4. Ferox Neto
    Ferox Neto February 22, 2018

    Hi Scott,

    This is a very nice example, and it was very helpful.
    However, adding the implementation of the SpitAttack on the Monster.cs class seems to me like a violation of the Open/Closed principle.

    I know its probably out of scope of this tutorial, but i was wondering if there was a way of demonstrating the composition over inheritance without violating OCP, or any other SOLID principle.

    • Scott Lilly
      Scott Lilly February 22, 2018

      Hi Ferox,

      Thank you. A better way to do this might be to combine it with the Command Design Pattern, and pass in different Attack command objects for the different types of attacks. You would create different Attack objects (BiteAttack, KickAttack, PunchAttack, etc.). The Monster class could have a List variable to hold its Attack objects. Then, when the Monster attacks, its Attack() function could look at its available Attack objects and select the best one for the current opponent.

  5. Karthik Chintala
    Karthik Chintala July 13, 2018

    Great post scott. I loved the way you walked through the post.

    This is the best post I’ve read on Composition over inheritance.

  6. Hassan Amin
    Hassan Amin February 22, 2019

    Hi, thank you for this post you made this complex concept so easy to understand.

  7. Atif
    Atif April 10, 2019

    Awesome tutorial, that’s exactly how OOP concepts should be taught.

  8. mark
    mark August 5, 2019

    I like the Composition method, but using different attacktypes as an interface, Then you can have an attack method which only accepts the correct attacktype interface. also this means that if you have a list of monsters that are roaming the passages,you can easly extract those with certain attacktypes also in your base class you can have a virtual primary and secondary attack method which is overwritten in your inherited classes. then you can call method primary attack on every monster and every monster will attack correctly to its type. I am not sure now if this is the best way.

    • Scott Lilly
      Scott Lilly August 7, 2019

      You can definitely do a better form of Composition Over Inheritance by using interfaces and other design patterns, like the strategy pattern.

      I’m doing something like what you described with the different attack methods in a “real” program at work. My program parses data, and get get values from different locations, depending on the data source. So, the parser class accepts parsing strategy objects that implement the IDataParser interface. It takes a list of IDataParser objects. So, if it can’t find the data with the first parsing strategy object, it will try the next one. So far, I like that design for the program. It should be easy to extend. If we need to look for data in another place, we just need to write a new parser class that implements IDataParser and add it to the parsing object’s list of available parsing strategies.

      • mark
        mark August 10, 2019

        Hi Scott, thanks for your reply, I think I will be looking at XML as a data source, where I can bound different types of attack and modifiers to Race, Fightingtype and Range. I will Create some strategy delegates for each type of attack, Within each range have your primary and secondary attacks, create interfaces for your ranges. then its so easy with xml parser to grab all the correct attacks and drop the correct delegate into your primary and secondary attack methods for the range. it would be a cinch in xml to change any attribute you wished. Just wondering if this would be a viable way to go.

        • Scott Lilly
          Scott Lilly August 11, 2019

          That could be a good way to go.

          Something to watch out for is that you may change your battle logic (the types of strategy objects you’d create) significantly as you work with the game. If you start with putting the parameters in XML, you’ll need to keep the XML in sync – which will probably be more difficult than using something like a factory class to instantiate and set the properties for your strategy objects (where you’ll have more help from IntelliSense). Personally, I’d wait until I thought I was close to being finished with the battle logic before moving the values to XML files.

  9. Andreas
    Andreas September 13, 2019

    Iam not quite sure

    but in my opinion inheritance is the right design pattern here…

    You have a base class monster. So first of all this should be implemented as an abstract class, which only contains functionality or properties which
    every derived monster needs.

    In your example you talk about: What would you do if you need an BittingKickingMonster?….

    For me Biting, Kicking or Punching functionality should be completly extracted an injected via Interface…

    With this approach you don´t get so complexe inheritance, where one class interhit from more than one.

    Example:

    abstract class Monster()
    {

    protected int HittingPoints {get; set;}

    }

    class KickingMonster() : Monster
    {
    private readonly ICanKick kick;

    public KickingMonsterICanKick damage)
    {
    this.kick= kick;
    }
    }

    class BittingMonster() : Monster
    {
    private readonly ICanBite bite;

    public BittingMonsterICanBite bite)
    {
    this.bite = bite;
    }
    }

    class BittingKickingMonster() : Monster
    {
    private readonly ICanBite bite;
    private readonly ICanKick kick;

    public BittingKickingMonster(ICanBite bite, ICanKick kick)
    {
    this.bite = bite;
    this.kick = kick
    }
    }

    So i think this i much more flexible and readable. But just a thought….

    • Scott Lilly
      Scott Lilly September 23, 2019

      Hi Andreas,

      Have you looked at the Strategy Pattern? I normally use that when I’m doing Composition over Inheritance. I wanted to keep each design pattern code sample to one pattern, so I didn’t include that in this sample – but these two patterns work together extremely well.

      Using the strategy pattern, I would create an IAttack interface and have create different attack classes – Bite, Kick, Punch, etc. that implement the interface. The IAttack interface might define “void Attack(LivingCreature opponent)”, with that function being the one the monster will use in battle.

      I might even create High, Medium, and Low versions of these attacks for creatures of different heights. Then, we could account for the player’s armor. A HighBite (by a dinosaur) would check its “hit” against the player’s helmet stats. A LowBite (by a rat) would check against the player’s boots stats.

      The Monster class would have a List that is populated when the Monster object is instantiated. We’d fill that with all classes that implement IAttack and are the appropriate attack type for this monster. Then, the Monster class would call “Attack(opponent)”, look through its available IAttack objects, check which would do the most damage against the opponent, and use that for the attack.

      I’m using this strategy + composition over inheritance in a real project at work where I need to parse data from files in different formats. There are strategy classes to find the different values in the different formats. I build one “Parser” class, load it with the strategy objects it needs for the particular file, and it parses it. This way, the code is very small and easy to maintain. Each strategy class only does one thing, which makes it very small and simple – which helps prevent creating bugs.

  10. Imran Ali
    Imran Ali February 15, 2020

    Excellent tutorials/explanations, much appreciated

  11. Efrain
    Efrain August 5, 2020

    I was having a hard time grasping composition and finding a practical example, this one is fantastic to visualize better what composition is and the benefit. I definitely see how it can be beneficial to futureproof the code somewhat. Thanks!

  12. Karol
    Karol March 22, 2022

    Just small typo:
    “”The definition of “Composition” is, “combining parts or elements to form a while”.
    … to form a WHOLE. 🙂

    Great explanation.
    Awesome

Leave a Reply to Efrain Cancel reply

Your email address will not be published. Required fields are marked *