1

I wanted to gain a better understanding of composition over inheritance. I watched a few tutorials which explain the concepts very well but using languages I am more familiar with such as Python. However, I am learning C# at the moment and am struggling to implement what I have learned on the subject and wondered if anyone could help. Here is the code I have:

using System;

namespace Composition
{
    class Program
    {
        static void Main(string[] args)
        {
            SuperBot steven = new SuperBot(new Dog(),new Robot());
            steven.Bark();


        }
    }

    class Dog
    {
        public void Bark()
        {
            Console.WriteLine("Woof");
        }

    }

    class Robot
    {
        public void Move()
        {
            Console.WriteLine("I'm moving!");
        }
    }

    class CleanRobot
    {
        public void Clean()
        {
            Console.WriteLine("Just keep dusting, just keep dusting");
        }
    }

    class SuperBot
    {
        // can clean, move and bark
        public Dog o1;
        public Robot o2;
        public CleanRobot o3;

        public SuperBot(Dog dog, Robot robot)
        {
            this.o1 = dog;
            this.o2 = robot;

        }

    }

}

Essentially, I have a Dog that can bark, a Robot that can move and a CleanRobot that can clean. I want to create a SuperBot that can move, bark and clean, and use composition to achieve this.

I have two main questions:

  1. The above is what I have coded so far, but yet it will not allow me to call the Bark method on my SuperBot object, as it says SuperBot class doesn't contain this method.

  2. How would I use composition in the case where my Dog class could also Eat() but I do not want to use this method for my SuperBot class?

Really appreciate any help - struggling to get my head around this!

UPDATE - ATTEMPT USING INTERFACES

using System;

namespace Composition
{
    public interface ICanBark
    {
        string Bark();
    }

    public interface ICanMove
    {
        string Move();
    }

    public interface ICanClean
    {
        string Clean();
    }

    class Program
    {


    static void Main(string[] args)
        {
            SuperBot steven = new SuperBot();
            steven.Bark();

        }
    }

    class Dog : ICanBark 
    {
        public string Bark()
        {
            return "Woof"; 
        }
    }

    class Robot : ICanMove
    {
        public string Move()
        {
           return "I'm moving";
        }
    }

    class CleanRobot : ICanClean
    {
        public string Clean()
        {
           return "Just keep dusting, just keep dusting";
        }
    }

    class SuperBot : ICanBark, ICanMove, ICanClean
    { 
        public string Bark()
        {
            return "Barking";
        }
    }

}
juemura7
  • 411
  • 9
  • 20

2 Answers2

3

When using composition as in your example, you unfortunately have to implement the exposed methods in the composing class as well:

class SuperBot
{
    // can clean, move and bark
    public Dog o1;
    public Robot o2;
    public CleanRobot o3;

    public SuperBot(Dog dog, Robot robot, CleanRobot cleanRobot)
    {
        this.o1 = dog;
        this.o2 = robot;
        this.o3 = cleanRobot;
    }

    public void Bark() => o1.Bark();
    public void Move() => o2.Move();
    public void Clean() => o3.Clean();
}

This will solve your compiler error. Also if you add more methods to Dog, Robot or CleanRobot, they will not impact what the SuperBot offers, i.e. if Dog has a method Eat(), SuperBot will not have this method unless you add it as well.

Conceptually you use composition like this, if you model a SuperBot as actually consisting of a Dog, a Robot and a CleanRobot. The SuperBot then uses its Dog to bark and its CleanRobot to clean.


Edit (from comments):

If you want to have a function that calls Bark

    public void GoodBoy(Dog dog)
    {
        dog.Bark();
    }

You cannot pass a SuperRobot to this method. In that case you need a common interface for Dog and SuperRobot that contains the Bark method.

user1781290
  • 2,674
  • 22
  • 26
  • Thanks so much @user1781290 - this is really helpful and I have tried this and it worked! Perfect. – juemura7 Aug 03 '19 at 12:27
  • @jordantomiko Just one warning on this. SuperBot is not a Dog, Robot or CleanerBot - it just contains one. Generally you want something that has the ability of a Dog or ICanBark to also be - and thus be castable into - ICanBark. But maybe your learning has not yet reached that part of OOP. – Christopher Aug 03 '19 at 13:48
  • Thanks again @Christopher. That's useful to know. I understand that SuperBot is not a Dog, Robot or CleanerBot and that it just contains one. However, I'm not sure what you mean by wanting something that has the ability of a Dog or ICanBark and thus be castable into ICanBark? Do you mind elaborating please? :) – juemura7 Aug 03 '19 at 13:54
2

I think the SuperBot is a aggregration type object composition, but I am not 100% about the terminology that far into theory territory.

What you describe sounds like you want to do Multiple Inheritance. Unfortunately that one leads to the Diamond Problem. Wich was one of the many problems the .NET Developers wanted to avoid. So they declared: "Single Inheitance only". (Note that C# fully supports Multiple Inheritance, but here the Framework rules are more important).

.NET does have something somewhat similar to Multiple Inheritance: Interfaces. In lack of a better term, a Interface is "more abstract then a abstract class". Interface are not inherited, they are implemented. And as they do not carry any code, you can implement as many interfaces as you like without risking the dreaded diamond of death.

In your case, you propably want the Intefaces ICanBark, ICanMove and ICanClean. The concrete Dog, Robot and Cleaner classes implement one of them. The SuperBot all 3.

Christopher
  • 9,634
  • 2
  • 17
  • 31
  • Thanks Christopher! That's really helpful. Just looking into Interfaces now, which I haven't used before. I have implemented the ICanBark, ICanMove and ICanClean interfaces but not sure how to implement these into my SuperBot? – juemura7 Aug 03 '19 at 11:59
  • @jordantomiko When declaing a class, you just put interfaces where you would put the base class in normal inhertiance. You can still define the single base class on top of that, but if you do not Object will be implied by the .NET Rules. – Christopher Aug 03 '19 at 12:05
  • Thanks @Christopher. I've updated my answer to show the implementation of Interfaces (well, my attempt at least - I can't say I've fully got my head round it yet). Is this how you'd do this in C#? – juemura7 Aug 03 '19 at 12:14
  • @jordantomiko: Almost. It should be `class SuperBot : ICanBark, ICanMove, ICanClean` for the Super bot. – Christopher Aug 03 '19 at 12:17
  • I'd argue that you are mixing two concepts here. Interfaces are as sort of contract: An interface specifies what a specific class has to implement in order to "offer" this interface. Composition is used when you delegate functionality to already existing classes/objects. – user1781290 Aug 03 '19 at 12:20
  • @user1781290: I said Interfaces are "somehwat similar to" - not the same as - Multiple Inheritance. Describing them as "contract" is not wrong and I myself described them so as well. But for me the intetion is clear on the MI. Side. MI was forbidden, but also nessesary. So they choose "Single Inhertiance, but with Interfaces as a construct added". Same way they said "avoid Naked Points, but ref/out and delegates added to compensate". – Christopher Aug 03 '19 at 13:09
  • @Christopher What I meant is that interfaces (or multiple inheritance, for that matter) is not the same as object composition – user1781290 Aug 03 '19 at 13:13
  • @user1781290 Actually, I did not. I said "What you show there looks like a aggregate composition. But what you described sounds like you want Multiple Inheritance." Clearly showing the likely diffrences. – Christopher Aug 03 '19 at 13:49
  • That is my point. He described composition, not multiple inheritance. But I guess at this point we can just agree to disagree – user1781290 Aug 03 '19 at 13:54