8

Let's say I have an object A, which is too big(having too many methods and variables). So, I break it down to smaller objects. After that, I have Object A, B, and C. Obviously, Object A is from Class A, Object B is from Class B and so on.

Unfortunately, I got dependencies: A depends on B, and B depends on C. Ultimately, I would use Object A only. From that situation, I have two scenarios:

  1. create an Object C in Class B, and create an Object B in Class A. Then, create Object A in main method.
  2. create Object A, B, and C through dependency injection in main method.

Generally, is it better to use scenario #1 to hide dependency or scenario #2 to expose dependency?

By the way, I would prefer to see Java code in case anyone would provide code example.

2
  • 2
    Explicit is better than implicit... Commented Jun 30, 2016 at 23:34
  • 1
    It depends on what's involved with creating B and C. Commented Jul 1, 2016 at 2:47

3 Answers 3

10

Is it better to expose or hide dependency in OOP?

It's best to do both.

Before I explain that let me explore the problems with your proposals.

Let's say I have an object A, which is too big(having too many methods and variables). So, I break it down to smaller objects.

This is good. Always break objects down until they have only one responsibility, that is, only one reason they would ever change. That doesn't mean they can have only one method or dependency, but they should center around one idea.

  1. create an Object C in Class B, and create an Object B in Class A. Then, create Object A in main method.

Depending on something isn't the same as knowing how to create it. This idea requires that A (and B) do both. Didn't we break up A because it was doing to much in the first place?

I'll make exceptions for value objects (Strings and such) but generally I like to separate use from construction.

  1. create Object A, B, and C through dependency injection in main method.

This sounds fine, A uses B but doesn't build it. B uses C but doesn't build it. What about applications so big they need more than 26 letters of the alphabet to give them unique names? Main can suddenly become a busy place.

There are a host of dependency injection frameworks that would love to solve this problem for you if you'll just sell your soul to them. That is, become dependant on them.

If you're a bit stubborn you can use creational patterns. The GoF ones still work. You can also use the newer creational patterns, The Josh builder works well in Java and other named parameter impaired languages. There are other builders that let you have amazing levels of control over creation if you're willing to put in the effort.

This goes as far as internal Domain Specific Languages (iDSLs). These let you essentially define your own language inside Java (or most any other) and Java's compiler will compile it for you. This gives you complete control the creation and injection of objects. But it can demand considerable work. Tread lightly here because this rabbit hole is deep.

This brings me to my "do both" recommendation.

A very successful habit I've developed is to accept everything as an injection in my behavior objects, but build them with specialized injectors that know good default values that take few or no injections themselves. You can have, say, 5 such injectors for the same class that build it in 5 different ways. You can have one injector that builds 5 behavior objects all nicely wired together. You can even inject injectors into injectors and have them weave together a behavoir object graph.

Identify good default values if they exist and use those to make your basic injector. Some tend to use static methods as injectors but they can be full blown objects themselves. That's actually more powerful.

That's how you "do both". Dependencies are exposed for behavior objects. Creational objects hide dependencies, doing the work that would otherwise be spread all over main.

What this resolves is a fundamental tension between good machine API and human usability. What makes for a flexible injectable class doesn't make for a good human usable class constructor. When I find myself wanting to make building an object easier I reach for another object to do that work. I don't distract the behavior object with it's own creational details.

I've used different dependency injection frameworks and came away feeling that all they really ended up doing for me was expressing object creation in another language (be that xml or @annotations). Turns out you can do it all in java. Pick good names so people know which kind of class they're in and it's just as maintainable.

Done this way, a look in main will show the linking together of a few canned object graphs and one call to a method on one of those objects to start the whole thing ticking. That's the one place I mix creation and use. You have to do it somewhere.

Like anything this can be overdone, but with the right balance it makes coding a bit simpler.

2
  • Pardon me, I wasn't trying to nitpick. It just caught my eye. :)
    – Andy
    Commented Jul 1, 2016 at 11:36
  • @DavidPacker not at all. Please nitpick. Constructive criticism that leads to edits is most welcome. Commented Jul 2, 2016 at 3:29
1

What you said make me think why Object A is that big.

Instead of thinking about dependencies and injection I'd concentrate about composition over inheritance which allows late just-in-time object creation only if needed.

If you're worried about dependency exposition it could be managed by exposing interfaces instead of internal classes.

0

It depends. If B and C only support A and are useless/meaningless in any other context, hide them (encapsulate them in A). If they are entities that mean something in your top level problem domain already, independent from you use of A, you as the user may want to create them yourself and inject them for added flexibility.

Example: class EmailSender.

Scenario 1 You need to send email. You do not care about the way email is sent, you want to provide From, To, Subject and BodyText and be done with it. Starting a thread and opening a socket, choosing a port, you don't care, it is just noise to you. All you care about is getting out a notification about some status change in your system. So you do method 1.

Scenario 2 You need to send email. This time you do care about the details because your application is a bulk mailer and you need the control to set priority, memory use and routing information. So you do method 2.

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.