1

I am doing some work to refactor a class. It currently a 'God class' and contains all different logic/operations solely in that class. One of my solutions is to extract all different parts of logic to their own classes. This will result in an 'Orchestrator' class calling all separate classes to execute logic. Example:

public class TripOrchestrator {

  public Trip build(TripInformation information) {

    final TripFee tripFee = new TripFeeBuilder(information.getFee());

    final TripTiming tripTiming = new TripTimingBuilder(information.getTripTiming());

    final TripSettings tripSettings = new TripSettingsBuilder(information.getSettings());

    final TripNotification tripNotification = new TripNotification(information.getNotification);

    return new Trip(tripFee, tripTiming, tripSettings, tripNotification);
  }
}

Before this refactoring, all of this logic was placed into one 'God class' without any builder classes.

My Question

Firstly is this a reasonable approach to go with for a problem such as this? Secondly, is it bad that I am violating DIP? For example,

final TripFee tripFee = new TripFeeBuilder(information.getFee());

TripFee object is a concrete class, so is TripFeeBuilder. All of these implementations are 'concrete' and not 'abstracted' to their interface. My thought on why this MAY be okay is that all of these builders and classes are very stable. It is rare that logic/functionality will change and if there is a change, it will be a very minor one.

DIP states that the above line should be:

final ITripFee tripFee = new TripFeeBuilder(information.getFee());

With 'ITripFee' being an interface and having TripFeeBuilder be one of the possible implementations. But like I stated, there most likely won't be another implementation of TripFee.

Would love thoughts/opinions about this. I have experience in Java but still a novice in design. Thanks.

  • 2
    Your approach is fine. Introducing unnecessary interfaces will not improve anything. The guideline to "depend on interfaces" just means you shouldn't depend on implementation details of the dependencies. Whether the type actually is a class or interface shouldn't matter. – JacquesB Feb 17 '19 at 19:34
  • Thanks for the feedback. – bob9123 Feb 17 '19 at 20:17
  • Trip and TripInformation share such a lot of information - are there actually differences between them? – Bernhard Hiller Feb 18 '19 at 9:16
  • @BernhardHiller My actual use case is more complicated than this. Structured it like above for ease of understanding – bob9123 Feb 18 '19 at 12:44
  • 1
    "Is it sometimes okay to intentionally violate the [...] Principle?" - The answer to that question is always yes. There are no principles in software engineering which you should never ever, under no circumstances, violate. – Christian Hackl Feb 18 '19 at 14:47
5

If you don't need an interface for polymorphism or for testing, don't create one. (and even if you create one for testing, you don't need to use it everywhere)

You can always extract an interface later, there's tools to do this 'automatically' in many IDEs.

|improve this answer|||||
  • I understand this but there is also the notion of 'design for extensibility'. So I thought that could change things. – bob9123 Feb 17 '19 at 19:24
  • 2
    It's a judgment call. If you think it's likely you'll need an interface in the near future, then go ahead and create one. It can also make sense when you're exposing some api to others which you'll want to keep stable in that case it can make sense to start off with an interface to avoid disruption later, even if you don't see the need for multiple implementations yet. But IMO, if it's all internal code, it's cheap enough to change later to just follow YAGNI – Bwmat Feb 17 '19 at 19:29
5

Your approach is fine.

DI does not say that depending on an interface is better than depending on a class.

The DI principle says you should depend on abstractions rather than low-level details. It is sometimes stated as "program to interfaces rather than implementation", but in this context "interface" refer to the public surface of a module or type, not specifically to interface declarations.

|improve this answer|||||
1

Is it sometimes okay to intentionally violate the Dependency Inversion Principle?

Yes. This is expected. It's rare to follow every principle the first time you get something working. If this must be changed later you've taken on something called technical debt. If this will only be changed once you're forced to change it like any other requirements change then you may have simply been practical. Following a principle should get you something. Never follow a principle when you can't see what that is. Do not program on faith. Follow the principles you understand and whose value you can see.

However, you can still make a mess either way. Allow me to give you a mini review.

DIP states that the above line should be:

final ITripFee tripFee = new TripFeeBuilder(information.getFee()).build();

Actually that's an issue with C# convention not a DIP thing. Following Java's convention this would be:

final TripFee tripFee = new TripFeeBuilder(information.getFee()).build();

ITripFee vs TripFee is a C# thing not a Java thing. Java uses TripFee vs TripFeeImpl. The nice thing is, here you don't even see TripFeeImpl and thus the source is not statically dependent on it.

Also, understand that the explicit interface keyword is a kludge around the buggy type system of these two languages whose creators didn't understand how to do multiple inheritance until it was to late to add it to the respective languages cleanly. Don't let either pollute your understanding of what it means to depend on abstractions rather then implementations. I can follow DIP with or without the interface keyword. So can you.

Doing without the interface keyword is easy if you don't have a need for multiple inheritance. One way is the old school template pattern. It's out of fashion but it still works. So long as nothing unmodifiable is using new on TripFee it can be made into an abstract class. Your different implementations can then inherit from it. Your factory gets to choose which.

Another way is composition. TripFee can take a parameter that knows how do do things it's expected to do and can delegate that work to that parameter. Of the three options this one involves the most keyboard typing but it's the most powerful. This method lets TripFee remain a concrete class but is no longer a fixed implementation.

By the way, if you follow Java's convention you haven't tipped your hand as to whether TripFee is an abstraction or a fixed implementation. That's a good thing. Clients have no business knowing which it is. All they need to know is how to talk to it.

Sadly, while following Java's convention helps you hide this knowledge in the source, changing from TripFee being a class to a keyword interface will require that everything that knows about TripFee be recompiled.

That usually isn't a big issue unless you have published binaries that you were hoping to update with a small patch.

|improve this answer|||||
  • Thanks for the thorough answer. Can you give an example of "I can follow DIP with or without the interface keyword". All examples I see of DIP (and many design patterns for that matter) rely on Java interfaces. Going of the basis of my example in my main question, if I wanted another implementation of 'TripTiming', how can I do that without an interface? – bob9123 Feb 17 '19 at 20:25
  • I can when I get back to my PC. Meanwhile look up abstract classes. For single inheritance they still work perfectly fine. What you're asking for is called polymorphism. It comes in many forms. – candied_orange Feb 17 '19 at 20:34
  • @bob9123 better? – candied_orange Feb 18 '19 at 7:03

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.