Castle揭密2----IOC(2)

   Castle IOC是Castle的核心和灵魂。有一句话是这么说的,如果要理解castle和spring这样的框架,必须首先理解其IOC。当然,如果停留在使用层次那就不需要了。本章帮你一起揭密Castle IOC本质。

首先来看个例子。假设有这么个需求,实现给一个通知功能,把一些消息通知一群朋友。需求分析如下:

 

image

参与者,接受者,通知内容,通知方式,消息服务协调者(manager,service)

1. 参于actor 操作员(登录者,管理员等) 和 朋友

简单期间,客户用string array实现

String[] friendsList = new String[] { "john", "steve", "david" };

2. 通知形式。为了扩充通知方式,抽出一接口先。

public interface IEmailSender
{
    void Send(String from, String to, String message);
}

实现smtp方式,注意,fake方法。示意而已。

注意,这里实现了三个构造函数。

   1: public class SmtpEmailSender : IEmailSender     
   2:    {
   3:  
   4:        #region IEmailSender Members
   5:  
   6:        public virtual void Send(String from, String to, String message)
   7:        {
   8:            Console.WriteLine("Sending e-mail from {0} to {1} with message '{2}'", from, to, message);
   9:        }
  10:  
  11:        #endregion
  12:  
  13:        private String host = "my.default.host";
  14:        private int port = 25;
  15:  
  16:        public SmtpEmailSender()
  17:        {
  18:        }
  19:  
  20:        public SmtpEmailSender(String host, int port)
  21:        {
  22:            this.host = host;
  23:            this.port = port;
  24:        }
  25:        private String[] _hosts;
  26:  
  27:        public SmtpEmailSender(String[] hosts)
  28:        {
  29:            if (hosts == null) throw new ArgumentNullException("hosts");
  30:            if (hosts.Length == 0) throw new ArgumentException("hosts is empty");
  31:  
  32:            _hosts = hosts;
  33:        }
  34:  
  35:    }

3. 通知内容,为了通知很丰富格式多样的内容,抽出一模板接口,用不同方法实现各种内容。例如纯文本的,html的,特殊模板语法的等

public interface ITemplateEngine
{
 
    String Process(String templateName);
}

实现一dummy NVelocity类,只返回一简单字符串而已

public class NVelocityTemplateEngine :ITemplateEngine   
{
    public NVelocityTemplateEngine()
    {
    }
 
    #region ITemplateEngine Members
 
    public string Process(string templateName)
    {
        return "some content";
    }
 
    #endregion
}

4. 抽象出通知内容和通知方式后,该主角出场。主角是一个通知service,通过使用IEmailSender发送ITemplateEngine 返回的内容给我的朋友,此功能封装在里INewsletterService,定义了Dispatch方法作为分发service。

public interface INewsletterService
{
    void Dispatch(String from, String[] targets, String messageTypeName);
}

SimpleNewsLetterService实现了这一服务,定义了两个构造,第二个构造是为了测试对sometype的依赖用,没有他意。这里聚合依赖接口IEmailSender和ITemplateEngine,不是具体类型。这里通过构造器注入的方式实现解藕。

   1: public class SimpleNewsLetterService :INewsletterService    
   2:    {
   3:  
   4:  
   5:        private IEmailSender _sender;
   6:        private ITemplateEngine _templateEngine;
   7:  
   8:        public SimpleNewsLetterService(IEmailSender sender, ITemplateEngine templateEngine)
   9:        {
  10:            _sender = sender;
  11:            _templateEngine = templateEngine;
  12:        }
  13:  
  14:        public SimpleNewsLetterService(Type sometype, IEmailSender sender, ITemplateEngine templateEngine)
  15:        {
  16:            if (sometype == null) throw new ArgumentNullException("sometype");
  17:  
  18:            _sender = sender;
  19:            _templateEngine = templateEngine;
  20:        }
  21:        public void Dispatch(String from, String[] targets, String messageTypeName)
  22:        {
  23:            String message = _templateEngine.Process(messageTypeName);
  24:    
  25:            foreach(String target in targets)
  26:            {
  27:                _sender.Send(from, target, message);
  28:            }
  29:        }
  30:    }

