2

I have two classes that are similar in nature but they have different functional signatures. I am considering between having two interfaces vs having one. Let me illustrate:

Approach 1: two interfaces

public interface RaceCar {

    CompletableFuture<Double> drive(final Wheel arg1, Tactic tactic);

}

public interface Bus {

    CompletableFuture<Double> drive(final String someOtherKey, final Controller arg2);
}

One abstract class AbstractCar to share logic between the two:

public abstract AbstractCar { 
    // Add shared logic here... 
} 

And the implementation.


public class RaceCarImpl extends AbstractCar implements RaceCar {
... 
}


public class BusImpl extends AbstractCar implements Bus {
...
}

Results: 2 interfaces, 1 abstract, 2 implementation = 5 files.

Approach 2: One interface, two methods

public interface Vehicle { 

    CompletableFuture<Double> drive(final Wheel arg1, Tactic tactic);

    CompletableFuture<Double> drive(final String someOtherKey, final Controller arg2);

} 
public abstract AbstractVehicle implements Vehicle {
    // Sharing code here 
} 
public class RaceCar extends AbstractVehicle { 

    CompletableFuture<Double> drive(final Wheel arg1, Tactic tactic) { 
        // Implement this ... 
    } 

    CompletableFuture<Double> drive(final String someOtherKey, final Controller arg2) { 
        throw new IllegalStateException("Not implemented");  
    } 

} 


public class Bus extends AbstractVehicle { 

    CompletableFuture<Double> drive(final Wheel arg1, Tactic tactic) { 
        throw new IllegalStateException("Not implemented");  
    } 

    CompletableFuture<Double> drive(final String someOtherKey, final Controller arg2) { 
        // Implement this 
    } 

} 

Results: 1 interfaces, 1 abstract, 2 implementation = 4 files.

Verdict

I think having 1 interface is superior because it requires less files. What are your thoughts?

Which approach above incurs less technical debt in the long term?

2
  • 5
    Requiring less files is a terrible reason, if a vehicle can only be driven a certain way, I would be annoyed if it "lied" about being able to drive the other way then threw an error. There are legitimate reasons for throwing an error (like if the wheels fell off), but it shouldn't be a way to jam a bunch of methods into a single interface Commented Oct 4, 2022 at 19:21
  • This question is not opinion based. The interface segregation principle tells which option is most suited and on an objective base. Commented Oct 5, 2022 at 5:48

2 Answers 2

5

Option 2 is terrible. Your objects are lying about what functionality they support. Reducing the number of files is nowhere near enough of a justification for that.

Option 1 is better, but consider option 3: just don't write those interfaces at all.

It doesn't sound like you have any other classes that would implement those interfaces. The names "RaceCarImpl" and "BusImpl" strongly suggest that those are the only implementations. In that case, just use the classes directly.

If you later want to write more classes that actually share a common API, you'll have a better understanding of what your interfaces should look like - what methods really generalize, what methods are specific to particular implementations, etc. You can refactor any code that took concrete classes to take meaningful interfaces then.

Sign up to request clarification or add additional context in comments.

2 Comments

Can you explain why option 1 is better?
@Tinker: Because with that option, you're not lying about what functionality your objects support. Someone with a RaceCar knows they can use RaceCar methods, instead of having to know the concrete class they're working with.
2

You should create interfaces that represent the “abilities” you want your classes to have. In your case the second example is correct: What is common amongst vehicles? You can drive them. Therefore a vehicle is “Drivable”. Bus and RaceCar are implementations of Drivable. They can also be “Paintable”, “Washable”, etc…

4 Comments

And please get rid of the “Impl” in your class names. You should be able to name that class based on what it does.
But these cars aren't both "drivable". The method names are completely misleading. These cars support drive methods with entirely unrelated signatures that just happen to share a name, and they cannot be used the same way.
Just FYI I had to replace the class names from my production code.
"These cars support drive methods with entirely unrelated signatures that just happen to share a name" - then you may be better of to use two differnt names for those methods?

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.