Java

Breaking Dependencies

Legacy code often has tangled dependencies that make testing and modification difficult. This article covers techniques to identify and break these dependencies safely, enabling incremental improvement.

📋 At a Glance

AspectDetails
GoalMake code testable by isolating dependencies
Key TechniqueExtract and inject dependencies
Safety NetCharacterization tests before changes
OutcomeTestable code, easier modifications

🎯 What You'll Learn

  • Identifying problematic dependencies (static, global, hidden)
  • Dependency breaking techniques (extract, parameterize, wrap)
  • Safe refactoring order for untested code
  • Dealing with singletons and global state
  • Interface extraction for seams

Production Story: The Untestable Monster

A 2000-line service class was impossible to test:

JAVA(27 lines)
Code
Loading syntax highlighter...
Problems:
  • Can't test without real database
  • Can't test without real Stripe
  • Can't disable emails in tests
  • Can't verify what was sent/saved

Mental Model: Dependency Breaking

TEXT(34 lines)
Code
Loading syntax highlighter...

🔬 Deep Dive

Technique 1: Extract and Override Factory Method

JAVA(44 lines)
Code
Loading syntax highlighter...

Technique 2: Parameterize Constructor

JAVA(45 lines)
Code
Loading syntax highlighter...

Technique 3: Wrap Static Calls

JAVA(47 lines)
Code
Loading syntax highlighter...

Technique 4: Break Singleton Dependencies

JAVA(68 lines)
Code
Loading syntax highlighter...

Technique 5: Extract Interface

JAVA(62 lines)
Code
Loading syntax highlighter...

Technique 6: Adapt Parameter

JAVA(46 lines)
Code
Loading syntax highlighter...

Technique 7: Preserve Signatures

JAVA(34 lines)
Code
Loading syntax highlighter...

⚠️ Common Mistakes

Mistake 1: Breaking Too Many Dependencies at Once

TEXT(11 lines)
Code
Loading syntax highlighter...

Mistake 2: Forgetting Backward Compatibility

JAVA(17 lines)
Code
Loading syntax highlighter...

🐛 Debug This: The Invisible Seam

A developer refactored legacy code to break dependencies, but the tests still can't mock the dependency. "I extracted a factory method like the book says, but I still can't override it in tests!"

JAVA(52 lines)
Code
Loading syntax highlighter...
The factory methods were extracted, but the tests still can't mock them. What went wrong?

✅ Solution:

Two critical mistakes:

Problem 1: Factory methods are private
JAVA
Code
Loading syntax highlighter...

Private methods cannot be overridden by subclasses. The factory method pattern only works if the method can be overridden in a test subclass.

Problem 2: Config is still a hidden dependency
JAVA
Code
Loading syntax highlighter...

Even if you could override the factory method, the Config singleton is still called.

Correct implementation:
JAVA(80 lines)
Code
Loading syntax highlighter...
The lesson: Factory methods must be protected (not private) to enable the subclass seam pattern. Better yet, use constructor injection with a default constructor for backward compatibility.

💻 Exercises

Exercise 1: Extract Factory Method

⭐ Difficulty: Easy | ⏱️ Time: 10 minutes

Task: Extract a factory method to make this class testable.
JAVA(13 lines)
Code
Loading syntax highlighter...
✅ Solution:
JAVA(56 lines)
Code
Loading syntax highlighter...

Exercise 2: Parameterize Constructor

⭐⭐ Difficulty: Medium | ⏱️ Time: 15 minutes

Task: Break the singleton dependency using constructor parameterization while maintaining backward compatibility.
JAVA(14 lines)
Code
Loading syntax highlighter...
✅ Solution:
JAVA(64 lines)
Code
Loading syntax highlighter...

Exercise 3: Wrap Static Calls

⭐⭐ Difficulty: Medium | ⏱️ Time: 15 minutes

Task: Make this class testable by wrapping the static calls.
JAVA(13 lines)
Code
Loading syntax highlighter...
✅ Solution:
JAVA(77 lines)
Code
Loading syntax highlighter...

Exercise 4: Extract Interface

⭐⭐⭐ Difficulty: Medium-Hard | ⏱️ Time: 20 minutes

Task: Extract an interface from this concrete class and refactor the dependent code.
JAVA(41 lines)
Code
Loading syntax highlighter...
✅ Solution:
JAVA(111 lines)
Code
Loading syntax highlighter...

Exercise 5: Full Dependency Breaking Workflow

⭐⭐⭐⭐ Difficulty: Hard | ⏱️ Time: 25 minutes

Task: Apply the full dependency breaking workflow to make this legacy class fully testable.
JAVA(33 lines)
Code
Loading syntax highlighter...
Break all dependencies and write comprehensive tests.
✅ Solution:
JAVA(277 lines)
Code
Loading syntax highlighter...

📝 Summary

TechniqueUse When
Extract Factory MethodDirect new in method
Parameterize ConstructorSingleton/static dependencies
Wrap Static CallsStatic utility methods
Extract InterfaceNeed to mock concrete class
Adapt ParameterComplex parameters (HttpRequest)
Preserve SignaturesAvoid breaking existing callers

📅 Review Schedule for This Article

DayTaskTime
Day 1Review Dependency Breaking Workflow diagram5 min
Day 3Redo Exercise 1 (Extract Factory Method)10 min
Day 7Identify 3 dependencies to break in your current codebase15 min
Day 14Redo Debug This (The Invisible Seam)10 min
Day 30Apply Parameterize Constructor to a real class20 min

Next: [Part 22: Strangler Fig Pattern]