To już ostatnia zasada SOLID, mówi, że moduły wyższego poziomu (zawierający skomplikowaną logikę, interfejsy, abstrakcja) nie powinny zależeć od modułów niższego poziomu (od zmian dokonanych w modułach niższego poziomu, niski poziom to detale) , oba powinny zależeć od abstrakcji np. interfejsów. Abstrakcje nie powinny zależeć od detali (konkretnych implementacji). Detale powinny zależeć od abstrakcji.
Abstrakcja to np. interfejsy albo klasy abstrakcyjne czyli takie struktury, których obiektów nie możemy stworzyć, nie są zbyt konkretne.
Żeby lepiej zobrazować zasadę spójrzmy na przykład.
public class MySQLDatabase{
public void getData(){
//gets data from database
}
public void saveData(){
//gets data from database
}
}
public class AddressBook{
public MySQLDatabase msqlDB;
public AddressBook(MySQLDatabase msqlDB){
this.msqlDB = msqlDB;
}
public void showRecordsFromDB(){
msqlDB.getData();
}
public void saveRecordsToDB(){
msqlDB.saveData();
}
}
public class App
{
public static void main( String[] args ) {
MySQLDatabase msqldb = new MySQLDatabase();
AddressBook ab = new AddressBook(msqldb);
ab.showRecordsFromDB();
}
}
Mamy klasę AddressBook, w której mamy pole msqlDB, które jest obiekten typu MySQLDatabase. Aplikacja działa dobrze, jednak narusza zasadę odwrócenia zależności. Klasa AdressBook (moduł wysokiego poziomu) jest zależne od klasy niższego poziomu MySQLDatabase. Narusza on również zasadę by otwarte/zamknięte bo co w momencie, gdy potrzebne było jednak skorzystanie z innej bazy danych?
By naprawić nasz program najprostszym sposobem jest utworzenie abstrakcji- interfejsu DatabaseInterface, zamiast korzystać z konkretnej klasy. Tym sposobem każda klasa bazy danych, którą będziemy chcieli użyć, będzie mogła zaimplementować ten interfejs. Będziemy też mogli użyć typu DatabaseInterface dla naszego argumentu w konstruktorze.
public interface DatabaseInterface {
public void getData();
public void saveData();
}
public class MySQLDatabase implements DatabaseInterface{
public void getData(){
//gets data from database
}
public void saveData(){
//gets data from database
}
}
public class OracleDatabase implements DatabaseInterface{
public void getData(){
//gets data from database
}
public void saveData(){
//gets data from database
}
}
public class AddressBook{
public DatabaseInterface database;
public AddressBook(DatabaseInterface database){
this.database= database;
}
public void showRecordsFromDB(){
database.getData();
}
public void saveRecordsToDB(){
database.saveData();
}
}
public class App
{
public static void main( String[] args ) {
DatabaseInterface msqldb = new MySQLDatabase();
DatabaseInterface oracledb = new MySQLDatabase();
AddressBook ab1 = new AddressBook(msqldb);
AddressBook ab2 = new AddressBook(oracledb );
ab1.showRecordsFromDB();
ab2.showRecordsFromDB();
}
}
Teraz nasza klasa AddressBook nie zależy od konkretnej klasy bazy danych, a od interfejsu czyli od abstrakcji. W ten prosty sposób zachowaliśmy zasadę Dependency inversion.