15

Our knowledge domain involves people walking over a pressure-recording plate with their bare feet. We do image recognition which results in objects of the 'Foot' class, if a human foot is recognized in the sensor data.

There are several calculations that must be performed on the foot's data.

Now, which API would be better:

class Foot : public RecognizedObject  { 
  MaxPressureFrame getMaxPressureFrame();
  FootAxis getFootAxis();
  AnatomicalZones getAnatomicalZones();

  // + similar getters for other calculations

  // ...
}

Or:

class Foot : public RecognizedObject {
  virtual CalculationBase getCalculation(QString aName);

  // ...
}

Now, there are a lot of pro's and con's I can come up with, but I can't really decide which are the most important. Note, this is an end-user application, not a software library we sell.

Any advice?

Some pro's for the first approach could be:

  • KISS - everything is very concrete. The API, but the implementation as well.
  • strongly typed return values.
  • inheriting from this class is fool-proof. Nothing can be overridden, only added.
  • API is very closed, nothing goes in, nothing can be overridden, so less can go wrong.

Some con's:

  • The number of getters will grow, as every new calculation we invent gets added to the list
  • API is more likely to change, and if breaking changes are introduced, we need a new API version, a Foot2.
  • in case of re-use of the class in other projects, we might not need every calculation

Some pro's for the second approach:

  • more flexible
  • the api is less likely to change, (assuming we got the abstraction correct, if not, changing will cost more)

Some con's:

  • loosely typed. Needs casts on every call.
  • the string parameter - I have bad feelings about that (branching on string values...)
  • There is no current use case/requirement that mandates the extra flexibility, but there might be in the future.
  • the API imposes restrictions: every calculation needs to derive from a base class. Getting a calculation will be forced through this 1 method, and passing extra parameters will be impossible, unless we devise an even more dynamic, super-flexible way of passing parameters which increases complexity even more.
4
  • 5
    You could make an enum and switch on it's values. Still, I think the second option is evil, because it deviates from KISS.
    – Vorac
    Commented Jan 28, 2014 at 14:37
  • Harder to find all usages of a specific calculation if you use a string parameter to trigger it. Hard to be be 100% you found them all. Commented Jan 28, 2014 at 16:09
  • Take the best of two worlds and write a façade with a bunch of getters invoking getCalculation().
    – nalply
    Commented Jan 30, 2014 at 23:06
  • 1
    Enums are definitely better than strings! I had not thought of this. They restrict the API and prevent abuse of the string parameter (like concat of tokens and other crap) So I guess the comparison is between option 1 and option 2 with enum instead of string.
    – Bgie
    Commented Feb 10, 2014 at 8:31

2 Answers 2

6

I think in first approach will pay off. Magic strings can create the following issues: Typing errors, misuse, non-trivial return type safety, lack of code completion, unclear code (did this version have that feature? i guess we'll find out at runtime). Using enums will resolve some of those issues, but let's look at the cons you raised:

  • The number of getters will grow, as every new calculation we invent gets added to the list

True, that can be annoying, however it keeps things nice and strict, and gives you code completion anywhere in your project in any modern IDE, with good heading comments that is much more useful than enums.

  • API is more likely to change, and if breaking changes are introduced, we need a new API version, a Foot2.

True, but that's actually a huge pro ;) you can define interfaces for partial APIs, and then you don't need to re-compile dependent class that are not affected by newer APIs (So no need for Foot2). That allows for better decoupling, the dependency is now of the interface and not the implementation. Moreover if an existing interface changes, you will have a compilation error in dependent classes, which is great to prevent stale code.

  • in case of re-use of the class in other projects, we might not need every calculation

I don't see how using magic strings or enums will help with that... If I understand correctly, either you include the code in the Foot class or you break it down to a few smaller classes, and that goes for both options

2
  • I like the partial APIs idea with interfaces, it seems future-proof. I will go with that one. Thank you. If it should becomes too cluttered (foot implementing too many interfaces), using several small adapter classes would be even more flexible: If several variations of foot exist with different api's (like foot, humanfoot, dogfoot, humanfootVersion2) there could be a small adapter for each to allow one GUI widget to work with all of them...
    – Bgie
    Commented Feb 10, 2014 at 9:04
  • An advantage of using a command selector is that one can have an implementation that receives a command it doesn't understand call a static helper method provided with the interface. If the command represents something that can be done with nearly all implementations using a general-purpose approach, but which some implementations might be able to do via better means [consider e.g. IEnumerable<T>.Count], such an approach may allow code to enjoy the performance benefits of new interface features when using implementations that support them, but stay compatible with old implementations.
    – supercat
    Commented Jul 15, 2014 at 22:20
12

I would recommend option 3: Make it clear that the calculations are not an intrinsic part of the abstraction of a Foot, but operate on it. Then you can split Foot and the calculations into separate classes, like this:

class Foot : public RecognizedObject {
public:
    // Rather low-level API to access all characteristics that might be needed by a calculation
};

class MaxPressureFrame {
public:
    MaxPressureFrame(const Foot& aFoot); // Performs the calculation based on the information in aFoot
    //API for accessing the results of the calculation
};

// Similar classes for other calculations

This way, you still have strong typing of your calculation, and you can add new ones without affecting existing code (unless you made a serious error in the amount information exposed by Foot).

4
  • Definitely nicer than options 1 and 2. This is only a short step away from making use of the strategy design pattern.
    – Brian
    Commented Jan 28, 2014 at 17:59
  • 4
    This might be appropriate. This might be overkill. Depends on how complex the calculations are. You going to add a MaxPressureFrameStrategy and a MaxPressureFrameStrategyFactory? And it tends to turn Foot into an Anemic Object.
    – user949300
    Commented Jan 28, 2014 at 21:45
  • Though this is a nice alternative, reversing the dependencies would not work in our case. The foot also has to act as some kind of mediator, as some calculations need to be recalculated if another one changed (due to the user changing a parameter or so).
    – Bgie
    Commented Feb 10, 2014 at 8:34
  • @Bgie: With such dependencies between calculations, I agree with @user116462 that the first option is best.` Commented Feb 10, 2014 at 9:27

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.