I am trying to extend classes using composition where possible. But I am having trouble with a particular problem.
Lets say I have a Base class and an Extending class. The Extending class takes in an instance of the Base class rather than inheriting from it if we are using composition and we want to inject our dependencies:
public class Base
{
private ISomeDependency dependency;
public Base(ISomeDependency dependency)
{
this.dependency = dependency;
}
public ISomeDependency getDependency()
{
return dependency;
}
}
public class Extending
{
private Base base;
public Extending(Base base)
{
this.base = base;
}
}
The Base class takes in some dependency which we can then use in the Extending class using the getDependency() method.
However, what if we want to specialize what subtype of ISomeDependency we need in the extending class.
If we were using inheritance we could simply pass the subtype into the superclass constructor like so:
public class Extending extends Base
{
private ISomeDependencySubtype dependency;
public Extending(ISomeDependencySubtype dependency)
{
super(dependency);
this.dependency = dependency;
}
}
But if we are using composition then this becomes more difficult. I have thought of a number of options:
Having an initialization method instead of a constructor in Base
public class Extending
{
private ISomeDependencySubtype dependency;
private Base base;
public Extending(Base base, ISomeDependencySubtype dependency)
{
base.init(dependency);
this.base = base;
this.dependency = dependency;
}
}
This option hides the fact that the Extending class uses the init() method on the Base class so the interface becomes perhaps unclear. Upon using the Extending class another programmer might assume that the Base instance must have init() called on it before it is passed into the Extending instance.
Adding a generic argument
public class Base<T extends ISomeDependency>
{
private T dependency;
public Base(T dependency)
{
this.dependency = dependency;
}
public T getDependency()
{
return dependency;
}
}
public class Extending
{
private ISomeDependencySubtype dependency;
private Base<ISomeDependencySubtype> base;
public Extending(Base<ISomeDependencySubtype> base)
{
this.base = base;
this.dependency = base.getDependency();
}
}
This seems like an abuse of generics. If the Base class is a class in its own right and can be used without the Extending class then it shouldn't need to care which subtype of ISomeDependency is passed to it. The generic argument would only exist for this specific scenario despite everything needing to be refactored to accommodate it.
Instantiating Base inside of Extending
public class Extending
{
private ISomeDependencySubtype dependency;
private Base base;
public Extending(ISomeDependencySubtype dependency)
{
this.base = new Base(dependency);
this.dependency = dependency;
}
}
This means that the Base instance cannot be passed in which means the dependency cannot be injected. This is inflexible for reasons which are probably obvious to most.
So I would like to know what people think is the most preferable option using composition to overcome this problem (no inheritance based answers please).