结构图和创建具有初始状态的对象

发布于 2024-10-09 20:10:19 字数 673 浏览 0 评论 0原文

我有一个需要向其中注入依赖项的对象

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 的依赖并设置初始状态?或者这是与框架作斗争的情况之一是代码应该重构的标志吗?

I have an object which needs a dependency injected into it

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;
}

For arguments sake, lets assume that I can't pass the round parameter into the GetLadderEntries call itself.

Using StructureMap, how can I inject the dependency on the IMatchRepository and set the initial state? Or is this one of those cases where struggling against the framework is a sign the code should be refactored?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

怀中猫帐中妖 2024-10-16 20:10:19

您始终可以使用构造函数参数作为默认值。我将以下内容用于 sqlconnection 的默认实例。

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

还有其他方法,但我记不起来了。

编辑:这也是可以完成的另一种方法。我从这里找到了这个:
http://www.theabsent Mindcoder.com/2010/05 /struct-map-26-constructor-arguments.html

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

如果 round 的值是通过方法确定的,您可以使用 lambda 表达式指定它来加载值,如下所示

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

希望这是有道理的。但要回答你的问题,是的,这是可能的,而且很容易。

You can always use constructor parameters for default values. I used the following for a default instance of a sqlconnection.

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

There are other ways as well but I don't remember them off the top of my head.

EDIT: Here is another way it could be done as well. I found this one from here:
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);

If the value of round was determined from a method you could specify it with a lambda expression to load the value like so

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

Hope this makes sense. But to answer your question yes it is possible and pretty easily.

墨离汐 2024-10-16 20:10:19

大多数 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实例的状态。除此之外,当 Round 从未设置时,FootballLadder 类应该抛出 InvalidOperationException。我认为这样的检查很好而且很干净,但确实会让你编写更多的代码。

3) 将 IRoundProvider 注入到 FootballLadder 构造函数中:

正如 Spinon 所写,您可以实现 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

我希望这有帮助。

Most DI frameworks allow you to inject primitives in constructors as Spinon showed you. When possible, I try to refactor my code in a way that I don't need complex configurations. Often this makes my application code the most understandable, with the least surprises (low number of WTFs per minute ;-)). You have to balance this out carefully, because sometimes complex configurations could make your application code simpler.

Here are some possible suggestions for refactorings:

1) Use a factory:

Using a factory is useful when clients must control the round value:

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

This way you can inject a IFootballLadderFactory and allow clients to call:

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

2) Use a property:

You can remove the round argument from the constructor and change it in to a get/set property. This is both useful when clients must be able to control the round value or when using a factory:

public class FootballLadder
{
    private IMatchRepository matchRepo;

    public FootballLadder(IMatchRepository matchRepository)
    {
    }

    public int Round { get; set; }
}

And implementation of the IFootballLadderFactory for instance could look like this:

public class CastleFootballLadderFactory : IFootballLadderFactory
{
    public IWindsorContainer Container;

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

Or a client could set the Round property:

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

Please be careful with this last example. The client should normally not have to care about the lifetime of the dependency. In this case, we're changing the state of an injected dependency. This prevents us from changing the lifetime of this dependency, because in that case the state of the ladder instance, could be changed from under the client's feet. Besides this, the FootballLadder class should throw an InvalidOperationException when the Round was never set. I think such a check is nice and clean, but does make you write a bit more code.

3) Inject a IRoundProvider into the FootballLadder constructor:

As Spinon wrote, you can implement a IRoundProvider, but instead of using it in your configuration, you can use it as constructor argument.

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

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

4) Create a sub type specific for your DI configuration:

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

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

Now you can register the it as follows:

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

Downside of this is that you have this extra code with itself is plain configuration code. Besides that, when the dependencies of the FootballLadder change, you have to change the DIFootballLadder as well.

I hope this helps.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文