1

I'm learning about the "coding to an interface to hide implementation details" design principle, and I'm confused over this idea. In my example, I have a WorkoutRoutine class that has a List of RoutineStep. By following the coding to an interface principle, my getWorkoutRoutine() method will return the List interface so that the implementation details of which list is used is hidden from the client.

What if I decide to change the implementation from a List of RoutineStep to an array of RoutineStep, won't disclosing the return type as List reveal to the client that a list was implemented and not an array, tree, graph, or any other data structure? How can I best encapsulate my data structure for holding a collection of RoutineStep, such that I can change the implementation of the data structure without the client code breaking if the List was changed to an array, tree, graph, etc?

public class WorkoutRoutine {
     
     private List<RoutineStep> workoutRoutine;

     public List<RoutineStep> getWorkoutRoutine() {
         //What if I later decide to change the data structure from a List to an array, tree,     
         //graph, set, map. What approach should I take so that the client code doesn't break
         //as they would have already coded to receive a List but after changing the 
         //implementation from a list to an array or any other data structure, their code would 
         //break.
     }
}
Wizard
  • 11
  • 2
  • You could define the return value of workOutRoutine to be a Collection – jr593 Mar 01 '21 at 07:47
  • 1
    Then you need to refactor things. Programming to an interface does not mean your code is 100% future proof to all changes. You also need to design up front, so you can imagine whether a workout routine can have a tree form or if it's always one step followed by another. – Kayaman Mar 01 '21 at 07:48

2 Answers2

1

decide to change the implementation from a List of RoutineStep to an array of RoutineStep

The ArrayList is just what you would choose – a List implementation backed by an array structure.

Later, you may find that your app is more often inserting elements into the middle of the list rather than appending to the end. So you decide to switch your choice of List implementation from ArrayList to LinkedList. By having your method return an object of the more general interface List rather than the more specific concrete classes of ArrayList and LinkedList, your change from one class to the other does not break calling code.

By the way, we generally do not use array in Java where we expect to need the features and flexibility of a List or Set from the Java Collections Framework. We generally use arrays only where we need to conserve RAM because of deploying to constrained devices or we need maximum performance. Arrays were also used for their more convenient compact literals, but the new List.of and Set.of methods fill that need.

without the client code breaking if the List was changed to an array, tree, graph, etc?

If you are making such a massive change to the data structures of your app, then no amount of encapsulation will mask that change. At some point, your design changes may indeed break existing code.

Such breaking changes may be a natural part of the early stages in an emergent design. This is a normal part of our work. Later, in an evolved design, proper use of encapsulation with help protect against smaller changes having wider impact than is necessary, will make your codebase less brittle.

Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
1

The idea is to return a type that is as generic as possible but specific enough.

For example returning a LinkedList may be too specific - if the client starts using the getFirstmethod and you later on decide to return an ArrayList for performance reasons, the client code will break. Hence the principle of returning a more generic type, such as a List.

You could even go more generic and return a Collection - it may make sense if you don't think the client code will need to access the n-th position in the collection for example. But if you feel that accessing the n-th step of the routine (routine.get(n-1)) without having to iterate is an important feature, it means that Collection is too generic for your use case.

assylias
  • 321,522
  • 82
  • 660
  • 783