13

Inspired by a great video on the topic "Favor object composition over inheritance" which used JavaScript examples; I wanted to try it out in C# to test my understanding of the concept, but it didn't go as well as I'd hoped.

/// PREMISE
// Animal base class, Animal can eat
public class Animal
{
    public void Eat() { }
}
// Dog inherits from Animal and can eat and bark
public class Dog : Animal
{
    public void Bark() { Console.WriteLine("Bark"); }
}
// Cat inherits from Animal and can eat and meow
public class Cat : Animal
{
    public void Meow() { Console.WriteLine("Meow"); }
}
// Robot base class, Robot can drive
public class Robot
{
    public void Drive() { }
}

The problem is that I want to add RobotDog class that can Bark and Drive, but not eat.


First solution was to create RobotDog as subclass of Robot,

public class RobotDog : Robot
{
    public void Bark() { Console.WriteLine("Bark"); }
}

but to give it a Bark function we had to copy and paste the Dog's Bark function so now we have duplicate code.


The second solution was to create a common super class with a Bark method that then both the Animal and Robot classes inherited from

public class WorldObject
{
    public void Bark() { Console.WriteLine("Bark"); }
}
public class Animal : WorldObject { ... }
public class Robot : WorldObject { ... }

But now EVERY animal and EVERY robot will have a Bark method, which most of them don't need. Continuing with this pattern, the sub-classes will be laden with methods they don't need.


The third solution was to create an IBarkable interface for classes that can Bark

public interface IBarkable
{
    void Bark();
}

And implement it in the Dog and RobotDog classes

public class Dog : Animal, IBarkable
{
    public void IBarkable.Bark() { Console.WriteLine("Bark"); }
}
public class RobotDog : Robot, IBarkable
{
    public void IBarkable.Bark() { Console.WriteLine("Bark"); }
}

But once again we have duplicate code!


The fourth method was to once again use the IBarkable interface, but create a Bark helper class that then each of the Dog and RobotDog interface implementations call into.

This feels like the best method (and what the video seems to recommend), but I could also see a problem from the project getting cluttered with helpers.


A fifth suggested (hacky?) solution was to hang an extension method off an empty IBarkable interface, so that if you implement IBarkable, then you can Bark

public interface IBarker {    }
public static class ExtensionMethods
{
    public static void Bark(this IBarker barker) { 
        Console.WriteLine("Woof!"); 
    }
}

A lot of similar answered questions on this site, as well as articles I've read, seem to recommend using abstract classes, however, wouldn't that have the same issues as solution 2?


What is the best object-oriented way to add the RobotDog class to this example?

6
  • 1
    in your example, the way Animal barks or Robot barks does not change which seems to be incorrect....Bark behaviour would be different in both case that means code which you write in bark method would be different.... Commented Feb 12, 2016 at 17:33
  • @Viru i'd say the exact functions in the question are just for illustrative purposes and not to be taken literally. Besides, I'm sure there are robot dogs out there that bark. Commented Feb 12, 2016 at 17:35
  • I think your case is a perfect example of a composite pattern. "Favor composition over inheritance": safaribooksonline.com/library/view/head-first-design/0596007124/… Commented Feb 12, 2016 at 17:36
  • @user1666620 Viru's point is that the way (code within the method) that a Dog barks versus a RobotDog would be fundamentally different. As an illustrative purpose, it is a Duck Typing problem, and causing confusion because the OP is trying to force it. Commented Feb 12, 2016 at 20:28
  • Your RobotDog is a Robot, but it is not a Dog, because the RobotDog is not an Animal. They both have a similar behavior, but since it does not implement everything that Dog does (the inherited Animal behavior), then it is not a Dog. Therefore, like mojorisinfy said, the closest you can get to "shared behavior" is an IBark interface. Commented Feb 14, 2016 at 2:49

2 Answers 2

5

At first if you want to follow "Composition over Inheritance" then more than half of your solutions don't fit because you still use inheritance in those.

Actually implementing it with "Composition over Inheritance" there exists multiple different ways, probably each one with there own advantage and disadvantage. At first one way that is possible but not in C# currently. At least not with some Extension that rewrites IL code. One idea is typically to use mixins. So you have interfaces and a Mixin class. A Mixin basically contains just methods that get "injected" into a class. They don't derive from it. So you could have a class like this (all code is in pseudo-code)

class RobotDog 
    implements interface IEat, IBark
    implements mixin MEat, MBark

IEat and IBark provides the interfaces, while MEat and MBark would be the mixins with some default implementation that you could inject. A design like this is possible in JavaScript, but not currently in C#. It has the advantage that in the end you have a RobotDog class that has all methods of IEat and IBark with a shared implementation. And the this is also a disadvantage at the same time, because you create big classes with a lot of methods. On top of it there can be method conflicts. For example when you want to inject two different interfaces with the same name/signature. As good as such an approach looks first, i think the disadvantages are big and i wouldn't encourage such a design.


As C# doesn't support Mixins directly you could use Extension Methods to somehow rebuilt the design above. So you still have IEat and IBark interfaces. And you provide Extension Methods for the interfaces. But it has the same disadvantages as a mixin implementations. All methods appear on the object, problems with method names collision. Also on top, the idea of composition is also that you could provide different implementations. You also could have different Mixins for the same interface. And on top of it, mixins are just there for some kind of default implementation, the idea is still that you could overwrite or change a method.

Doing that kind of things with Extensions Method is possible but i wouldn't use such a design. You could theoretically create multiple different namespaces so depending on which namespace you load, you get different Extension Method with different implementation. But such a design feels more awkward to me. So i wouldn't use such a design.


