Reputation: 18961
Injecting classes into other classes is considered bad, some of the arguments is that it is hard to mock a class and it couples the objects together.
But I see many developers do this everyday and most of the mocking frameworks are very good at mocking classes and provide test mocks so what is the problem ?
Upvotes: 11
Views: 2575
Reputation: 1203
Abstract types (like interfaces) tend to be more stable (change less over time) than concrete classes. It is generally less risky to depend on more-stable things. Put these together, and it's generally less risky to depend on interfaces compared to concrete classes.
I also find it easier to detect significant changes in behavior when I depend more on interfaces. When an interface has to change, something significant is happening; and when something insignificant is happening that (surprisingly) causes an interface to change, then I interpret that as a signal to raise the level of abstraction in the design. With concrete classes, this signal doesn't "sound" quite as crisp, clean, and obvious to me.
Injecting a concrete class creates far fewer risks than instantiating them directly inside the client module. If you're not worried about the risk of injecting concrete classes, then do it for a while and wait for the risk to become a problem. You can decide then how much you mind. Fortunately, extracting interfaces from concrete classes is pretty safe, as refactorings go. Just remember, when it comes time, to extract the smallest interface that the client needs, and don't mindlessly put every concrete class method onto the interface.
Upvotes: 12
Reputation: 140457
Certain (influential) people, like Robert Martin claim that using the names of concrete classes in your source code is basically something that you should avoid in order to prevent "too tight coupling".
Let me quote from his book on "Agile principles":
High-level modules should not depend on low-level modules. Both should depend on abstractions. Abstractions should not depend upon details. Details should depend upon abstractions
and further:
Consider the implications of high-level modules that depend on low-level modules. It is the high-level modules that contain the important policy decisions and business models of an application. These modules contain the identity of the application. Yet when these modules depend on the lower-level modules, changes to the lower-level modules can have direct effects on the higher-level modules and can force them to change in turn. This predicament is absurd! It is the high-level, policy-setting modules that ought to be influencing the low-level detailed modules. The modules that contain the high-level business rules should take precedence over, and be independent of, the modules that contain the implementation details. High-level modules simply should not depend on low-level modules in any way.
But of course, in reality, there is always balancing. When you are very much convinced that
then there is not much sense in preferring an interface over a class.
But - if in doubt, interfaces are the better way to go.
I have to explain people far too often that their idea of using PowerMock in order to allow mocking some final classes/methods is the wrong approach; and the much better answer is to change the type of a parameter to some interface instead.
Upvotes: 6
Reputation: 9009
The most obvious reason I have seen in practice is that you can lose capabilities of the DI framework that are sometimes essential for the proper functioning of your application. Furthermore, the issues may not become obvious until later on, when the code is happily executing on production.
For example, spring framework (undoubtedly among the top DI frameworks in the Java world) relies on using the decorator pattern to dynamically implement interfaces that are a subject to AOP. It dynamically creates a class that implements from your interface, the class has your original bean instance as a private member and decorates the relevant members with the AOP logic.
When injecting the class directly, you disable the possibility for the DI framework to perform the AOP magic, as the resulting type of the dynaimic implementation will not be assignable to the concrete class you have requested (I am not sure how spring will do this - it will either create the decorator and complain it is unassignable to the injected member type, or worse - inject the non-decorated member that is stripped of AOP capabilities). Losing the AOP capabilities of an injected bean may result in an incorrect behaviour of the injected class.
Notable examples never to use classes for direct injection are persistence services that rely on the transaction management trough the @Transactional
annotations. If somehow such a service gets injected, and has no AOP, then you could end up corrupting your database as the transaction manager will not be around to clean up your mess when an exception is thrown. Of course, you will realize the consequences of this later on, when the application users complain, or when the application itself misbehaves.
Some people could object and recommend to use cjlib along spring's AOP instead of the default AOP behavior. This has the effect of making Spring extend your AOP-enabled class instead of using the decorator pattern. While this allows you to inject a class, and its AOP to still work, this approach is considered a bad practice and is mostly rejected by now in favor of the decoration approach. Among the various reasons for this are:
final
, cjlib will fail. Decoration can handle this better.Upvotes: 2