Структура карты и создание объектов с начальным состоянием - PullRequest
1 голос
/ 03 января 2011

У меня есть объект, которому нужно вставить зависимость

public class FootballLadder
{
    public FootballLadder(IMatchRepository matchRepository, int round)
    {
        // set initial state
        this.matchRepo = matchRepository;
        this.round = round;
    }

    public IEnumerable<LadderEntry> GetLadderEntries()
    {
        // calculate the ladder based on matches retrieved from the match repository
        // return the calculated ladder
    }

    private IMatchRepository matchRepo;
    private int round;
}

Ради аргументов, давайте предположим, что я не могу передать параметр round в сам вызов GetLadderEntries.

Используя StructureMap, как я могу внедрить зависимость в IMatchRepository и установить начальное состояние? Или это один из тех случаев, когда борьба с фреймворком является признаком того, что код должен быть реорганизован?

Ответы [ 2 ]

2 голосов
/ 03 января 2011

Вы всегда можете использовать параметры конструктора для значений по умолчанию.Я использовал следующее для экземпляра sqlconnection по умолчанию.

this.For<SqlConnection>().Use(c => new SqlConnection(ConfigurationManager.ConnectionStrings["conn"].ConnectionString));

Есть и другие способы, но я не помню их на макушке.

РЕДАКТИРОВАТЬ: Здесьэто еще один способ сделать это.Я нашел это здесь: http://www.theabsentmindedcoder.com/2010/05/structure-map-26-constructor-arguments.html

x.For<MatchRepository>().Use<MatchRepository>();
x.For<IFootballLadder>().Use<FootballLadder>()
    .Ctor<int>("round")
    .Is(3);

Если значение round было определено с помощью метода, вы можете указать его с помощью лямбда-выражения, чтобы загрузить значение, например

.Is(c => c.GetInstance<IRoundProvider>().GetRound())

Надеюсь, это имеет смысл.Но ответить на ваш вопрос: да, это возможно и довольно легко.

1 голос
/ 04 января 2011

Большинство каркасов DI позволяют вам внедрять примитивы в конструкторы, как показало Spinon .Когда это возможно, я пытаюсь реорганизовать свой код так, чтобы мне не требовались сложные конфигурации.Часто это делает мой код приложения наиболее понятным, с наименьшим количеством сюрпризов (небольшое количество WTF в минуту ;-)).Вы должны тщательно сбалансировать это, потому что иногда сложные конфигурации могут упростить код вашего приложения.

Вот несколько возможных предложений по рефакторингу:

1) Использовать фабрику:

Использование фабрики полезно, когда клиенты должны контролировать round значение:

public interface IFootballLadderFactory
{
    FootballLadder CreateNew(int round);
}

Таким образом, вы можете ввести IFootballLadderFactory и разрешить клиентам звонить:

var ladder = this.footballLadderFactory.CreateNew(3);

2) Использовать свойство:

Вы можете удалить аргумент round из конструктора и изменить его на свойство get / set.Это полезно, когда клиенты должны иметь возможность контролировать значение round или при использовании фабрики:

public class FootballLadder
{
    private IMatchRepository matchRepo;

    public FootballLadder(IMatchRepository matchRepository)
    {
    }

    public int Round { get; set; }
}

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

public class CastleFootballLadderFactory : IFootballLadderFactory
{
    public IWindsorContainer Container;

    public FootballLadder CreateNew(int round)
    {
        var ladder = this.Container.Resolve<FootballLadder>();
        ladder.Round = round;
        return ladder;
    }
}

Или клиент может установить свойство Round:

public class Client
{
    public Client(FootballLadder ladder)
    {
       ladder.Round = 3;
    }
}

Пожалуйста, будьте осторожны с последним примером.Клиенту обычно не нужно заботиться о времени жизни зависимости.В этом случае мы меняем состояние внедренной зависимости.Это не позволяет нам изменить время жизни этой зависимости, потому что в этом случае состояние экземпляра ladder может быть изменено из-под ног клиента.Кроме того, класс FootballLadder должен выдавать InvalidOperationException, когда Round никогда не был установлен.Я думаю, что такая проверка хороша и чиста, но заставляет вас писать немного больше кода.

3) Внедрить IRoundProvider в конструктор FootballLadder:

Как писал Спинон, вы можете реализовать IRoundProvider, но вместо того, чтобы использовать его в своей конфигурации, вы можете использовать его в качестве аргумента конструктора.

public class FootballLadder
{
    private IMatchRepository matchRepo;
    private int round;

    public FootballLadder(IMatchRepository matchRepository,
        IRoundProvider roundProvider)
    {
       this.round = roundProvider.GetRound();
    }        
}

4) Создать определенный подтипдля вашей конфигурации DI:

public class DIFootballLadder : FootballLadder
{
    private const int Round = 3;

    public DIFootballLadder(IMatchRepository matchRepository)
        : base(matchRepository, Round)
    {
    }
}

Теперь вы можете зарегистрировать его следующим образом:

x.For<FootballLadder>().Use<DIFootballLadder>();

Недостатком этого является то, что у вас есть этот дополнительный код с собой в простой конфигурациикод.Кроме того, когда изменяются зависимости FootballLadder, вам также необходимо изменить DIFootballLadder.

Надеюсь, это поможет.

...