3

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?

Guo
  • 177
  • 4
  • 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. Jun 10 '20 at 16:57
  • What about making the interface an abstract class? – Dirk V Jun 17 '20 at 13:39

3 Answers3

7

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.

candied_orange
  • 102,279
  • 24
  • 197
  • 315
  • 1
    I 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 for `default` methods but there's no way to make them private. – JimmyJames Jun 10 '20 at 20:32
  • @JimmyJames I find myself curious about that practice. Care to cite any arguments for it? – candied_orange Jun 10 '20 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 Jun 11 '20 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 Jun 11 '20 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()` now – candied_orange Jun 11 '20 at 14:54
  • @candied_orange I think I know what you mean. I've also tended away from more than a couple ways to construct a class. I've done the thing where there's say 5 parameters so there's 5 constructors with fewer and fewer parameters and more defaults. When I go back it's just a mess. In those scenarios the builder approach is superior. Of course, if named parameters are an option, then a lot of these issues go away. – JimmyJames Jun 11 '20 at 14:54
  • @JimmyJames which builder? We've been blessed with two. GoF's and Joshua Blochs. Joshs is wonderful at simulating named and optional arguments. Still wish Java would catch up with C# and add those. – candied_orange Jun 11 '20 at 14:56
  • @candied_orange Exactly, that's actually part of why the factory method is great as well. It frees you from some of the restrictions and limitations of what you can do in a constructor. For example, sometimes try-catch confuses the compiler as to whether a variable is deterministically assigned. – JimmyJames Jun 11 '20 at 14:57
  • Bloch's Builder. Sorry, seems like no one talks about the other Builder anymore. It's a great pattern, though. Totally underrated. – JimmyJames Jun 11 '20 at 14:59
  • GoF was totally weak on ALL "creational" patterns. Construction is a discipline in itself. I like anything that lets me seperate construction and behavioral logic. I don't like objects that know too much about building themselves. – candied_orange Jun 11 '20 at 15:01
  • Sorry if I'm missing your point but I like GoF builder for building views on a model. But it's not something I've seen widely used and a bit heady to explain well. Not possible in comments. – JimmyJames Jun 11 '20 at 15:08
  • Let us [continue this discussion in chat](https://chat.stackexchange.com/rooms/109199/discussion-between-candied-orange-and-jimmyjames). – candied_orange Jun 11 '20 at 15:10
6

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 from Tuple, and let Tuple1, Tuple2 derive from TupleBase.

And without providing more context and meaningless names like Tuple1 or utilityMethod don't expect to get a more specific answer.

Doc Brown
  • 199,015
  • 33
  • 367
  • 565
4

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.

fuhoi
  • 51
  • 2