jeudi 25 juillet 2019

Sub-classes that add new methods

I'm writing a class Tracker that will expose to the client methods to get the current status of user's training, like distance, pace, calories, etc. Imagine this values as getters.

class Tracker{
  float getDistance();
  float getTime();
  float getCalories();
}

Now, thinking ahead, I may discover a way to also get the elevation, and then maybe (not at the same time) the number of steps, so my problem is how to better solve this design.

First thought, classic inheritance 

My first thought was to just subclass this interface, so I'll end up with

class ElevationTracker extends BaseTracker{

   float getElevation();

}

But then, I may want to add a StepTracker, extending the ElevationTracker so I have both stats.

class StepTracker extends ElevationTracker{

   float getStepsCount();

}

This looks a bit weird to me, because the StepTracker now implicitly provides the Elevation stat, and it could have been the other way around, the ElevationTrackerextending StepTracker, in this case it's just a matter of which feature I discover first.

Also, I'm not completely sure if this inheritance is consistent with the specialization philosophy

A single class for everything 

Another idea, maybe the simplest, is to have just one class Tracker and any time I want to add a new feature, change this class by adding a new method to it to retrieve this feature's information; and then the client can update its code to use this new features. So for example, next month I change the Tracker class and it looks like this

class Tracker{
  float getDistance();
  float getTime();
  float getCalories();

  //This is new
  float getElevation();
}

I think this solution like if I had thought of this Elevation feature before (The first time I created Tracker class), I would have added this last method from the beginning.

I do it now just because now the "requirements have changed"

Making each feature a class

Another thought was to not think every feature as the methods of a class, but as a class on its own, meaning that I would have an interface like Tracker

interface Tracker {

  float getValue();

  /** Maybe some other methods */

}

and then have a class for each feature

  • DistanceTracker
  • TimeTracker
  • CaloriesTracker
  • ...

So then I would just add a new ElevationTrackerand a StepTrackerthat are independent from each other.

The problem here is that there are some features that depend on others, like for example PaceTracker and CaloriesTracker may depend on DistanceTracker, so they will probably need receive an instance of DistanceTracker.

Also, the client code may become a bit messy, having to hold an instance for each feature. And the most important pitfall I see is that I would usually use all this trackers together, I probable won't use just the DistanceTracker or just the ElevationTracker, so maybe there's no benefit in having each feature separately

Conclusion

I would like to know which of this options is the best, or if there's another better option. Maybe I can reconsider one of them with some tweaks or adding a design pattern to improve it.

In my opinion, the Single class option provides faster development, considering that, although the others make use of OOP features, they just move the problem of updating a class already written from the Tracker class to another client or intermediate class.

Aucun commentaire:

Enregistrer un commentaire