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.