Thông não Dependency Injection bằng Eimi Fukada

Hello what’s up, dạo này anh em còn bị chửi code như shit nữa không nhờ. Tôi thì vẫn thế =)))), vẫn như shit nhưng hôm nay tôi muốn giới thiệu đến anh em một kỹ thuật mới là Dependency Injection để code của anh em đỡ bốc mùi hơn một chút nhé, vào việc.

1. Dependency Injection là gì?

Theo tài liệu thì:

Dependency Injection is a design pattern, …

Nôm na Dependency Injection (DI) là một kỹ thuật cho phép xóa bỏ sự phụ thuộc giữa các module, làm cho ứng dụng dễ dàng hơn trong việc thay đổi module, bảo trì code và testing.

DI cung cấp cho một đối tượng các thể hiện phụ thuộc (dependencies) của nó từ bên ngoài truyền vào mà không phải khởi tạo trực tiếp từ trong class sử dụng.

2. Nhiệm vụ của dependency injection

  • Tạo các đối tượng.
  • Quản lý sự phụ thuộc (dependencies) giữa các đối tượng.
  • Cung cấp (inject) các phụ thuộc được yêu cầu cho đối tượng (được truyền từ bên ngoài đối tượng).

3. Nguyên tắc hoạt động của DI

  • Các module không giao tiếp trực tiếp với nhau, mà thông qua interface. Module cấp thấp sẽ implement interface, module cấp cao sẽ gọi module cấp thấp thông qua interface.
  • Việc khởi tạo các module cấp thấp sẽ do DI Container/ IoC Container thực hiện.
  • Việc Module nào gắn với interface nào sẽ được config trong file properties, trong file XML hoặc thông qua Annotation. Annotation là một cách thường được sử dụng trong các Framework, chẳng hạn như @Inject với CDI, @Autowired với Spring hay @ManagedProperty với JSF.

4. Các dạng Dependency Injection

  • Constructor Injection: Các dependency sẽ được container truyền vào (inject vào) 1 class thông qua constructor của class đó. Đây là cách thông dụng nhất.
  • Setter Injection: Các dependency sẽ được truyền vào 1 class thông qua các hàm Setter.
  • Fields/ properties: Các dependency sẽ được truyền vào 1 class một cách trực tiếp vào các field.
  • Interface Injection: Class cần inject sẽ implement 1 interface. Interface này chứa 1 hàm tên Inject. Container sẽ injection dependency vào 1 class thông qua việc gọi hàm Inject của interface đó. Đây là cách rườm rà và cũng ít được sử dụng.
  • Service Locator: nó hoạt động như một mapper, cho phép thay đổi code tại thời điểm run-time mà không cần biên dịch lại ứng dụng hoặc phải khởi động lại.

Anh em đọc đến đây đã hiểu Dependency Injection là gì chưa? Anh em nào đã thông não thì có thể bỏ qua, anh em nào vẫn lơ tơ mơ thì tiếp tục, full hd không che phía dưới =)))).

5. Ví dụ sử dụng Dependency Injection.

Bikini.java

Java
package org.tmsblog.patterns.di;

public class Bikini {
    public void wear() {
        System.out.println("Đã mặc bikini");
    }
}

Girl.java

Java
package org.tmsblog.patterns.di;

public class Girl {

    private Bikini outfit = new Bikini();
    
    public void result() {
        outfit.wear();
    }
}

Ví dụ anh em tạo 1 Girl, anh em sẽ tạo ra thêm 1 bộ Bikini đi kèm với cô gái đó. Lúc này, Bikini tồn tại mang ý nghĩa là dependency (phụ thuộc) của Girl.

Khi khởi tạo thuộc tính như này, vô tình tạo ra một điểm thắt nút trong chương trình, giả sử, Girl muốn mặc một bộ Quần Jeans + áo hai dây thì anh em sẽ phải thay class Bikini thành JeansWithTshirt(quần jeans với áo T-shirt) ư?

Hay nguy hiểm hơn, bộ đồ Bikini bị hỏng? (code lớp Bikini không hoạt động?) nó sẽ ảnh hưởng trực tiếp tới Girl.

Vấn đề là ở đó, chúng ta cùng đọc lại nguyên tắc D của SOLID:

  • Các mô-đun cấp cao không được nhập bất cứ thứ gì từ các mô-đun cấp thấp. Cả hai nên phụ thuộc vào sự trừu tượng

Bây giờ chúng ta sẽ áp dụng Dependency Injection để giải quyết vấn đề trên.

