Thật sự trìu tượng

Lần đầu mình tiếp xúc với khái niêm tiêm phụ thuộc (DI - Dependency Injection) là khi mới ra trường. Thật sự là nó quá khó hiểu. Lúc đó mình cũng chỉ hiểu DI là cái gì đó tương đương với @Inject annotation của JavaEE hay một cái file cấu hình xml của Spring. Và chỉ đến khi làm thư viện, mình mới thực sự hiểu, à hoá ra nó cũng chỉ là một cách thức khởi tạo đối tượng thay thế cho hàm setter mà thôi. Tuy nhiên đơn giản không có nghĩa là nó it tác dụng, mà ngược lại tác dụng của nó là siêu to khổng lồ.

Bài toán thực tế

Giả sử ứng dụng quản lý sách của chúng ta sẽ có 3 lớp:

  1. BookRepository: để truy xuất cơ sở dữ liệu
  2. BookService: để thực hiện một số nghiệp vụ, xử lý logic trước khi ghi truy xuất cơ sở dữ liệu
  3. BookController: để nhận request, gọi xuống tầng service để xử lý logic và trả lại response cho client

Sơ đồ lớp phụ thuộc sẽ thế này:

Và source code sẽ như sau:

public class BookRepository {

    public void save(Book book) {
        System.out.println("saved book: " + book);
    }
}

public class BookService {
    private final BookRepository bookRepository;

    public void saveBook(Book book) {
        bookRepository.save(book);
    }
}

public class BookController {
    private final BookService bookService;

    public void saveBook(Book book) {
        bookService.saveBook(book);
    }
}

Ứng dụng của chúng ta sẽ chạy thông qua hàm main kinh điển như thế này:

final BookRepository bookRepository = new BookRepository();

final BookService bookService = new BookService();
bookService.setBookRepository(bookRepository);

final BookController bookController = new BookController();
bookController.setBookService(bookService);

bookController.saveBook(new Book(1L, "EzyFox in action"));

Phân tích vấn đề

Chúng ta có thể thấy rằng hàm main này khá phức tạp so với những gì mà chúng ta thấy khi lập trình với Spring Boot. Rõ ràng là chúng ta phải tự mình khởi tạo mọi đối tượng và set các đối tượng phụ thuộc vào nhau. Với những ứng dụng đơn giản thì không sao, nhưng với những ứng dụng với hàng trăm hàng nghìn lớp theo mô hình thế này:

Thì có lẽ việc phải khởi tạo từng đối tượng và set chúng lại sẽ không khác gì chúng ta phải đi xây kim tự tháp vậy, thực sự rất khó khăn. Đây chính là lúc DI ra đời, hay nói chính xác hơn là tự động tiêm phụ thuộc (Dendency Injection Atomatically) đã ra đời.

Mục tiêu ra đời

Về cơ bản thì DI ra đời với 2 mục tiêu chính:

  1. Tự động khởi tạo các đối tượng và tiêm (inject) các đối tượng phụ thuộc vào với nhau mà không cần bất cứ sự can thiệp nào từ lập trình viên
  2. Giúp chúng ta giải quyến vấn đề tương thích ngược (IoC) mà mình sẽ nói ở bài sau

Áp dụng DI

Nào bây giờ hãy quay trở lại bài toán quản lý sách, chúng ta sẽ sử dụng thư viện ezyfox-bean. Thư viện này là sự kết hợp giữa java reflectionjava just in time (JIT), nó sẽ giúp chúng ta dễ dàng áp dụng DI chỉ với vài dòng code:

final EzyBeanContext beanContext = EzyBeanContext.builder()
            .scan("com.tvd12.ezyfox.example.bean")
            .build();

Bản chất bên trong của những dòng code này là nó sẽ đi tìm kiếm các lớp được đánh dấu với @EzySingleton annotation để khởi tạo. Và muốn sử dụng chúng ta chỉ cần làm thế này:

final BookController bookController = (BookController)beanContext.getBean(BookController.class);
bookController.saveBook(new Book(1L, "EzyFox in action"));

Bạn có thể thấy rằng việc khởi tạo các lớp và gọi hàm set đã biến mất, và cho dù có hàng trăm, hàng nghìn lớp thì với sự hỗ trợ của các thư viện lập trình DI, mọi thứ cũng chỉ đơn giản là một vài dòng code mà thôi.

Kết luận

Dependency Injection ngày nay là một thứ không thể thiếu, nó sẽ giúp chúng ta hoàn toàn thoát khỏi việc khởi tạo và set các phụ thuộc cho các lớp. Tuy nhiên trong số các ngôn ngữ lập trình thì hiện nay vẫn chỉ có Java là mạnh nhất với các bộ thư viện hỗ trợ scan và đọc annotation, C# cũng có nhiều thư viện Entity hỗ trợ DI nhưng chưa triệt để được bằng Java, còn các ngôn ngữ khác thì đa phần chúng ta vẫn phải tự khởi tạo và set. Nhưng dù là ở ngôn ngữ nào, thì chúng ta hãy luôn tìm cách để làm DI nhé, nó thực sự có sức mạnh ghê gớm.

Tham khảo

  1. Ví dụ