Ошибка внедрения фиктивной зависимости в dagger2

Я просто играю, пытаясь узнать Dagger2 немного лучше. Я нашел в Интернете простой пример, где Bar зависит от BarDatabase:

    public class Bar {
       // Bar depends on BarDatabase
       private BarDatabase db;

       @Inject
       public Bar(BarDatabase db) {
          this.db = db;
       }

       public BarDatabase getBar() { return db; }

       public Cocktail getCocktail(String name) {
          String row = db.getCocktail(name);
          return new Cocktail(row);
       }
    }

Dagger отлично работает со следующими модулями и компонентами:

    package com.example.eflatt.di.barcomponent;
    import com.example.eflatt.di.Bar;
    import com.example.eflatt.di.module.BarModule;
    import com.example.eflatt.di.testmodule.TestBarModule;
    import javax.inject.Singleton;
    import dagger.Component;

     /**
      * Created by eflatt on 4/26/16.
      */
    @Singleton
    @Component(modules = {BarModule.class, TestBarModule.class})
    public interface BarComponent {
        public Bar provideBar();
    }

    package com.example.eflatt.di.module;

    import com.example.eflatt.di.Bar;
    import com.example.eflatt.di.BarDatabase;
    import com.example.eflatt.di.MockBarDatabase;
    import javax.inject.Singleton;
    import dagger.Module;
    import dagger.Provides;

    /**
     * Created by eflatt on 4/26/16.
     */
    @Module
    public class BarModule {
       @Provides @Singleton
       public Bar provideBar () {
       return new Bar(new BarDatabase());
    }
 }

Но я хотел попробовать внедрить MockBarDatabase() в свой модульный тест, когда все пошло не так.

Вопрос 1: Я решил написать TestBarModule, который создал MockBarDatabase() в методе ProvideBar (отдельный файл с именем TestBarModule) — это неправильный метод?

    @Module
    public class TestBarModule {
      @Provides @Singleton
      public Bar provideBar () {
          return new Bar(new MockBarDatabase());
      }
    }

Вопрос 2: Если написание отдельного модуля для обработки Mock-кейса является правильным решением (я предполагаю, что это не так), то как мне справиться с тем фактом, что моя IDE говорит мне:

Ошибка: (15, 16) ошибка: com.example.eflatt.di.Bar привязан несколько раз: @Provides @Singleton com.example.eflatt.di.Bar com.example.eflatt.di.module.BarModule.provideBar( ) @Provides @Singleton com.example.eflatt.di.Bar com.example.eflatt.di.testmodule.TestBarModule.provideBar()


person Eric Flatt    schedule 28.04.2016    source источник


Ответы (1)


это неправильная техника?

Да.

Что вы делаете, так это настраиваете все для использования внедрения конструктора

@Inject
public Bar(BarDatabase db) {
   this.db = db;
}

затем вы решаете все же предоставить его самостоятельно и, кроме того, не использовать фреймворк кинжала и заниматься вещами самостоятельно

@Provides @Singleton
public Bar provideBar () {
    // MockBarDatabase isn't supplied by dagger but by you.
    return new Bar(new MockBarDatabase());
}

Улучшения

По крайней мере, вы должны предоставить каждую зависимость отдельно. Затем вы также увидите, что намного проще переключать отдельные объекты на фиктивные объекты.

// this would be a 'righter' way to do it
@Provides @Singleton
public Bar provideBar (MockBarDatabase db) {
    return new Bar(db);
}

MockBarDatabase должен быть предоставлен из другого метода @Provides в приведенном выше примере. Но, как упоминалось ранее, вы уже настроили все для внедрения конструктора. Так что используйте его. Вы можете просто пойти дальше и опустить любые вызовы new:

// dagger will create the object for you!
@Provides @Singleton
public Bar provideBar (Bar bar) {
    return bar;
}

Таким же образом вы бы реализовали интерфейс. Или просто в полной мере используйте внедрение конструктора и не указывайте собственный метод в своем модуле. Теперь в вашем случае, если вы действительно хотите, чтобы ваш бар был одноэлементным, вам придется адаптировать свой класс:

@Singleton
public class Bar {
    @Inject Bar(Db db) {/**/}
    // ...
}

И да, тогда вашему модулю просто нужно будет предоставить BarDatabase, если он не может быть также введен конструктором.

Компоненты могут просто создавать объекты, если у них есть аннотированный конструктор и их зависимости также могут быть созданы (внедрение конструктора) или предоставлены (эти @Provide и @Modules). Так что используйте это и используйте модули только в том случае, если вы не можете использовать внедрение конструктора или вам нужны квалификаторы или дополнительная настройка.


Я не знаю, как вы создаете эту ошибку, но я полагаю, вы пытались добавить второй модуль, предоставляющий фиктивный объект для того же компонента?

Если вы хотите имитировать модули, попробуйте создать подкласс модуля и переопределить метод для предоставления объекта.

Есть много хороших руководств и примеров по dagger 2, и я настоятельно рекомендую вам изучить их.

person David Medenjak    schedule 28.04.2016
comment
Спасибо за фрагменты. По правде говоря, мой первоначальный удар по этому вопросу я явно не обновлял, как здесь, просто я нашел (в отличие от вас) несколько хороших руководств. Тонны слов, тонны того, что говорят о DI и зачем его использовать с небольшим упоминанием кинжала на бэкэнде, но с настоящим пошаговым руководством - не так повезло (поэтому я просто начал возиться с тривиальным примером, а потом пошел и прочитал сгенерированный код). Так что если у вас есть что-то особенное, что вам нравится, я был бы рад. Еще раз спасибо за фрагменты и взглянуть, я ценю это. - person Eric Flatt; 29.04.2016
comment
@EricFlatt Этот был наиболее информативным для меня, но вы должны начать с внимательного прочтения Руководства пользователя, если вы не не делайте этого уже, также фактический javadoc также подробно объясняет большинство вещей - person David Medenjak; 29.04.2016