In my recent article (Static X OOP), I suggested that regular classes and interfaces are more effective than utility classes' static methods, particularly when it comes to validation. There are distinctions, though, much like with other software development tasks. I want to look at a situation today where static methods—more especially, static factory methods—can be a useful and sophisticated tool. Inspired by Joshua Bloch's 'Effective Java,' let's see how I utilize them to improve the testability and flexibility of my code.
The Right Tool
'Effective Java' teaches us to strategically employ static factory methods instead of constructors when applicable. The ability to retrieve subtypes, give descriptive names, and manage instance generation are just a few benefits of these methods. They've been especially helpful to me in managing dependencies like the JapeContext in my work extending ERP platforms.
public static BlockRepository getInstance() {
return new BlockRepositoryImpl(new BlockRepositorySqlExecutorImpl(() -> {
try {
return JapeUtils.executeWithJape(context -> context); // Get JapeContext
} catch (SQLException e) {
throw new RuntimeException("Error getting JapeContext", e); // Handle exception
}
}));
}
In this case, BlockRepositoryImpl
instances are created using a controlled entry point provided by the getInstance()
method, which functions as a static factory. Using this method enables me to incorporate the intricate reasoning involved in acquiring the JapeContext
into the factory method. Using a BlockRepositorySqlExecutorImpl
and a lambda function, I can postpone retrieving the context until it's truly required. This type of deferred initialization can boost efficiency.
But the real magic happens when it comes to testing. By using a static factory, I can easily provide a mock JapeContext
during unit tests. For instance, I can create a test-specific getInstance()
method that returns a BlockRepositoryImpl
instance with a mocked BlockRepositorySqlExecutorImpl
. This allows me to isolate the BlockRepositoryImpl
and test its behavior without relying on the actual ERP platform.
The flexibility of the code is also improved by the use of this pattern. All I have to do is alter the getInstance()
method if I ever need to change how the JapeContext
is retrieved. BlockRepositoryImpl
callers are not impacted. This is a powerful demonstration of the Open/Closed Principle.
Takeaway
Static factory methods provide an alternative to static utility classes, which frequently result in rigid and untestable code. They offer a flexible and controlled method for managing dependencies, improving testability, and creating instances. We may create code that is reliable and manageable by using the right tool for the right job.
Have you used static factory methods in your projects? What insights and experiences do you have? Leave a comment below with your opinions! Let's continue the discussion and learn from each other.
Top comments (0)