Girl.java

Java
package org.tmsblog.patterns.di;

public class Girl {

    private final Outfit anything;

    public Girl(Outfit anything) {
        this.anything = anything;
    }
}

Bikini.java

Java
package org.tmsblog.patterns.di;

public class Bikini implements Outfit{
   @Override
    public void wear() {
        System.out.println("Đã mặc bikini");
    }
}

Outfit.java

Java
package org.tmsblog.patterns.di;

public interface Outfit {
    public void wear();
}

Main.java

Java
package org.tmsblog.patterns.di;

public class Main {
    public static void main(String[] args) {
        Outfit bikini = new Bikini();

        Girl fukada = new Girl(bikini);
        //Vậy là đã lấy được đối tượng Girl nha
    }
}

Với đoạn code ở trên, chúng ta đã gần như tách được Bikini ra hoàn toàn khỏi Girl. điều này làm giảm sự phụ thuộc giữa Girl và Bikini. Mà tăng tính tùy biến, linh hoạt cho code.

Bây giờ Girl sẽ hoạt động với Outfit mà thôi. Và Outfit ở đâu ra? Chúng ta tạo ra và đưa nó vào (Inject) cô gái Girl.

Khái niệm Dependency Injection từ đây mà ra:
Dependency Injection là việc các Object nên phụ thuộc vào các Abstract Class và thể hiện chi tiết của nó sẽ được Inject vào đối tượng lúc runtime.

Bây giờ muốn Girl mặc gì khác, anh em chỉ cần tạo một Class kế thừa Outfit và Inject nó vào Girl là xong!

Dependency Injection giúp chúng ta dễ dàng mở rộng code và giảm sự phụ thuộc giữa các dependency với nhau. Tuy nhiên, lúc này, khi code bạn sẽ phải kiêm thêm nhiệm vụ Inject dependency (tiêm sự phụ thuộc). Thử tưởng tượng một Class có hàng chục dependency thì bạn sẽ phải tự tay inject từng ý cái. Việc này lại dẫn tới khó khăn trong việc code, quản lý code và dependency

Java
package org.tmsblog.patterns.di;

public class Main {
    public static void main(String[] args) {
       Outfit bikini = new Bikini();
       Accessories gucci = new GucciAccessories();
       HairStyle hair = new KoreanHairStyle();
       Girl fukada = new Girl(bikini, gucci, hair);
    }
}

Vãi chưởng nhỉ, lúc này có thằng nào làm hộ được chúng ta việc này thì tốt biết mấy =))).

Bây giờ giả sử, chúng ta định nghĩa trước toàn bộ các dependency có trong Project, mô tả nó và tống nó vào 1 cái kho và giao cho một thằng tên là framework quản lý. Bất kỳ các Class nào khi khởi tạo, nó cần dependency gì, thì cái framework này sẽ tự tìm trong kho rồi inject vào đối tượng thay chúng ta. sẽ tiện hơn phải không?

Úi dời ơi, tuyệt chứ nhỉ, đó cũng chính là nguyên lý chính của Inversion of Control (IOC) – Đảo chiều sự điều khiển

Nguyên văn Wiki:

  • Inversion of Control is a programming principle. flow of control within the application is not controlled by the application itself, but rather by the underlying framework.

Khi đó, code chúng ta sẽ chỉ cần như này, để lấy ra đối tượng Girl, còn trong Girl.java anh em tiêm chích AccessoriesHairStyle như Outfit nha:

Java
package org.tmsblog.patterns.di;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;

@SpringBootApplication
public class Main {
    public static void main(String[] args) {
        ApplicationContext context = SpringApplication.run(Main.class, args);

        Girl girl = context.getBean(Girl.class);
        // Anh em muốn lấy gì từ Girl thì cứ chấm mút ra nha =))
    }
}

Lời kết:

Dependency InjectionIoC container là những khái niệm rất quan trọng, hầu hết các ứng dụng, framework hiện tại đều sử dụng nó. Chúng ta cần tìm hiểu để biết rõ DI và IoC được ứng dụng trong trường hợp nào. Nếu áp dụng hợp lý code của chúng ta sẽ ít kết dính hơn (loose coupling), dễ bảo trì, dễ test hơn, tổ chức ứng dụng hợp lý và gọn gàng hơn.

Source Code tham khảo tại đây:

0 Shares:
Leave a Reply

Your email address will not be published. Required fields are marked *

You May Also Like