Кинжал 2: инжектирование введенного пользователем параметра в объект

Скажем, у меня есть класс Util, который принимает объект - экземпляр класса Validator.

Поскольку я хочу избежать создания экземпляра класса Validator в Util, я передаю его через конструктор:

public class Util {

   @Inject
   public Util(Validator validator) {

   }


}

У меня есть модуль, который обеспечивает экземпляр Validator:

@Provides
@Singleton
Validator provideValidator() {
    return Validator.getInstance();
}

и экземпляр класса Util:

@Provides
Util provideUtil(Validator validator) {
    return new Util(validator);
}

У меня подключен компонент, который даст мне экземпляр Util:

Util getUtil()

поэтому в рамках своей деятельности я мог бы назвать это так:

Util myUtil = getComponent.getUtil();

Все это прекрасно работает - у myUtil есть соответствующий экземпляр класса Validator при создании экземпляра.

Теперь я хочу передать строковую переменную с именем address (которая вводится пользователем через пользовательский интерфейс). Я хочу изменить конструктор, чтобы передать как экземпляр Validator, так и введенную пользователем строку:

@Inject
public Util(Validator validator, String address) {

}

Я просто не могу понять, как передать этот второй параметр. Может кто-нибудь сказать мне, как?

В идеале я хочу создать Util как:

Util myUtil = getComponent.getUtil(txtAddress.getText());

Ответ 1

У меня был тот же вопрос, что и вы, когда я начал изучать Кинжал 2 пару недель назад. Я нашел информацию об этом (и большинстве других вопросов, связанных с кинжалом 2), которые трудно найти, поэтому я надеюсь, что это поможет!

Самый простой ответ - вы не можете. То, что вы ищете, это то, что называется вспомогательной инъекцией, и оно не является частью кинжала 2. Некоторые другие рамки инъекций зависимостей (DI), такие как Guice, предложите эту функцию, чтобы вы могли изучить их. Конечно, есть еще способы сделать то, что вы хотите сделать, используя Dagger 2.

Заводы фабрик фабрик

Стандартный способ делать то, что вы хотите сделать в сочетании с DI, - это использовать шаблон Factory. В принципе, вы создаете инъекционный класс Factory, который принимает параметры времени выполнения, такие как address, в качестве аргументов для методов создания объектов, которые он предоставляет.

В вашем случае вам понадобится UtilFactory, в который Dagger 2 вводит Validator при создании и предлагает метод create(String address), который создает экземпляры Util. UtilFactory должен содержать ссылку на введенный экземпляр Validator, чтобы он имел все необходимое для создания экземпляра Util в методе create.

Код для многих таких заводов может быть громоздким. Вы обязательно должны взглянуть на AutoFactory, что облегчает часть бремени. Похоже, что инъекция с помощью Guice работает очень похоже на Dagger 2 + AutoFactory (хотя и с более приятным синтаксическим сахаром).

Дополнительные модули/компоненты

Я сомневаюсь, что это то, что вы хотели бы сделать в этом случае, но вы могли бы просто создать модуль, который предоставляет адрес (и создать экземпляр нового компонента). Вам не нужно создавать новый класс @Module для каждого возможного адреса. Вместо этого вы можете просто передать адрес в качестве аргумента конструктору модуля. Вы можете использовать аннотацию @BindsInstance, предложенную teano для достижения аналогичного результата.

Я не уверен, что это анти-шаблон или нет. Для меня это похоже на приемлемый маршрут в некоторых случаях, но только тогда, когда вы фактически используете тот же, например. адрес для инициализации "многих" объектов. Вы определенно не хотите создавать экземпляр нового компонента и новую модель для каждого объекта, требующего инъекции. Это не эффективно, и если вы не будете осторожны, вы получите больше кода шаблона, чем без кинжала.

Не используйте (всегда) DI: Инъекции против нововведений

Что-то, что было очень полезно для меня при изучении каркасов DI, заключалось в том, что использование рамки DI не означает, что вы должны инициализировать все свои объекты DI. Как правило: вводите объекты, которые вы знаете во время компиляции, и которые имеют статические отношения с другими объектами; не вводите информацию о времени выполнения.

