0

Let's say I have the following inheritance tree:

        ______ Object______
       /                   \
     Food              Material
   /     \            /        \
Egg       Carrot   Box          Axe

Expressed like this in C#

class Object { ... }

class Food : Object { ... }
class OfficeMaterial : Object{ ... }    

class Box : OfficeMaterial { ... }
class Spoon : OfficeMaterial { ... }
class Egg : Food { ... }
class Carrot : Food { ... }

And now I want to share the functionality only between the class Box and Egg, for instance:

bool opened = true;

public void Open() { open = true; }
public void Close() { open = false; }
public bool IsOpen() { return opened; }

This is a short example, but it could be something much longer.

What would be the proper way to do it? If I use inheritance, other classes like Carrot and Axe will get it, which is something I do not wish.

UPDATE Many people mention Composition. Could someone show me how that would work in the example I presented?

Enrique Moreno Tent
  • 24,127
  • 34
  • 104
  • 189
  • This might be relevant for you: http://stackoverflow.com/questions/10729230/how-would-you-implement-a-trait-design-pattern-in-c – obe Mar 13 '16 at 23:16
  • 1
    Composition is your friend. Inheritance is overrated anyhow (a personal opinion of mine). – MicroVirus Mar 13 '16 at 23:18
  • @Dbugger You want to share code between classes, presumably share some behaviour. The composition solution is to put that behaviour into a class and then add an instance of that class as a private member of your Egg and Box classes; they will delegate the shared behaviour to the new class. It's called composition, because one class (Box, Egg) has a reference to ('composes with') a shared behaviour class. This can be optionally be paired with an interface that is shared between Egg and Box, depending on your requirements. – MicroVirus Mar 13 '16 at 23:42
  • So you mean I should do something like `Egg e = new Egg(); e.openness.Open()`? That is syntactically hideous... – Enrique Moreno Tent Mar 13 '16 at 23:44
  • No, you're missing the point. Composition is an implementation detail of the class that is preferably hidden from the class user. You would just call `e.Open()`, and the `Open` method then delegates to a class that models open/closedness. – MicroVirus Mar 15 '16 at 00:00
  • That would still include some redundancy between the classes Egg and Box (even if not much) – Enrique Moreno Tent Mar 15 '16 at 08:09
  • Possible duplicate of [C++ - Put members in common for two sub classes](http://stackoverflow.com/questions/36008299/c-put-members-in-common-for-two-sub-classes) – MicroVirus Mar 15 '16 at 10:32
  • The redundancy should only be in implementing the interface of the Egg and Box classes, where the behaviour is not redundant but shared. Unless the functionality comes via inheritance, this is to be expected. – MicroVirus Mar 15 '16 at 10:41
  • I didnt say meaningless, but redundant. The problem is that if the composited class has 50 public methods, all have to be written 50 times on each class that implements the composition. Not really elegant code. – Enrique Moreno Tent Mar 15 '16 at 10:46
  • 50 public methods is too much, anyhow, for one class. There's always a bit of redundancy in code, but it shouldn't be too much in practice. Multiple inheritance is one thing that can 'solve' this, but it's rarely needed in practice. C# does not support it, though. [This question is about the same problem you are seeing](http://stackoverflow.com/questions/178333/multiple-inheritance-in-c-sharp). – MicroVirus Mar 15 '16 at 21:24

4 Answers4

2

You have a few options:

  1. Use interfaces

Ex:

public interface IOpenable
{
    void Open();
    void Close();
    bool IsOpen();
}

Then any class that needs the openable/closable behavior can implement the interface accordingly.

  1. Favor composition over inheritance and compose your objects out of other objects that implement the desired behavior.
Daniel Mann
  • 57,011
  • 13
  • 100
  • 120
  • Interfaces would share the declaration of the methods, but not the definition. Could you give me an example of how Composition would look like in the case I presented? – Enrique Moreno Tent Mar 13 '16 at 23:17
0

Why not use an interface? IOpenable?

interface IOpenable {
    void Open();
    void Close();
    bool IsOpen();
}
Daniel A. White
  • 187,200
  • 47
  • 362
  • 445
0

When I think about the similarities between Egg and Box that are not properties shared by Carrot and Axe I think first about the fact that they contain something.

  1. Egg and Box have Contents which can be exposed, hidden, added, removed, etc.

Notice the language here - Egg and Box have Contents. The word have (or has) is an indicator that an object representing the Contents of another object should be used as a composition element.

An Egg is a type of Food. An Egg has Contents which are a Yolk and an EggWhite.

A Box is a type of OfficeMaterial. A Box has Contents which are OfficeSupplies.

I could write something like this:

public class Food : Object 
{
    public void Eat() { }
}

public class OfficeMaterial : Object { }

public class Contents : Object 
{
    bool _visible = false;

    List<Object> _things = new List<Object>();

    public int Count { get { return _things.Count; } }

    public bool IsOpen { get { return _visible; } }

    public void Expose()
    {
        _visible = true;
    }

    public void Hide()
    {
        _visible = false;
    }

    public void Add(object thing)
    {
        _things.Add(thing);
    }

    public bool Remove(object thing)
    {
        return _things.Remove(thing);
    }
}

public class Box : OfficeMaterial 
{
    public Contents BoxContents = new Contents();
}

public class Egg : Food 
{
    public Contents EggContents = new Contents();
}

Which would further allow me to:

void PlaceEggsInBox(int numEggs, Box box)
{
    box.BoxContents.Expose();

    if (box.BoxContents.AreVisible)
    {
        int eggsInBox = box.BoxContents.Things.Count(thing => thing is Egg);

        for (int i = 0; i < numEggs - eggsInBox; i++)
        {
            box.BoxContents.Add(new Egg());
        }
    }
}

And then I might like to

void EatAllEggsInBox(Box box)
{
    box.BoxContents.Expose();

    foreach (Egg egg in box.BoxContents.Things.Where(thing => thing is Egg))
    {
        box.BoxContents.Remove(egg);

        egg.EggContents.Expose();

        if (egg.EggContents.AreVisible) egg.Eat();
    }
}

The Eat() method is a method of the Food class. The Expose() method is a method of the Contents class, not the Food class or the OfficeMaterials class, nor of the Egg nor Box classes.

khargoosh
  • 1,450
  • 15
  • 40
  • I understand the point that you are trying to make, even though in this case it is not the content of the element, but just if it is open or not. I guess I could retort the language to make it fit Composition. Something like `Egg.openness.open()`, even though it sounds horrible... – Enrique Moreno Tent Mar 14 '16 at 08:11
  • 1
    Using the `Contents` was just another way to look at the same problem. Do whatever works for you - I might be wrong, but there must be something important inside if you need to `open()` it at all... perhaps try coding from the perspective of that object. Anyways, there's always more than one way to `cat.Skin()` – khargoosh Mar 14 '16 at 10:13
0

That's effectively multiple inheritance, which C# doesn't support other than through interfaces.

An option is to create an interface without the Open method (otherwise you'll have to implement it, so this is only for typing purposes) then implement a static extension methods in a static class alongside your interface.

public Interface IOpenable { }

public static class IOpenableExtensions {
  public static Open(this IOpenable obj){
      // work on any concrete implementation of IOpenable
  }
}

Usage:

var box = new Box; // Box implements IOpenable
var egg = new Egg; // Egg implements IOpenable
box.Open();
egg.Open();

This pattern may work well when the actual inheritance reflect a core structure or organization while the interfaces provide common "behaviors" shared by certain objects. A class may implement one or more of such interfaces.

C# 8 default interface implementation might make this pattern more common.

In practice, you often end up needing to cast your objects to the interface when working with functions that deal with your base classes.

public IEnumerable<Food> GetFoods(){ ... }

public void HandleFood() {
    for(var food in GetFoods()) {
        if(food is IOpenable openableFood){ 
             openableFood.Open();
        }
    }
}

Finally, note that this is not true multiple inheritance as you cannot inherit/override the extension methods themselves through a hierarchy of interfaces. If you have IOpenableFood inherit IOpenable with both an Open() extension method, you can't call base() in the derived one, and you'll have to choose which extension method to use explicitly. Best to avoid this altogether.

var egg = new Egg(); // Egg inherits both IOpenable and IOpenableFood
egg.Open(); // Error: Ambiguous method
IOpenableFoodExtensions.Open(egg); // OK, call that method
IOpenableExtensions.Open(egg); // OK, call other method
mtone
  • 1,685
  • 15
  • 29