Search code examples
javaspringsolid-principles

interface segregation SOLID principle in Spring services


I keep my service interfaces as lean are possible, normally they are @FunctionalInterface. I try to follow there the interface segregation principle.

Is a good practice that my service implementation implements multiple interfaces if they share most of there dependencies? or should I create a separate implementation for each interface?

// find the T scheduled between the given instants.
interface TaskPeriodFinder<T> {
   List<T> find(Instant initial, Instant end);
}

// schedule a task to be execute in a given time.
interface TaskScheduler<T> {
   void scheduleOn(T task, Instant scheduledOn);
}

// execute the scheduled task when the scheduledOn is reached.
interface TaskExecutor<T> {
   void execute(T task);
}

@Service 
class TaskService implements TaskScheduler<String>, TaskExecutor<>, TaskPeriodFinder<> {
   private final TaskFinder taskFinder;
   private final TaskCreator taskCreator;

   public void scheduleOn(String task, Instant scheduledOn) {
      // TODO
   }
   public void execute(String task) {
      // TODO
   }   
   public List<T> find(Instant initial, Instant end) {
      // TODO
   }
}

In my example; is a good practice that TaskService implement these two interfaces? or should I have a separate class for each interface?


Solution

  • There's no universal rule in how to apply the ISP. It's all about crafting useful abstractions for the clients and finding the right balance. If all the clients are using operations A & B together and these are cohesive then there's no real value gained by segregating the behaviors.

    Being overly granular may force clients to depend on multiple "views" of the very same concept to achieve their goal and at the other end of the spectrum, being too coarse forces the clients to depend on much more than they need.

    However, if you just end up bundling fine grained interfaces in a larger facade and just rely on the facade then your granular interfaces aren't helpful in any way.

    Unless you are building a library where flexibility must be baked in from the very start I'd suggest to let the abstractions appear by themselves based on actual usage rather than going for one method interfaces by default and refactor as you go.

    Finding the proper domain abstractions is usually an iterative process and rarely done right from the start. For instance, Java designers failed to recognize early they should have had a read-only interface for lists.