Принцип инверсии зависимостей (DIP) и внедрение зависимостей (DI) — связанные концепции, но они имеют разные значения. Давайте обсудим разницу между ними и приведем пример для каждого.

Принцип инверсии зависимостей (DIP)

Принцип инверсии зависимостей — это принцип проектирования, который гласит, что классы высокого уровня не должны напрямую зависеть от классов низкого уровня. Вместо этого как высокоуровневые, так и низкоуровневые модули должны зависеть от абстракций (интерфейсов или абстрактных классов). Этот принцип обеспечивает слабую связанность и модульность за счет разделения модулей и упрощения обслуживания и расширяемости.

Пример DIP:

Рассмотрим пример платежной системы. У нас есть класс PaymentService, который зависит от конкретного класса PaymentGateway.

public class PaymentService {
    private PaymentGateway paymentGateway;

    public PaymentService() {
        this.paymentGateway = new PaymentGateway();
    }

    public void processPayment(double amount) {
        paymentGateway.processPayment(amount);
    }
}

В этом примере класс PaymentService напрямую зависит от класса PaymentGateway, что нарушает принцип инверсии зависимостей. Если мы хотим изменить реализацию платежного шлюза или ввести новые платежные шлюзы, нам нужно изменить класс PaymentService.

Чтобы придерживаться принципа инверсии зависимостей, мы вводим абстракцию (интерфейс) PaymentProvider и зависим от этой абстракции как высокоуровневый PaymentService, так и низкоуровневый PaymentGateway:

public interface PaymentProvider {
    void processPayment(double amount);
}

public class PaymentGateway implements PaymentProvider {
    public void processPayment(double amount) {
        // Process payment using the payment gateway
    }
}

public class PaymentService {
    private PaymentProvider paymentProvider;

    public PaymentService(PaymentProvider paymentProvider) {
        this.paymentProvider = paymentProvider;
    }

    public void processPayment(double amount) {
        paymentProvider.processPayment(amount);
    }

    public static void main(String[] args){
        paymentProvider = new PaymentGateway();
        PaymentService paymentService= new PaymentService(paymentProvider);
        paymentService.processPayment(1000);
    }
}

Внедрив интерфейс PaymentProvider, класс PaymentService теперь зависит от абстракции, а не от конкретной реализации. Он больше не тесно связан с классом PaymentGateway, что упрощает переключение или введение новых платежных шлюзов без изменения класса PaymentService.

Внедрение зависимости

Внедрение зависимостей — это способ внедрить зависимость компонента фреймворка с помощью внедрения конструктора и внедрения сеттера, что обеспечивает слабую связь между классами.

Внедрение зависимостей конструктора (CDI). В этом случае DI будет внедряться с помощью конструкторов.

public interface UserRepository {
    void save(User user);
}

@Repository
public class UserRepositoryImpl implements UserRepository {
    public void save(User user) {
        // Save user to the database
    }
}

@Service
public class UserService {
    private UserRepository userRepository;

    // Dependency Injection via Constructor
    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public void registerUser(User user) {
        // Perform user registration logic
        userRepository.save(user);
    }
}

public class User {
    // User class implementation
}

Внедрение зависимостей установщика (SDI). В этом случае DI будет внедряться с помощью методов установщика.

@Service
public class UserService {
    private UserRepository userRepository;

    // Dependency Injection via Setter Method
    public void setUser(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public void registerUser(User user) {
        // Perform user registration logic
        userRepository.save(user);
    }
}

С помощью Autowire. В приложении Spring Boot вы можете использовать аннотацию @Autowired для внедрения зависимостей в ваши классы. Spring Boot автоматически создает и внедряет необходимые зависимости на основе аннотаций и конфигураций.

@RestController
public class UserController {
    
    @Autowired
    private UserService userService;

    @PostMapping("/users")
    public void registerUser(@RequestBody User user) {
        userService.registerUser(user);
    }
}

Заключение

Внедрение зависимостей больше сосредоточено на структуре кода, его основное внимание уделяется сохранению слабосвязанного кода. с другой стороны, внедрение зависимостей — это то, как функционально работает код. При программировании с помощью Spring Framework Spring использует внедрение зависимостей для сборки приложения.

Обратитесь по ссылкам для других концепций Spring Boot:

Ролевой доступ Spring Security с Spring Boot

Аутентификация и авторизация Spring Security с помощью JWT

Весенний учебник по АОП

Учебник по логированию Spring Boot

Централизованная конфигурация в Spring Boot

Пользовательские проверки в Spring Boot

Джавинпол Доменико Николи Трей Хаффин Мехмет Арсл Мехмет Озкая Образовательная команда