1

How do you handle fat interfaces? Here is an example:

public class TSP
{
    public AddEmployeeContribution(...) {...}
    public AddMatchingContribution(...) {...}
    public CalculateTotal() {...}
    public CalculateEmployeeReturn() {...}
    public CalculateMatchingReturn() {...}
    public CalculateTotalCumulativeContributions() {...}
    public CalculateEmployeeCumulativeContributions() {...}
    public CalculateMatchingCumulativeContributions() {...}
    public CalculateTotalContributionByYear(int year) {...}
    public CalculateEmployeeContributionByYear(int year) {...}
    public CalculateMatchingContributionByYear(int year) {...}
    // numerous other functions related to Savings Plan
}

Using TSP as an aggregate root, it seems that the classes that the client interacts with grows unwieldy. I'm not sure how to avoid a fat interface with a single aggregate root.

5
  • Have you tried breaking out functions into multiple Interfaces. From a quick look at names, I can see two interfaces, one that has the Add functions and another that has Calculate functions. From there, in other aspects of the system, you interact with an interface instead of a instance of the class. It is then easier to break up the class but you still implement the interface. – DFord Dec 11 '17 at 15:05
  • That's a lot externally exposed. How many of those functions are supposed to be directly called by some external thing and how many are called internally by the implementation of your TSP? – Berin Loritsch Dec 11 '17 at 15:11
  • @DFord, so add two interfaces/classes like TSPCalculator(calculate methods) and TSPContributions (add/remove)? – keelerjr12 Dec 11 '17 at 15:12
  • @BerinLoritsch, all of them will be called by the client (UI). It's displaying a simulation of data with regards to retirement plans. – keelerjr12 Dec 11 '17 at 15:12
  • @keelerjr12, there are several repeating name formats. CalculateXXXTotal() and CalculateXXXContributionByYear(). Perhaps you can use one set of these methods with three implementations: one for Total, one for Employee and one for Matching. This cuts your interface down by a factor of 3. – Berin Loritsch Dec 11 '17 at 15:18
2

When looking at fat interfaces like you have there, the challenge is to find the right abstraction that will allow you to break it up in reasonable chunks. The approach I look at goes something like this:

  • Are there logical groupings that make sense as their own thing?
  • Are there patterns that suggest separate instances?
  • Is there any way I can hide some of the functions/methods?

In your particular case, it appears the second option might be a valid way to go. You have a repeating set of methods for Total, Employee, and Matching contributions. What that suggests to me is that you actually have three instances of one interface that would look something like this:

public class TSP 
{
    AddContribution(...) {...}
    Calculate(...) {...}
    CalculateReturn(...) {...}
    CalculateCumulativeContribution(...) {...}
    CalculateContributionByYear(...) {...}
    // ...
}

You might have a parent interface that lets you get at the specific instances:

public class AllTSP
{
    GetTotalTSP() {...}
    GetEmployeeTSP() {...}
    GetMatchingTSP() {...}
}

Feel free to move the Add() methods to the root interface instead of keeping it in the TSP interface. The consumer then calls your AllTSP instance like this:

tsp.GetTotalTSP().CalculateContributionByYear(...)

I hope this is at least helpful enough to get your thought processes working. I wrote the sample code without any assumptions on the language you were using. If your language has properties (like C#) then the getters I put in the base interface could be implemented as properties and look a bit cleaner.

Some people can get nervous when you chain calls like this. The only way to make it really work is to guarantee that there is an object at every step in the chain. As long as the chain is short (i.e. just a couple links) then you should be fine. The danger in method chaining is that if any of the methods can return null you will have a really hard time figuring out which one is failing. If you take proper precautions and are aware of the problems, you can make it safe.

8
  • thank you for the answer. I'm also using this project as a chance to practice Object Calisthenics, found here: [link]williamdurand.fr/2013/06/03/object-calisthenics/…. So I'm trying to follow the Law (Suggestion) of Demeter and although it's merely a suggestion, I feel it exposes a bigger underlying with my design. Maybe my design does not follow SOLID principles? It's important to note that not all TSPs have matching contributions, but ALL TSPs have employee contributions. So what I'm thinking now is that I use a base class called TSP and then create... – keelerjr12 Dec 12 '17 at 15:55
  • A derived class called MatchingTSP that extends the base class TSP. This breaks up the classes a little better. – keelerjr12 Dec 12 '17 at 15:59
  • The reason I'm not necessarily crazy about this is that I believe it will require me to cast between different interfaces/base-derived class based on if I want employee or matching data. – keelerjr12 Dec 12 '17 at 16:03
  • There's no reason why you couldn't have 3 different interfaces. Most object oriented languages have interface inheritance so you can make the calls consistent for a subset, and then add more specific methods to the purpose built interface. That way your EmployeeTSP doesn't have to be cast even though it has more methods than the subset that's in a BaseTSP interface. Since there's separate getters/properties for the specific total/employee/matching they can return the most specific interface for those groups. – Berin Loritsch Dec 12 '17 at 19:00
  • Another option is to adjust the responsibilities so that you still serve the same requirements, but do so in a way where you don't have to expose as many things in your interface. This is a design question, and has a much larger impact on how things are put together. I do prefer to have objects minimize how much they expose as much as possible. But I also understand that sometimes it's just not possible. – Berin Loritsch Dec 12 '17 at 19:02

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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