The typical way how i would solve it, is by expecting fields for every behaviour you want. So your RobotDog looks like this

class RobotDog(ieat, ibark)
    IEat  Eat  = ieat
    IBark Bark = ibark

So this means. You have a class that contains two properties Eat and Bark. Those properties are of type IEat and IBark. If you want to create a RobotDog instance then you have to pass in a specific IEat and IBark implementation that you want to use.

let eat  = new CatEat()
let bark = new DogBark()
let robotdog = new RobotDog(eat, bark)

Now RobotDog would Eat like a cat, and Bark like a Dog. You just can call what your RobotDog should do.

robotdog.Eat.Fruit()
robotdof.Eat.Drink()
robotdog.Bark.Loud()

Now the behaviour of your RobotDog completely depends on the injected objects that you provide while constructing your object. You also could switch the behaviour at runtime with another class. If your RobotDog is in a game and Barking gets upgraded you just could replace Bark at runtime with another object and the behaviour you want

robotdog.Bark <- new DeadlyScreamBarking()

Either way by mutating it, or creating a new object. You can use a mutable or immutable design, it is up to you. So you have code sharing. At least me i like the style a lot more, because instead of having a object with hundreds of methods you basically have a first layer with different objects that have each ability cleanly separated. If you for example add Moving to your RobotDog class you just could add a "IMovable" property and that interface could contain multiple methods like MoveTo, CalculatePath, Forward, SetSpeed and so on. They would be cleanly avaible under robotdog.Move.XYZ. You also have no problem with colliding methods. For example there could be methods with the same name on each class without any problem. And on top. You also can have multiple behaviours of the same type! For example Health and Shield could use the same type. For example a simple "MinMax" type that contains a min/max and current value and methods to operate on them. Health/Shield basically have the same behaviour, and you can easily use two of them in the same class with this approach because no method/property or event is colliding.

robotdog.Health.Increase(10)
robotdog.Shield.Increase(10)

The previous design could slightly be changed, but i don't think it makes it better. But a lot of people brainlessly adopt every design pattern or law with the hope it automatically makes everything better. What i want to refer here is the often called Law-of-Demeter that i think is awful, especially in this example. Actually there exists a lot of discussion of whether it is good or not. I think it is not a good rule to follow, and in that case it also becomes obvious. If you follow it you have to implement a method for every object that you have. So instead of

robotdog.Eat.Fruit()
robotdog.Eat.Drink()

you implement methods on RobotDog that calls some method on the Eat field, so with what did you end up?

robotdog.EatFruit()
robotdog.EatDrink()

You also need once again to solve collisions like

robotdog.IncreaseHealt(10)
robotdog.IncreaseShield(10)

Actually you just write a lot of methods that just delegates to some other methods on a field. But what did you won? Basically nothing at all. You just followed brainless a rule. You could theoretically say. But EatFruit() could do something different or do something additional before calling Eat.Fruit(). Weel yes that could be. But if you want other different Eat behaviour then you just create another class that implements IEat and you assign that class to the robotdog when you instantiate it.

In that sense, the Law of Demeter is not a dot counting Exercise.

http://haacked.com/archive/2009/07/14/law-of-demeter-dot-counting.aspx/


As a conclusion. If you follow that design i would consider using the third version. Use Properties that contain your Behaviour objects, and you can directly use those behaviours.

Sign up to request clarification or add additional context in comments.

1 Comment

So far, it does seem like encapsulation is the way to go.
2

I think this is more of a conceptual dilemma rather than a composition issue.

When you say : And implement it in the Dog and RobotDog classes

public class Dog : Animal, IBarkable
{
    public void IBarkable.Bark() { Console.WriteLine("Bark"); }
}
public class RobotDog : Robot, IBarkable
{
    public void IBarkable.Bark() { Console.WriteLine("Bark"); }
}

But once again we have duplicate code!

If Dog and RobotDog have the same Bark() implementation, they should inherit from the Animal class. But if their Bark() implementations are different, it makes sense to derive from IBarkable interface. Otherwise, where is the distinction between Dog and RobotDog?

6 Comments

Making RobotDog inherit from the Animal class can't really be a solution because then we have the same problems. If we did do that, now RobotDog has an Eat() method from the Animal class that it should not have. Additionally it is now lacking the Drive() method from the Robot class. We would now need to either copy the Drive() method into the RobotDog class (copy/paste duplicate code) or implement an IDrivable interface (same issues as IBarkable with duplicate identical implementations between classes)
The main purpose of inheritance is not code-sharing. Those ridiculous ideas of "hey two classes use the same method, put in class and derive from it" leads to the exact horrible inheritance trees that no one really can understand at some point in time. Inheritance is for creating subtypes, that they can share common code is "nice to have", you don't inherit for the purpose of sharing some lines of code. You should follow LSP. And the Circle-Elipse problem explain it further why you not should inherit just for sharing some code: en.wikipedia.org/wiki/Circle-ellipse_problem
@chriszumberge I think the issue is that you are trying to combine two different concepts Animal and Robot. RobotDog and Dog don't bark the same way and are essentially two distinct objects regardless of "barkable" behavior. Agree?
@SidBurn If all you intend is to create a subtype, then you should use an interface, not a base class. Inheritance is absolutely intended as a code-sharing technique.
@BenjaminHodgson In C# (implementation) inheritance is also automatically subtyping (interface inheritance). If you inherit from another class, that new class can be put everywhere where the parent class was expected. Just inheritance and code-reuse without creating a subtype doesn't exists in C#. So inheritance have to fulfill also the rules for subtyping (LSP). So someone should not use (implementation) inheritance just because he sees some code he wants to reuse.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.