5. 客户端调用如下。(请引入相应dll,我使用的是build 936,目前新的castle还没有release,较早的release是1.0 Release Candidate 3 - September 20, 2007 ,封装的是nhibernate1.2。

   1: IWindsorContainer container = new WindsorContainer();   
   2:            container.AddComponent("mailSender", typeof (IEmailSender), typeof (SmtpEmailSender));
   3:            container.AddComponent("NVelocityEngine", typeof (ITemplateEngine), typeof (NVelocityTemplateEngine));
   4:            container.AddComponent("newLetter", typeof (INewsletterService), typeof (SimpleNewsLetterService));
   5:  
   6:  
   7:  
   8:            String[] friendsList = new String[] { "john", "steve", "david" };
   9:  
  10:            INewsletterService newsLetterService = container["newLetter"] as INewsletterService ;
  11:            newsLetterService.Dispatch("peter", friendsList, "hello world");

解释

1:首先new一castle IOC容器WindsorContainer,放入三个component

2:调用的时候通过AddComponent方法,有很多重载。其中一个,AddComponent(string key, System.TypeserviceType , System.Type classType), serviceType是抽象服务type,定义了基本契约。可以是类,接口,抽象类等。classType是实现类 。key必须唯一,相同classType和serviceType不同key可以加入。

3:获取容器中的的组件可以通过Key或者service Type。

4:最后一个AddComponent调用完,容器自动确定IEmailSender,ITemplateEngine ,INewsletterService的依赖关系,并且通过依赖注入(这里是构造器注入)将ITemplateEngine和IEmailSender的实现注入给INewsletterService

5:这里SmtpEmailSender和NVelocityTemplateEngine是调用的默认构造函数,SimpleNewsLetterService是调用的public SimpleNewsLetterService(IEmailSender sender, ITemplateEngine templateEngine)。

问题来了:

1:容器怎么知道mailSender,NVelocityEngine和newLetter的依赖关系?

2:容器怎么会调用到SimpleNewsLetterService的构造函数将mailSender和templateEngine传入的?

3:容器如何管理组件生命周期的?

4:容器对其它注入方式如何支持?如Type3-setter

 

带着这些问题,让我们来解读大师们的源码,在这里我们领略下成功的open source框架架构设计所带来的乐趣。

未完,待续。。。

附:依赖注入可以通过代码设置,更多的是通过配置文件注入。Spring主要就是配置注入。关于xml配置的在开发中的优缺点,xml配置是否过渡使用,动态语言带来的革新对配置的冲击和影响,有兴趣可以另外探讨

配置文件示范:     

Config.XML如下,细节不解释了。看看注释就明白了。另外不管是spring,castle还是microsoft enterprise library都对sectionHandler进行扩充,为了便于在.net开发中在web.config或者app.config进行配置注入。例如Castle.Windsor.Configuration.AppDomain.CastleSectionHandler:

<section name="castle" type="Castle.Windsor.Configuration.AppDomain.CastleSectionHandler, Castle.Windsor"/>

<castle>

。。。

</castle>

注意下文的对array参数和list的配置,更多的还有泛型type的配置等,依赖的配置。如<sender>${smtpemailsender}</sender>,这里是揭示原理篇,更多使用请参考其它文章或者我的.net solution架构汇总。

 
  
<?xml version="1.0" encoding="utf-8" ?>

<configuration>

        <!-- Please note the usage of service and type attributes.

      Windsor will automatically installs the components and 

      facilities with a type attribute (and optionally service for components)-->

        <!--This is a facility configuration example:

      <facilities>

      <facility id="nhibernate" type="Full Type Name, AssemblyName">

      </facility>

      </facilities>

      -->

  <components>

    <component id="smtpemailsender"

    service="CastleIOC.IOC.Component.IEmailSender, CastleIOC"

    type="CastleIOC.IOC.Component.SmtpEmailSender, CastleIOC">

      <parameters>

        <hosts>

          <!-- The following nodes will be converted to an array of String 

          which is what the component expects. If the component expected

          an int[], the MicroKernel would try to convert each element 

          as well.The MicroKernel also supports

          - Dictionaries

          <dict keyType="System.String, mscorlib" valueType="System.Int32, mscorlib">

          <item key="key1">1</item>

          <item key="key2">2</item>

          </dict>

          - Lists 

          <list type="typetobeconverted">

          <item>item1</item>

          <item>item2</item>

          </list>

-->

          <array>

            <elem>MyFirstHost</elem>

            <elem>OtherHost</elem>

          </array>

        </hosts>

      </parameters>

    </component>

    <component id="newLetter"

    service="CastleIOC.IOC.Component.INewsletterService, CastleIOC"

    type="CastleIOC.IOC.Component.SimpleNewsLetterService, CastleIOC">

      <parameters>

        <!-- This argument will be converted to a System.Type        -->

        <sometype>System.Collections.IList, mscorlib</sometype>

        <!-- Here we're overriding which IEmailSender implementation 

this component should use                               -->

        <sender>${smtpemailsender}</sender>

        <!-- Same thing for ITemplateEngine                          -->

        <templateEngine>${nvelocityengine}</templateEngine>

      </parameters>

    </component>

    <component id="nvelocityengine"

    service="CastleIOC.IOC.Component.ITemplateEngine, CastleIOC"

    type="CastleIOC.IOC.Component.NVelocityTemplateEngine, CastleIOC" />

  </components>

</configuration>



你可能感兴趣的:(cast)