Reputation: 1025
I can't understand the difference between the annotation over the interface and the implementation. I've searched a lot of information it's not complete and it doesn't seem right to me. I decided to look into the question.
Consider
1 Variant.
Interface
@Service
public interface AuthenticationService {
boolean authenticate(String username, String password);
}
Impl
public class InMemoryAuthenticationService implements AuthenticationService {
@Override
public boolean authenticate(String username, String password) {
//...
}
}
And
@Autowired
private AuthenticationService authService;
Many sources, including stackoverflow, say that there are 2 key problems with this approach
2 Option
Interface
public interface AuthenticationService {
boolean authenticate(String username, String password);
}
Impl
@Service
public class InMemoryAuthenticationService implements AuthenticationService {
@Override
public boolean authenticate(String username, String password) {
//...
}
}
And
@Autowired
private InMemoryAuthenticationService authService;
In my practice, I always created an interface for the service and injected the interface. But now I've started to see the second option sometimes.
Questions.
Can you describe the pros and cons of both options in more detail? Does it even make sense to use the interfaces then, if they only have 1 implementation?
Upvotes: 2
Views: 878
Reputation: 2169
It is all in relation with two principal design : Open to extension closed to modification , and dependencies injection.
The main idea of Open closed from wekipedia : In object-oriented programming, the open–closed principle states "software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification";[1] that is, such an entity can allow its behaviour to be extended without modifying its source code.
For the dependencies injection on spring , it is from Baeldung : Dependency Injection is a fundamental aspect of the Spring framework, through which the Spring container “injects” objects into other objects or “dependencies”. Simply put, this allows for loose coupling of components and moves the responsibility of managing components onto the container.
So as example , you have an application that read some data from an Oracle DataBase the implementation will be like that :
public interface ReadDataFromSource{
public abstract List<String> getData();
}
The implementation will be
@Service
public class ReadDataFromOracleDB implements ReadDataFromSource {
public List<String> getData(){
// code to read data from Oracle DB
}
}
For some reason the client need to change the implementation to read the data from a MysqlDataBase , the idea is to dont change the existen code ( closed to modification) , we should here add a new class that implements the same interface ReadDataFromSource
:
@Service
public class ReadDataFromMysqlDB implements ReadDataFromSource {
public List<String> getData(){
// code to read data from Mysql DB
}
}
The call ( reference ) should be using the interface to garantee that the instance can be from any class that implements the interface. And like that we have the two version in our software , using Qualifiers or @primary , naming the beans and call using the names ; will inform the framework about any bean will be used in a given place . The first version (Oracle DB) still exist if we need to use it.
@Autowired
private ReadDataFromSource dataReader;
Upvotes: 1