настраиваемые зависимости с легким макетом реализации по умолчанию - PullRequest
0 голосов
/ 02 июля 2011

Я работаю над своего рода библиотекой парсера значений параметров. Я бы хотел, чтобы Parser был определен следующим образом:

public class Parser {

    private ValuesConfiguration configuration;
    private ValuesProvider valuesProvider;
    private ValuesMapper valuesMapper;

    public Parser(ValuesConfiguration configuration) {
        this.configuration = configuration;
    }

    public Result parse(String parameterName) {
        List<Values> values = valuesProvider.getValues(parameterName);
        // do other stuff on values
        // ...
        return valuesMapper.transformValues(values, configuration);
    }
}

Я бы хотел, чтобы клиенты этой библиотеки не знали о реализациях по умолчанию ValuesProvider и ValuesMapper и использовали его как

Result result = new Parser(myConfig).parse("sampleParam");

Хотя должна быть возможность устанавливать собственные реализации при необходимости. Интересно, как и где я должен инициировать эти реализации по умолчанию и все же позволить клиентам устанавливать свои собственные, если они хотят. Я не хочу придерживаться

new DefaultValuesProvider()

и т.д.. в конструкторе, потому что реализация по умолчанию будет, например, доступ к файловой системе, так что было бы трудно проверить (макетировать их). Я знаю, что могу использовать сеттеры (как в DI), но как насчет значений по умолчанию?

EDIT: После того, как вы ответите полностью, я думаю, что здесь лучше всего иметь сеттеры, позволяющие клиентам предоставлять свои собственные реализации ValuesProvider и ValuesMapper. Но как создать реализации по умолчанию? Я хотел бы отделить создание экземпляров от логики, поэтому я не хочу использовать новый DefaultValueProvider () здесь. Применяется ли здесь заводская модель? Если да, то как и где мне его использовать?

Ответы [ 2 ]

2 голосов
/ 02 июля 2011

Как насчет:

public void setValuesProvider(ValuesProvider valuesProvider) {
    this.valuesProvider = valuesProvider;
}

public Result parse(String parameterName) {
    if (valuesProvider == null) {
        valuesProvider = new DefaultValuesProvider();
    }

    List<Values> values = valuesProvider.getValues(parameterName);
    // do other stuff on values
    // ...
    return valuesMapper.transformValues(values, configuration);
}

Как отмечает Марк, инъекция в конструктор может быть лучшим способом.Это будет выглядеть следующим образом:

public Parser(ValuesConfiguration configuration) {
    this(configuation, new DefaultValuesProvider());
}

public Parser(ValuesConfiguration configuration, ValuesProvider valuesProvider) {
    this.configuration = configuration;
    this.valuesProvider = valuesProvider;
}


public Result parse(String parameterName) {
    List<Values> values = valuesProvider.getValues(parameterName);
    // do other stuff on values
    // ...
    return valuesMapper.transformValues(values, configuration);
}

Я также согласен с его кратким изложением преимуществ:

Это позволит вам сделать зависимость окончательной / только для чтения.Внедрение свойств предоставляет довольно слабые гарантии относительно инвариантов - например, вы можете продолжать изменять зависимость для одного и того же экземпляра, и это, вероятно, не то, что вам нужно.

Однако есть и недостатки:

  1. Количество конструкторов является экспоненциальным по отношению к числу необязательных параметров, что вызывает большое делегирование и дублирование JavaDoc для параметров.Поэтому я бы использовал инжекцию в конструктор, только если есть несколько необязательных зависимостей.У вас есть 2, что о моем пороге боли для такого рода вещей.
  2. Внедрение в конструктор не очень удобно для подклассов, так как конструкторы подкласса должны будут повторно объявить зависимости суперкласса.В вашем случае подклассу, который хочет предоставить все параметры конфигурации, понадобится 4 конструктора ... и если он добавляет необязательный параметр, лучше не делать этого с инжекцией конструктора, поскольку для этого потребуются еще 4 конструктора.

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

public void setValuesProvider(ValuesProvider valuesProvider) {
    if (this.valuesProvider != null) {
        throw new IllegalStateException("Dependency already set");
    }
    this.valuesProvider = valuesProvider;
}
0 голосов
/ 02 июля 2011

Используйте Google Guice или аналогичную инфраструктуру внедрения зависимостей:

http://code.google.com/p/google-guice/

Идеально подходит для ваших нужд.Вы можете настроить несколько модулей, описывающих, какие реализации будут созданы для конкретных интерфейсов.Вы можете использовать ProductionModule и UnitTestModule, например.

...