65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 IoC 容器构建可插拔应用程序

starIconstarIconstarIconstarIconemptyStarIcon

4.00/5 (6投票s)

2012年4月7日

CPOL

3分钟阅读

viewsIcon

29558

downloadIcon

394

在本文中,我将展示如何使用 IoC 容器构建一个可插拔应用程序。

引言

在构建应用程序时,我们总是面临应用程序开发期间和之后的许多更改。提前为变更做好准备是灵活应用程序的关键特征。 IoC 容器代表控制反转容器。它经常用于我们不想在应用程序中直接硬编码合同/接口实现的逻辑的情况。通过遵循合同优先的设计原则并将实例化责任传递给 IoC 容器,我们可以创建一个可扩展且灵活的应用程序。

演示应用程序

假设我们要设计一个可以将文本输出到不同输出通道(例如文件输出通道和控制台输出通道)的应用程序。我们首先可以做的是定义一个输出合同,以便主应用程序遵循该合同输出文本,而无需知道具体的输出通道。

public interface IOutputService
{
    void WriteLine(string data);
}

定义合同后,我们可以有两种不同的实现,即文件输出通道和控制台输出通道。

文件输出:

public class FileOutput: IOutputService
{
    public void WriteLine(string data)
    {
        using (StreamWriter sw = new StreamWriter("test.txt", false))
        {
            sw.WriteLine(data);
        }
    }
}

控制台输出:

public class ConsoleOutput: IOutputService
{
    public void WriteLine(string data)
    {
        Console.Write(data);
    }
}

在应用程序中获取输出通道的最简单方法是拥有一个通道工厂,该工厂将创建所请求的输出通道的实例。 但是,这样做会将实例化逻辑硬编码到我们的应用程序中。 如果我们想添加一个新的输出通道,例如网络输出通道,那么我们唯一的意见就是更改代码。

使演示应用程序可插拔

借助 IoC 容器,我们可以轻松地创建一个可插拔应用程序,而无需进行太多编码。 想法是定义一个基本库,它将具有如下的 API

public interface IIoCContainer
{
    T Resolve<t>();
    IEnumerable<t> ResolveAll<t>();
}

此 API 为应用程序提供了一种检索指定类型的单个实例或实例集合的方法,因此您可以使用 IOutputService interface 来获取特定的输出通道。 IoC 容器 API 允许我们使用任何 IoC 容器,例如 Unity 或 StructureMap。 在应用程序启动时,我们使用 BootStrapper 来设置 IoC 容器。

BootStrapper:

public class BootStrapper
{
    public static void StartUp()
    {
        RegisterIoCContainer();
    }

    private static void RegisterIoCContainer()
    {
        ObjectFactory.Initialize(x => { x.PullConfigurationFromAppConfig = true; });
        IoC.IoCContainerManager.IoCContainer = new StructureMapIoCContainer();
    }
}

使用 BootStrapper 初始化 IoC 容器。

BootStrapper.StartUp();

初始化 IoC 容器后,应用程序可以直接使用 IoC 容器 API 来获取 IOutputService

IEnumerable<ioutputservice> outputServices = 
          IoC.IoCContainerManager.IoCContainer.ResolveAll<ioutputservice>();
outputServices.ToList().ForEach(o => o.WriteLine("Hello World!"));

为了使我们的应用程序更加灵活,并允许我们在生产环境中切换不同的输出通道而无需重新编译,我决定不直接在主应用程序中引用输出通道实现。

输出通道实现将通过后期生成事件命令行复制到主应用程序中。

IOutputService 和实际实现之间的映射在应用程序配置文件中定义。

<configSections>
    <section name="StructureMap" 
      type="StructureMap.Configuration.StructureMapConfigurationSection,StructureMap"/>
</configSections>

<StructureMap>
    <PluginFamily Type="OutputService.IOutputService" 
             Assembly="OutputService" DefaultKey="Default">
      <Plugin Type="FileOutput.FileOutput" 
             Assembly="FileOutput" ConcreteKey="Default"/>
      <Plugin Type="ConsoleOutput.ConsoleOutput" 
             Assembly="ConsoleOutput" ConcreteKey="Console"/>
    </PluginFamily>
</StructureMap>

整个应用程序如何工作

当调用 IoCContainer.ResolveAll<ioutputservice>() 时,应用程序会将调用转发到 IoC 容器,然后 IoCContainer 将基于配置文件中的映射来获取特定的实现。

在获取 IOutputService 的集合后,调用 IOutputServiceWriteLine 将“Hello World”写入到每个输出通道。

摘要

借助 IoC 容器,我们可以快速实现一个简单的可插拔应用程序。 这个可插拔应用程序可以利用配置文件为定义的合同添加新的实现,而无需更改代码。

Using the Code

该代码在 Visual Studio 2010 中开发。您可以创建自己的 IOutputService 实现并将其添加到配置文件中,以尝试该演示应用程序。

© . All rights reserved.