Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why injecting classes instead of interfaces is considered bad practice?

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 ?

like image 220
Adelin Avatar asked Jun 15 '16 07:06

Adelin


2 Answers

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. (Interface Segregation Principle.)

like image 90
J. B. Rainsberger Avatar answered Oct 20 '22 13:10

J. B. Rainsberger


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

  1. your class is very likely to not change over time
  2. you class isn't final, and the methods you might want to mock aren't final either

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.

like image 23
GhostCat Avatar answered Oct 20 '22 13:10

GhostCat