my following example seems to go into the direction Inheritance VS Composition. But that's not, what i want to ask. I see the concept Inheritance and Composition on one side and the alternative, which I will show, on the other.
Let's say I have the following Base
class
class Base
{
protected final String a = "1";
protected final String b = "2";
protected void c()
{
System.out.println("I'm Base" + this.a);
}
public final void action()
{
//uses in any way a, b and c
}
}
Now I want to have some different functionality based on that.
So I can do the following:
class Child1 extends Base
{
protected final String a = "super";
@Override
protected void c()
{
System.out.println("I'm Child1" + this.a);
}
}
static class Child2 extends Base
{
@Override
protected void c()
{
System.out.println("I'm Child1" + this.b);
}
}
With Child1 and Child2 i get two other functionality based on Base. And if I want to I can create a lot of more
The same I can achieve with:
final class Base2
{
// there exists ways to hide this public things here too,
// with the help of some interfaces, but that's not relevant for now
public final String a;
public final String b;
private Consumer<Base2> cConsumer = null;
public Base2(String a, String b, Consumer<Base2> cConsumer)
{
this.a = a;
this.b = b;
this.cConsumer = cConsumer;
}
public void c()
{
this.cConsumer.accept(this);
}
public final void action()
{
//uses in any way a, b and c
}
}
So the client can use now for example the functionality of Child1 the following three ways:
public void clientMethod()
{
var result1= new Child1();
var result2= new Base2(
"super",
"2",
base2 ->
{
System.out.println("I'm Child1" + base2.a);
});
var result3 = Some result from a client-related-specific Builder/Factory
}
The second solution (result2=...
) seems to be, that this would be the composition-way. And yes that is true. But normally a client doesn't do that in that way. Normally there would be factory/builder (result3=...
) in between, so that it doesn't make any difference in the view of the client.
So I don't focus on inheritance VS/over composition, but on inheritance/composition VS something "Direct Injection Construction".
So the problem is with the Builder (which creates an composition-Object) or Inheritance, that I must create some extra class to get a Base
-Class, which acts like the Client wants to. In my alternative solution (direct create and use of the composition), I don't need that extra class. So I can on demand let the Base class acts like I want to.
So my question is,
when I should let the client define (by injection directly) the final behavior of Base
, and when I should "hard" code that behavior into child classes or builder-classes , IF I need that specific behavior only once (in that related CLIENT)
The second question goes into more detail. Maybe it makes sense to "hard code" parts and let other parts "injectable". The question here is, which parts should follow the one or the other principle.
So a concrete example,
where my "injectable" version is used in existing software:
Lets say we have an Object were you can register listeneres/add hooks into it. So that kind of configuration do the client on himself. I don't think that this would be done in a inheritance hierarchy or in a hard coded Builder. The same, if i construct a object of the class human. Here the client puts some information into that object to configure it. Maybe the name, the age, the hair-color and so on, maybe the client also inject a function which determine the movement-speed. But this example (the complete Human) could also be modeled the other way. So that I have "fixed" Human-Classes. Which way I should choose? (Yeah I know there could be some decisions-helper, if I need the same Human more than one time; but this decision-case should be hidden/not relevant here)
An other example:
I have a Template-Class but the client should determine how the method xy()
in that Template-Class should work. I can create some child classes with that specific behavior. Or i give up and let the client directly choose the specific behavior which the client inject then into the template class and then uses that class.
Maybe there exists any advantages or disadvantages to use sometimes one or sometimes the other, but which advantages and disadvantages (related to the sometimes and sometimes^^)
Consumer<T>
is an interface, which the class generated from your lambda implements. This can be generalized further - you can accept an interface or a base class, and let the client decide which implementation of the interface / base class to inject (so, the two approaches can be combined). Many (most?) design patterns work exactly that way.When I should let the client define (by injection directly) the final behaviour of Base
when the consumer can change the behaviour dynamically at will. If the behaviour is rarely or never altered in runtime due to a consumer choice, why should it be dynamic at all?