Java interface design: where should I put a lot of duplicate code that will be used by all subclasses?
interface Tuple {
void method1();
}
class Tuple1 implements Tuple {
@Override
public void method1() {
utilityMethod();
// some code ....
}
private void utilityMethod(){
// some code....
}
}
class Tuple2 implements Tuple {
@Override
public void method1() {
utilityMethod();
// some code ....
}
private void utilityMethod(){
// some code....
}
}
Interface can't define final
or private
method, the utilityMethod
is private and it shouldn't be overrided. The utilityMethod
will be used by all subclasses of Tuple
, where should I put the utilityMethod
best?
-
This question is too specific to your situation and yet contains too little information for someone to be able to provide a useful answer to you. It also asks for a best answer which will only result in people providing their opinions, something that this site tries to avoid. You need to clearly state your assumptions and ask specific questions about what you are trying to achieve.Jason K.– Jason K.2020年06月10日 16:57:42 +00:00Commented Jun 10, 2020 at 16:57
-
What about making the interface an abstract class?Dirk V– Dirk V2020年06月17日 13:39:37 +00:00Commented Jun 17, 2020 at 13:39
3 Answers 3
Java interface design: where should I put a lot of duplicate code that will be used by all subclasses?
First, this is not interface design. This is implementation detail. Nothing outside Tuple
needs to know where you put your code.
Second, be sure this duplicate code really should be lumped together. Sometimes code needs to be allowed to change independently.
Finally, consider a solution that is extensible but follows convention over configuration:
interface Tuple {
void method1();
}
class Tuple1 implements Tuple {
private final Utility utility;
Tuple1() { this( new UtilityDefault() ); }
Tuple1(Utility utility) { this.utility = utility; }
@Override
public void method1() {
utility.method();
// some code ....
}
}
class Tuple2 implements Tuple {
private final Utility utility;
Tuple1() { this( new UtilityDefault() ); }
Tuple1(Utility utility) { this.utility = utility; }
@Override
public void method1() {
utility.method();
// some code ....
}
}
This way UtilityDefault
provides the conventional utility method. But you're allowed to replace that when constructing a Tuple if there is a need. Done this way construction code remains simple for the typical cases.
-
1I typically prefer having the no-arg constructor do
this(new UtilityDefault());
but yes, this seems to be the right answer for this situation. Java interfaces have support fordefault
methods but there's no way to make them private.JimmyJames– JimmyJames2020年06月10日 20:32:30 +00:00Commented Jun 10, 2020 at 20:32 -
@JimmyJames I find myself curious about that practice. Care to cite any arguments for it?candied_orange– candied_orange2020年06月10日 20:44:05 +00:00Commented Jun 10, 2020 at 20:44
-
@candied_orange I don't have a reference but my reasoning is that it means there is only on place in the class where that variable is assigned. (Side note: I also almost always make my member variables final.) Along similar lines, I've moved more towards using factory methods and having only one private constructor on a class à la Josh Bloch's "Effective Java".JimmyJames– JimmyJames2020年06月11日 14:40:16 +00:00Commented Jun 11, 2020 at 14:40
-
@JimmyJames true, but it makes one constructor dependant on another. It also means where is more than one place in the class where the order of the constructor parameters is defined. Not sure how to weigh that against duplicate assignments. However, with final you clearly have a good point. You caught me being lazy. Will edit.candied_orange– candied_orange2020年06月11日 14:49:23 +00:00Commented Jun 11, 2020 at 14:49
-
@JimmyJames Now if I was doing validation in the constructor you'd have an overwhelming point since who wants to duplicate that? I suppose making it easy to do that later would be a good reason to do
this()
nowcandied_orange– candied_orange2020年06月11日 14:54:00 +00:00Commented Jun 11, 2020 at 14:54
It depends, but the usual alternatives are
make it a static member of a separate utility class
make it a member of a separate class (you will have to instantiate an object of that class somewhere)
put it into a common base class
TupleBase
which derives fromTuple
, and letTuple1
,Tuple2
derive fromTupleBase
.
And without providing more context and meaningless names like Tuple1
or utilityMethod
don't expect to get a more specific answer.
Have a look a Java abstract classes and see if that works for your current use case. An AbstractTuple or BaseTuple or GenericTuple can "fill-in" the methods you wish to have shared between implementations and leave abstract methods to be defined by concrete classes. This should generate a compiler warning and should be pretty safe to use.
When designing an application architecture, beware of side effects as more and more concrete classes depend on certain behaviour. Having variables track state and object factories with feature flags can help to address this issue in the future.
As an alternative to consider, have a TupleHelper or TupleUtils static class that takes a ITuple and performs various operations on the tuple. This helps separate behaviour into composable methods that can be chained together to achieve a goal.