【问题标题】:TinyIoC and the decorator patternTinyIoC 和装饰器模式
【发布时间】:2013-07-01 08:51:35
【问题描述】:

谁能提供一个简单的例子来说明如何使用TinyIoC 实现装饰器模式?

之前的question 显示了如何在Ninject 中执行此操作,其中包含以下内容:

Bind<IRepository>().To<MoreAdvancedRespository>
               .WhenInjectedInto<TrickyRepository>();
Bind<IRepository>().To<SomeSimpleRepository>
               .WhenInjectedInto<MoreAdvancedRespository>();

【问题讨论】:

  • 我认为你已经超越了 TinyIoC。是时候继续讨论“大男孩”了。
  • 这并不总是那么容易 - TinyIoC 具有独特的品质,它是一个大小适中的代码文件,您只需将其放入项目而不是一个或多个额外的程序集,并且编译到 40k 多一点IL 代码(.NET 4.0 上的发布配置),这在许多项目中都很重要;我目前仅出于这个原因使用它。也就是说,我发现的严重缺陷的数量正在增加,但这并不总是通过简单地转移到不同的工具来解决。但是,如果 OP 可以,他当然应该在这种情况下,是的。

标签: design-patterns decorator ioc-container tinyioc


【解决方案1】:

你不能使用自动装配,所以你必须为每个装饰器注册一个 lambda:

// Register the repository
container.Register<SomeSimpleRepository>();

// Register the inner decorator
container.Register<MoreAdvancedRespository>(() => new MoreAdvancedRespository(
    container.Resolve<SomeSimpleRepository>(),
    container.Resolve<ISomeOtherDependency>()));

// Register the outer decorator
container.Register<IRepository>(() => new TrickyRepository(
    container.Resolve<MoreAdvancedRespository>(),
    container.Resolve<ISomeOtherDependency>()));

您必须对系统中的每个存储库重复此操作。

【讨论】:

  • 嗯,使用命名注册和/或NamedParameterOverloads,在实践中会更容易一些,但这通常是它所需要的努力,是的。
  • @TeaDrivenDev:请添加一个显示这一点的答案。我认为这会对 OP 有所帮助。
【解决方案2】:

在尝试设置此示例一段时间后,简短的回答是:TinyIoC 不能正确地做到这一点,至少如果我们谈论的是传统意义上的“存储库”并希望它们被视为容器中的单例。

话虽如此,这样的作品:

public interface IRepository { }

public class MoreAdvancedRepository : IRepository
{
    public MoreAdvancedRepository(IRepository innerRepository, ISomeOtherDependency otherDependency) { }
}

public class TrickyRepository : IRepository
{
    public TrickyRepository(IRepository innerRepository, ISomeOtherDependency otherDependency) { }
}

public class SimpleRepository : IRepository { }

public interface ISomeOtherDependency { }

public class SomeOtherDependencyWeasel : ISomeOtherDependency { }


// Register the other dependency
container.Register<ISomeOtherDependency, SomeOtherDependencyWeasel>();

// Register the "innermost" repository with a name
container.Register<IRepository, SimpleRepository>("simple");

// Register the inner decorator implementation, also with a name, and telling the container what to do for the dependency called "innerRepository"
container.Register<IRepository>((c, p) => c.Resolve<MoreAdvancedRepository>(new NamedParameterOverloads() { { "innerRepository", c.Resolve<IRepository>("simple") } }), "advanced");

// Register the outer decorator the same way, only without a name for the registration, so this will be what's resolved whenever IRepository is requested without specifying a name
container.Register<IRepository>((c, p) => c.Resolve<TrickyRepository>(new NamedParameterOverloads() { { "innerRepository", c.Resolve<IRepository>("advanced") } }));

// Resolve stuff to check how the registration worked out
var simple1 = container.Resolve<IRepository>("simple");
var simple2 = container.Resolve<IRepository>("simple");
var advanced1 = container.Resolve<IRepository>("advanced");
var advanced2 = container.Resolve<IRepository>("advanced");
var tricky1 = container.Resolve<IRepository>();
var tricky2 = container.Resolve<IRepository>();

Assert.IsType<SimpleRepository>(simple1); // this passes, unsurprisingly
Assert.Same(simple1, simple2); // this passes, too, as simple Register<TResolve, TImpl>() calls are implicitly .AsSingleton()
Assert.IsType<MoreAdvancedRepository>(advanced1); // passes
Assert.IsType<TrickyRepository>(tricky1); // passes

Assert.Same(advanced1, advanced2); // this fails, as Register<TResolve>(Func<TResolve, TinyIoCContainer, NamedParameterOverloads>) calls are implicitly .AsMultiInstance() and can not be changed to .AsSingleton() 
Assert.Same(tricky1, tricky2); // fails for the same reason

现在人们可能会尝试通过这样做来欺骗容器(我曾经):

container.Register<MoreAdvancedRepository>((c, p) => c.Resolve<MoreAdvancedRepository>(new NamedParameterOverloads() { { "innerRepository", c.Resolve<IRepository>("simple") } })); // always .AsMultiInstance()

container.Register<IRepository, MoreAdvancedRepository>("advanced"); // implicitly .AsSingleton(), so only one instance should be created and then returned for subsequent calls

然后可以将其包装在扩展方法中,以便在容器上再次调用单个方法。不幸的是,这不起作用 - 在尝试解析名为“advanced”的IRepository 注册时,容器显然不再找到MoreAdvancedRepository 的先前显式注册并抛出TinyIoCResolutionException

所以,如果
- 每种不同的存储库类型仅解析一次或
- 每次解决特定存储库类型时都会创建一个新实例,这没有问题。

否则,您可能需要为这些类回退到穷人的 DI 或使用不同的 IoC 容器。

【讨论】:

    猜你喜欢
    • 2011-07-18
    • 2014-10-29
    • 2011-03-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-14
    • 2012-08-28
    相关资源
    最近更新 更多