Я думаю, это - хорошая статья на эту тему. В нем представлена ​​концепция "новизны" и "инъекции".

  • Инъекции - это классы рядом с корнем вашего графика DI. Экземпляры этих классов - это те объекты, которые вы ожидаете, чтобы ваша инфраструктура DI обеспечивала и вводила. Объекты типа "менеджер" или "сервис-тип" являются типичными примерами инъекций.
  • Newables - это объекты на границах вашего графика DI или вообще не являются частью вашего графика DI. Integer, address и т.д. являются примерами новых возможностей.

В широком смысле, новизна - это пассивные объекты, и нет смысла вводить или издеваться над ними. Обычно они содержат "данные", которые находятся в вашем приложении, и доступны только во время выполнения (например, ваш адрес). Newables не должны содержать ссылки на инъекции или наоборот (то, что автор сообщения упоминает как "инъекционное/новое разделение" ).

В действительности я обнаружил, что не всегда легко или возможно провести четкое различие между инъекционными и новыми. Тем не менее, я думаю, что они - хорошие концепции, которые можно использовать как часть вашего процесса мышления. Определенно подумайте дважды, прежде чем добавить еще один Factory в свой проект!

В вашем случае, я думаю, было бы разумно рассматривать Util как инъекционную, а адрес - как новый. Это означает, что адрес не должен быть частью класса Util. Если вы хотите использовать экземпляр Util, например. validating/... address, просто передайте адрес, который вы хотите проверить как аргумент метода validation/....

Ответ 2

Вы можете изменить компоновщик компонентов для внедрения экземпляров. Смотрите: https://google.github.io/dagger/users-guide#binding-instances.

В вашем случае вы можете позвонить:

Util myUtil = DaggerMyComponent.builder().withAddress(txtAddress.getText()).build().getComponent().getUtil();

если MyComponent определен как:

@Component(modules = UtilModule.class)
interface MyComponent{

    MyComponent getComponent();

    @Component.Builder
    interface Builder {

        @BindsInstance Builder withAddress(@Address String address); //bind address instance

        MyComponent build();

    }
}

И UtilModule:

@Module
class UtilModule{

    @Provides
    Util getUtil(Validator validator, @Address String address){ //inject address instance
        return new Util(validator, address);
    }

}

Валидатор должен быть снабжен аннотированным конструктором @Inject или аннотированным методом @Provides в классе модулей, переданном модулям MyComponent в аннотации @Component.

Ответ 3

при инициализации модуля вы можете передать некоторые параметры, например:

public NetServiceModule(String baseUrl, boolean isLogEnabled, CookieJar cookieJar) {
    this.mBaseUrl = baseUrl;
    this.mIsLogEnabled = isLogEnabled;
    this.mCookieJar = cookieJar;
}

И затем получите компонент в "Container Class":

NetServiceComponent component = DaggerNetServiceComponent.builder()
            .netServiceModule(new NetServiceModule(baseUrl, mIsLogEnabled, cookieJar))
            .build();
    component.inject(this);

С помощью Предоставляет метод для обеспечения Инъекции, которые генерируют по некоторым параметрам, если это необходимо:

@Provides
Retrofit provideRetrofit(OkHttpClient httpClient, GsonConverterFactory gsonConverterFactory, NetCallAdapterFactory netCallAdapterFactory) {

    return new Retrofit.Builder()
            .client(httpClient)
            .baseUrl(mBaseUrl)
            .addConverterFactory(gsonConverterFactory)
            .addCallAdapterFactory(netCallAdapterFactory)
            .build();
}

Ответ 4

@Inject
Usermodel uuser;
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    userComponent dc = DaggeruserComponent.create();
    dc.injectMain(this);

    historymodel hm =  uuser.getHistorymodel();// get the models to pass user inputs 

    videoModel vm = uuser.getVideoModel();//  get the models to pass user inputs

    hm.setUid("userid ");


}

}