0

Google Guice - Java

Hôm này mình muốn chia sẻ với mọi người về một framework của google là Guice. Guice là một dependency injection được cung cấp bởi Google cho Java 6 trở lên.

Với dependency injection , các objects chấp nhận các dependency trong hàm khởi tạo của nó. Để khởi tạo một object. Chúng ta phải build những depens của nó. Để build depens, chúng ta cần những depens tiếp. Do đó, khi build một object, chúng ta thực sự cần build một object graph.

Tuy nhiên, việc build các objec graph bằng tay rât dễ gây lỗi, khó để test. Thay vào đó, Guice build object graph cho chúng ta. Hay nói cách khác. Guice là một framework chịu trách nhiệm quản lý các depens cho chúng ta. Tất nhiên, Guice phải được config để quản lý giúp chúng ta. Nói đến đây có vẻ khá trừu tượng. Nhưng các bạn thử nghĩ, các object của chúng ta chắc chắn sẽ cần nhiều depens để làm những tác vụ cần thiết. Ở đây có Guice quản lý giúp chúng ta việc đó. Ít nhiều giúp chúng ta bớt được việc phải quản lý chúng.

Để minh họa, chúng ta sẽ bắt đầu với một class service BillingService mà cần đến depens là các interface là CreditCardProcessor và TransactionLog trong hàm khởi tạo của nó.

class BillingService {
  private final CreditCardProcessor processor;
  private final TransactionLog transactionLog;

  @Inject
  BillingService(CreditCardProcessor processor, 
      TransactionLog transactionLog) {
    this.processor = processor;
    this.transactionLog = transactionLog;
  }

  public Receipt chargeOrder(PizzaOrder order, CreditCard creditCard) {
    ...
  }
}

Chúng ta muốn build một BillingService sử dụng 2 depens trên. Guice sử dụng bindings để map các kiểu cho các implementations. Chúng ta sẽ nói điều đó với Guice trong file module config.

public class BillingModule extends AbstractModule {
  @Override 
  protected void configure() {

     /*
      * This tells Guice that whenever it sees a dependency on a TransactionLog,
      * it should satisfy the dependency using a DatabaseTransactionLog.
      */
    bind(TransactionLog.class).to(DatabaseTransactionLog.class);

     /*
      * Similarly, this binding tells Guice that when CreditCardProcessor is used in
      * a dependency, that should be satisfied with a PaypalCreditCardProcessor.
      */
    bind(CreditCardProcessor.class).to(PaypalCreditCardProcessor.class);
  }
}

Module được quản lý bởi Injector, Injector chính là Guice object -graph builder. Bây giờ chúng ta đã tạo được một injector. Và chúng ta có thể sử dụng để build BillingService.

 public static void main(String[] args) {
    /*
     * Guice.createInjector() takes your Modules, and returns a new Injector
     * instance. Most applications will call this method exactly once, in their
     * main() method.
     */
    Injector injector = Guice.createInjector(new BillingModule());

    /*
     * Now that we've got the injector, we can build objects.
     */
    BillingService billingService = injector.getInstance(BillingService.class);
    ...
  }

Bây h mình sẽ giải thích cho các bạn một chút về các đoạn code bên trên.

Đầu tiên, khi application được bootstrap, một injector, hay một object graph được tạo ra từ BillingModule. Trong module này nói với Guice các implementation class của các interface khi các interface được inject vào các class khác. Ví dụ ở đây 2 interface được inject vào BillingService, Guice đã xác định được implementation của 2 interface đó là DatabaseTransactionLog và PaypalCreditcardProcessor. Nên khi BillingService được khởi tạo. Nó sẽ sử dụng 2 implementation classes này để hoạt động.

Quay trở lại một chút, nếu không có Guice, chúng ta sẽ phải làm như sau:

Đầu tiên cũng là tạo 2 interface, 2 implementation classes là PayPalCreditCardProcessor và DatabaseTransactionLog . Sau đó ở BillingService

 class BillingService {
  private final CreditCardProcessor processor;
  private final TransactionLog transactionLog;

  BillingService(CreditCardProcessor processor, 
      TransactionLog transactionLog) {
    this.processor = processor;
    this.transactionLog = transactionLog;
  }

  public Receipt chargeOrder(PizzaOrder order, CreditCard creditCard) {
  }
}

Trong hàm main

 public static void main(String[] args) {
   CreditCardProcessor processor = new PaypalCreditCardProcessor processor();
   TransactionLong log = new DatabaseTransactionLog();
    BillingService billingService = new BillingService(processor, log);
    ...
    
  }

Đến đây có thể bạn vẫn thắc mắc tại sao nên dùng Guice. Thử nghĩ xem, nếu sau này, bạn muốn thay đổi các implementation class cho các interface kia. Bạn sẽ phải đi tìm tất cả các nơi dùng nó để sửa ví dụ như các hàm main() trên đây. Thay vào đó, đi đến module config và sửa chắc chắn sẽ nhanh và chính xác hơn.

Ví dụ trên mới chỉ là một ví dụ nho nhỏ để làm quen và biết được chút ít sức mạnh của Guice.

Ở trong file module config kia, các bạn đã thấy Guice có dùng đến bind() method để mapping các interface và implementation classes. Vậy Binding trong Guice là gì và nó còn có những tác dụng như thế nào. Xin mời các bạn theo dõi các bài viết sau.

Nếu có điều gì sai sót, mong mọi người góp ý. Xin trân thành cảm ơn.

Tài liệu tham khảo https://github.com/google/guice


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí