0

When multiple classes implement one (or more) interface, despite being different classes, they might have a lot of logic in common and only differ slightly.

In that case, it may be handy to move the shared logic into a shared class, which is then used as a component. Calls of methods with a shared logic are delegated to that component.

For exemple :

interface IFoo
{
    void DoFoo();
}

interface IBar
{
    void DoBar();
}

interface IBaz
{
    void DoBaz();
}

class DefaultFoo()
{
    public void Do() { /* Shared logic */ }
}

class DefaultBar()
{
    public void Do() { /* Shared logic */ }
}

class FooBarBaz1 : IFoo, IBar, IBaz
{
    DefaultFoo defaultFoo = new DefaultFoo();
    DefaultBar defaultBar = new DefaultBar();

    public void DoFoo() { defaultFoo.Do(); }
    public void DoBar() { defaultBar.Do(); }
    public void DoBaz() { /* FooBarBaz1 specific IBaz implementation */ }
}

class FooBarBaz2 : IFoo, IBar, IBaz
{
    DefaultFoo defaultFoo = new DefaultFoo();

    public void Foo() { defaultFoo.Do(); }
    public void Bar() { /* FooBarBaz2 specific IBar implementation */ }
    public void Baz() { /* FooBarBaz2 specific IBaz implementation */ }
}

// More ForBarBazX classes...

In classes FooBarBaz1 and FooBarBaz2, we completely wrap the DefaultFoo instance to give the impression from the public side that FooBarBaz1 and FooBarBaz2 are doing the work while they are just delegating it.

The problem with that is, if you have a large number of classes that must implement the interface and / or the interface has many methods, you end up being force to write a lot of "useless" delegation code.

So my question is : can't we just NOT wrap the shared logic ? Instead of requiring the methods to be implemented, the interfaces require to have these components exposed.

We would have instead :

interface IFoo
{
    DefaultFoo Foo { get; }
}

interface IBar
{
    DefaultBar Bar { get; }
}

interface IBaz
{
    DefaultBaz Baz { get; }
}

class DefaultFoo()
{
    public virtual void Do() { /* Shared logic */ } // Note the virtual.
}

class DefaultBar()
{
    public virtual void Do() { /* Shared logic */ } // Note the virtual.
}

class FooBarBaz1 : IFoo, IBar, IBaz
{
    private class SpecificBaz : DefaultBaz
    {
        public override void DoBaz() { /* Specific logic */ }
    }

    public DefaultFoo Foo { get; private set; }
    public DefaultBar Bar { get; private set; }
    public DefaultBaz Baz { get; private set; }

    public FooBarBaz1()
    {
        Foo = new DefaultFoo();
        Bar = new DefaultBar();
        Baz = new SpecificBaz();
    }
}

class FooBarBaz2 : IFoo, IBar, IBaz
{
    private class SpecificBar : DefaultBar
    {
        public override void Bar() { /* Specific logic */ }
    }

    private class SpecificBaz : DefaultBaz
    {
        public override void Baz() { /* Specific logic */ }
    }

    public DefaultFoo Foo { get; private set; }
    public DefaultBar Bar { get; private set; }
    public DefaultBaz Baz { get; private set; }

    public FooBarBaz1()
    {
        Foo = new DefaultFoo();
        Bar = new SpecificBaz();
        Baz = new SpecificBaz();
    }
}

Than, instead of doing :

ForBarBaz1 fooBarBaz = new ForBarBaz1();
fooBarBaz.Foo();
fooBarBaz.Bar();
fooBarBaz.Baz();

One would access the exposed components :

ForBarBaz1 fooBarBaz = new ForBarBaz1();
fooBarBaz.Foo.Do();
fooBarBaz.Bar.Do();
fooBarBaz.Baz.Do();

What do you think about this ? It seems to be like it preserves modularity while avoiding the large amounts of "useless" wrapping code.

PS : I'm asking this for C# but I guess it would also apply to some other languages.

Virus721
  • 8,061
  • 12
  • 67
  • 123
  • 1
    There's easier ways to do compositon. Check out [this answer](https://stackoverflow.com/a/178368/585968) with the novel use of extension methods. Makes it much simpler –  Sep 28 '18 at 17:35
  • Vote to move this into [Code Review](http://codereview.stackexchange.com/) – yorodm Sep 28 '18 at 17:38
  • @virus721 comment is implied. Simply click the close link and it will show you the breakdown of votes and a description as to _"why"_ –  Sep 28 '18 at 17:38
  • Possible duplicate of [Multiple Inheritance in C#](https://stackoverflow.com/questions/178333/multiple-inheritance-in-c-sharp). Though the titles may be different, the net aim is the same. Of particular note is [this answer](https://stackoverflow.com/a/178368/585968) –  Sep 28 '18 at 17:40
  • @MickyD Thanks for your comment. It really ressembles what I did in the second part, execpt that the answer also includes some extensions methods that might have been included in the exposed components `SteeringWheel` and `BreakPedal`. What is so much different ? Thanks. – Virus721 Sep 28 '18 at 17:43
  • @yorodm this code would be too generic for CR, and thus would not be [on-topic](https://codereview.stackexchange.com/help/on-topic) – Sᴀᴍ Onᴇᴌᴀ Sep 28 '18 at 17:43
  • The _difference_ is that callers do not refer to the composition objects directly. Instead of `fooBarBaz.Foo.Do()` you would just perform a `fooBarBaz.Do()` –  Sep 29 '18 at 00:12
  • Ho I see. Instead of accessing components, you instead do some wrapping / delegation, but using some extension methods on the interface instead, so that it's not necessary to do it in every implementing class. I'll try that, thanks. – Virus721 Sep 29 '18 at 12:14
  • @MickyD Also, what about properties ? Extension methods allow factoring the wrapping / delegating code for methods, but it doesn't help with properties ! – Virus721 Sep 29 '18 at 12:23
  • Yes it's quite an efficient trick. I read somewhere that c# extension "methods" may soon address properties too. I can't tell you how much joy that will bring programmers :) –  Sep 30 '18 at 01:16

0 Answers0