Explore in Dagger - Module

همانطور که قبلا گفته شد، ماژول ها یک ساختار مجازی هستند که در زمان build هر تابع آن به یک Factory تبدیل میشود. در این پست امکانات و ویژگی های مختلف ماژول‌ها را بررسی خواهیم کرد.

اگر هنوز مطلب قبلی رو نخوندید، بهتره بعد از مطالعه‌ی اون برگردید و اینجارو ادامه بدید.

Multi Module

برای داشتن چندین ماژول مختلف، لیست ماژول‌های موردنیاز یک کامپوننت را به صورت زیر @Component(modules = {...}) نام میبریم. لینک

@Component(modules = {AppModule.class, SecondModule.class})
public abstract class AppComponent {

    abstract public void inject(MainActivity mainActivity);

}
@Module
public interface SecondModule {
    @Binds
    Heater bindsHeater(ElectricHeater electricHeater);
}

Child Module

این ساختار نیز همانند ساختار قبلیست اما میتوان آنها را به صورت سلسله مراتبی نیز معرفی کرد. لینک

@Module(includes = {SecondModule.class})
public abstract class AppModule {

    @Provides
    public static CoffeeMaker provideCoffeeMaker(Heater heater, Pump pump) {
        return new CoffeeMaker(heater, pump);
    }
}

Abstract/Non-abstract Module

کلاس ماژول را میتوان به صورت abstract تعریف کرد (تا حالا در مثالها به این صورت تعریف کرده‌ایم).

در این صورت متدهایی که میتوانیم در کلاس ماژول بنویسیم باید یکی از دو حالت زیر باشند:

  • abstract باشند. این متدها که معمولا برای مواردی ست که بخواهند با @Bind یک کلاس دیگر را به یک اینترفیس bind کنند.
  • non-abstract و static باشند. این متد ها معمولا برای متدهایی‌ ست که با @Provides علامتگذاری شده باشند.

بدلیل اینکه ماژول abstract است، قابل instantiate نیست. اما بدلیل اینکه متدها static هستند براحتی در Factory ساخته شده قابل دسترسی هستند. لینک

public final class AppModule_ProvideCoffeeMakerFactory implements Factory<CoffeeMaker> {
  ...
  public static CoffeeMaker provideCoffeeMaker(Heater heater, Pump pump) {
    return Preconditions.checkNotNull(
        AppModule.provideCoffeeMaker(heater, pump),
        "Cannot return null from a non-@Nullable @Provides method");
  }
}

اما در مواردی که ماژول abstract نباشد، Factory هایی که نیاز به آن type داشته باشند باید به یک instance از آن ماژول دسترسی داشته باشند تا بتوانند تابع مورد نظر خود را صدا کرده و مقدار مورد نظر را دریافت کنند. کامیت

public final class AppModule_ProvideCoffeeMakerFactory implements Factory<CoffeeMaker> {
  private final AppModule module;

  private final Provider<Heater> heaterProvider;

  private final Provider<Pump> pumpProvider;

  public AppModule_ProvideCoffeeMakerFactory(
      AppModule module, Provider<Heater> heaterProvider, Provider<Pump> pumpProvider) {
    this.module = module;
    this.heaterProvider = heaterProvider;
    this.pumpProvider = pumpProvider;
  }

  @Override
  public CoffeeMaker get() {
    return provideCoffeeMaker(module, heaterProvider.get(), pumpProvider.get());
  }

  public static AppModule_ProvideCoffeeMakerFactory create(
      AppModule module, Provider<Heater> heaterProvider, Provider<Pump> pumpProvider) {
    return new AppModule_ProvideCoffeeMakerFactory(module, heaterProvider, pumpProvider);
  }

  public static CoffeeMaker provideCoffeeMaker(AppModule instance, Heater heater, Pump pump) {
    return Preconditions.checkNotNull(
        instance.provideCoffeeMaker(heater, pump),
        "Cannot return null from a non-@Nullable @Provides method");
  }
}

همانگونه که در کامیت آخر نیز قابل مشاهده ست، یک فیلد از نوع خود ماژول به Factory ساخته شده از آن اضافه شده. همچنین تابع provideCoffeeMaker نیز تامین CoffeeMaker را از همان آرگومان انجام میدهد.

اما چه کسی این فیلد را تامین کرده‌است؟

باید توجه کرد که چه کلاسی نیازمندی‌های MemberInjector هارا تامین کرده و عمل injection را انجام میدهد؟

کامپوننت ها عامل اصلی injection هستند.

در مطالب بعدی ساختار دقیق کامپوننت را بررسی خواهیم کرد.

Instantiate/Non-instantiate Arguments

تمامی مواردی که به عنوان dependency قابل تامین هستند از لحاظ instantiate به دو دسته تقسیم میشوند.

  • کلاس‌هایی که فاقد آرگومان در constructor خود هستند. مانند Pump

این موارد به راحتی با @Inject و یا @Provides آماده ارائه میشوند.

  • کلاس‌هایی که دارای آرگومان دارند. مانند CoffeeMaker

لازم به ذکر است که تمامی این آرگومان‌ها به صورت Provider<T> از جنس آن آرگومان در کلاس Factory نگهداری میشوند.


public final class AppModule_ProvideCoffeeMakerFactory implements Factory<CoffeeMaker> {
  private final Provider<Heater> heaterProvider;

  private final Provider<Pump> pumpProvider;

  public AppModule_ProvideCoffeeMakerFactory(Provider<Heater> heaterProvider, Provider<Pump> pumpProvider) {
    this.heaterProvider = heaterProvider;
    this.pumpProvider = pumpProvider;
  }
    ...
}

این کلاس‌ها در صورتی که آرگومان‌های آنها قابل instantiate شدن باشند، به صورت آبشاری instantiate میشوند و به کلاس وابسته پاس داده میشوند. اما در صورتی که این آرگومان‌ها همانند Context یا Application قابل instantiate کردن نباشند باید از طریق کامپوننت تامین شوند که در مطلب بعدی به آن خواهیم پرداخت.

جمع‌بندی

  • ماژول های abstract نمیتوانند متدهای تامین کننده non-static داشته باشند چرا که از ماژول‌ها instantiate نمیشود و نمیتوان به این متدها دسترسی پیدا کرد.
  • Factory هایی که از ماژول‌های non-abstract به وجود آمده‌اند یک رفرنس از ماژول در Factory خود دارند تا متد مورد نظر خود را فراخوانی کنند. و این نیازمندی از طریق کامپوننت تامین میشود.
  • Factory هایی که از ماژول‌های abstract به وجود آمده‌اند به صورت مستقیم متد مورد نظر خود را فراخوانی میکنند.
  • نیازمندی‌/آرگومان های non-instantiative مربوط به Factory ها از طریق کامپوننت تامین میشوند.

تمامی فایل های این پست روی برنچ p2-module در دسترس است.

rss facebook twitter github youtube mail spotify lastfm instagram linkedin google google-plus pinterest medium vimeo stackoverflow reddit quora quora telegram heart