享受代码,享受人生

SOA is an integration solution. SOA is message oriented first.
The Key character of SOA is loosely coupled. SOA is enriched
by creating composite apps.
posts - 207, comments - 2345, trackbacks - 162, articles - 44
  博客园 :: 首页 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理
首先来看一下在Castle项目中用于示范其Ioc功能的一个小例子:


   1:  public interface IEmailSender
   2:  {
   3:      void Send(String from, String to, String message);
   4:  }
   5:   
   6:  public interface ITemplateEngine
   7:  {
   8:      String Process(String templateName);
   9:  }
  10:   
  11:  public interface INewsletterService
  12:  {
  13:      void Dispatch(String from, String[] targets, String message);
  14:  }
  15:   
  16:  public class SmtpEmailSender : IEmailSender
  17:  {
  18:      private String _host;
  19:      private int _port;
  20:   
  21:      public SmtpEmailSender(String host, int port)
  22:      {
  23:          _host = host;
  24:          _port = port;
  25:      }
  26:   
  27:      public virtual void Send(String from, String to, String message)
  28:      {
  29:          Console.WriteLine("Sending e-mail from {0} to {1} with '{2}'", 
  30:              from, to, message );
  31:      }
  32:  }
  33:   
  34:  public class NVelocityTemplateEngine : ITemplateEngine
  35:  {
  36:      public virtual String Process(String templateName)
  37:      {
  38:          return "Some template content";
  39:      }
  40:  }
  41:   
  42:  public class SimpleNewsletterService : INewsletterService
  43:  {
  44:      private IEmailSender _sender;
  45:      private ITemplateEngine _templateEngine;
  46:   
  47:      public SimpleNewsletterService
  48:          (IEmailSender sender, ITemplateEngine templateEngine)
  49:      {
  50:          _sender = sender;
  51:          _templateEngine = templateEngine;
  52:      }
  53:   
  54:      public void Dispatch(String from, String[] targets, String message)
  55:      {
  56:          String msg = _templateEngine.Process(message);
  57:   
  58:          foreach(String target in targets)
  59:          {
  60:              _sender.Send(from, target, msg);
  61:          }
  62:      }
  63:   
  64:  }


Ioc的作用就是能够利用容器自动的构建出一个SimpleNewsletterService对象,而不是由用户直接的创建并传递其参数,这样就可以进一步降低对象与对象之间耦合程度,从而方便的替换其实现。在上例中将采用构造器设值的方式,来实现对象依赖关系的注入。下面给出Castle的实现方式:

BasicUsage.xml
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <components>
        <component id="smtpemailsender">
            <parameters>
                <host>localhost</host>
                <port>110</port>
            </parameters>
        </component>
    </components>
</configuration>

   1:  public static void Main()
   2:  {
   3:      IWindsorContainer container = 
   4:      new WindsorContainer( new XmlInterpreter("../BasicUsage.xml") );
   5:   
   6:      container.AddComponent( "newsletter", 
   7:          typeof(INewsletterService), typeof(SimpleNewsletterService) );
   8:      container.AddComponent( "smtpemailsender", 
   9:          typeof(IEmailSender), typeof(SmtpEmailSender) );
  10:      container.AddComponent( "templateengine", 
  11:          typeof(ITemplateEngine), typeof(NVelocityTemplateEngine) );
  12:   
  13:      String[] friendsList = new String[] { "john", "steve", "david" };
  14:   
  15:      // Ok, start the show
  16:   
  17:      INewsletterService service = 
  18:          (INewsletterService) container["newsletter"];
  19:      service.Dispatch("hammett at gmail dot com",
  20:                                friendsList, "merryxmas");
  21:  }

不断向容器中加入各种类型的对象,加入过程中容器自动建立起它们之间的依赖关系,最后当我们从容器取出对象的时候,就是已经配置好依赖关系的对象。以上就是Castle的工作模式,不过Castle中的配置过程过于透明,在你完全意料不到的情况下,对象的依赖关系就按照对象被注入容器的先后顺序被设定了。非常的自动,非常的透明带来的就是某些时候的莫名奇妙。

再来看看ObjectBuild的实现方式,首先你需要通过属性(Attibute)或者配置文件的方式显式的设定依赖关系:

  42:  public class SimpleNewsletterService : INewsletterService
  43:  {
  44:      private IEmailSender _sender;
  45:      private ITemplateEngine _templateEngine;
  46:   
  47:      public SimpleNewsletterService(
  48:              [Dependency(CreateType = typeof(SmtpEmailSender))]
  49:              IEmailSender sender, 
  50:              [Dependency(CreateType = typeof(NVelocityTemplateEngine))] 
  51:              ITemplateEngine templateEngine)
  52:      {
  53:          _sender = sender;
  54:          _templateEngine = templateEngine;
  55:      }
  56:   
  57:      public void Dispatch(String from, String[] targets, String message)
  58:      {
  59:          String msg = _templateEngine.Process(message);
  60:   
  61:          foreach(String target in targets)
  62:          {
  63:              _sender.Send(from, target, msg);
  64:          }
  65:      }
  66:   
  67:  

 

然后再利用Build直接的构造出对象

public static void Main(string[] args)
{
    string config = string.Format(
      @"<object-builder-config xmlns='pag-object-builder'>
        <build-rules>
            <build-rule type='{0}' mode='Singleton'>
              <mapped-type type='{1}'/>                           
              <constructor-params>
                 <value-param type='System.String'>localhost</value-param>
                 <value-param type='System.Int32'>110</value-param>
              </constructor-params>
            </build-rule>
         </build-rules>
        </object-builder-config>"
, FullNameIEmailSender, FullNameSmtpEmailSender);

    Builder builder = new Builder(ObjectBuilderXmlConfig.FromXml(config));
    Locator locator = CreateLocator();

    INewsletterService newsletterService =
       builder.BuildUp<SimpleNewsletterService>(locator, null, null);


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

    // Ok, start the show

    newsletterService.Dispatch("hammett at gmail dot com",
                                  friendsList, "merryxmas");
}

从以上我们可以看出
1.  ObjectBuild和Sping类似,需要显式指定依赖关系,不过多出通过Attribute来指定的方式,而Castle则依赖于特定的对象注入顺序。可以说各有优缺点,但是我prefer前者,安全第一。(Castle也提供了在配置文件中显式指定的方式。)
2.  ObjectBuild支持直接创建临时对象,而不注入容器。(注意在上面的代码中我们仅仅BuildUp了SimpleNewsletterService,而没有象Castle那样还要将SimpleNewsletterService所依赖的对象也注入容器。)其实在OB中你可以选择是否将对象注入容器,通常情况下Singleton类型的对象才被注入容器。
3.  相对与Castle容器的概念,ObjectBuild如同它的名字所示,更象是一个构建器,你可以通过它来构建任意的对象,这些对象可能会被注入容器,也可能仅仅是一个临时对象,不过在对象的创建过程中你可以加入很多的控制,让它更符合你的需求。
4. 相对而言Castle Ioc的使用更加简洁,而ObjectBuild更加复杂一些(其实比较它们构建对象所需要的语句,OB需要的更少,只是OB的API还不够简洁,需要再做些包装),不过ObjectBuild的定制扩展能力也是相当强大,比如以上通过配置文件和属性(Attribute)结合的配置方式,完全可以通过OB中提供的API搞定,下面就是采用全编程方式的实现:
    public static void Main()
    {

        Builder builder = new Builder();
        Locator locator = CreateLocator();
       
        builder.Policies.SetDefault<ISingletonPolicy>(new SingletonPolicy(true));
       
        ConstructorPolicy policy = new ConstructorPolicy();
        policy.AddParameter(new ValueParameter<string>("localhost"));
        policy.AddParameter(new ValueParameter<int>(110));

        builder.Policies.Set<ICreationPolicy>
            (policy, typeof(SmtpEmailSender), null);
        builder.Policies.Set<ITypeMappingPolicy>
            (new TypeMappingPolicy(typeof(SmtpEmailSender), null),
             typeof(IEmailSender), null);

        builder.Policies.Set<ITypeMappingPolicy>
            (new TypeMappingPolicy(typeof(NVelocityTemplateEngine), null),
             typeof(ITemplateEngine), null);

        ConstructorPolicy policy2 = new ConstructorPolicy();
        policy2.AddParameter(new CreationParameter(typeof(IEmailSender)));
        policy2.AddParameter(new CreationParameter(typeof(ITemplateEngine)));
        builder.Policies.Set<ICreationPolicy>
            (policy2,typeof(SimpleNewsletterService),null); 


        INewsletterService newsletterService =
             builder.BuildUp<SimpleNewsletterService>(locator, null, null);
     
        String[] friendsList = new String[] { "john", "steve", "david" };

        // Ok, start the show

        newsletterService.Dispatch("hammett at gmail dot com",
                                   friendsList, "merryxmas");
    }

5. Castle Ioc和ObjectBuild都支持属性设值(Property Setter)的注入方式,在此不再举例。

Feedback

#1楼   回复  引用  查看    

2006-08-15 13:34 by Colin Han      
????
大幅度发
dfdfdsf
????

:-)

#2楼[楼主]   回复  引用  查看    

2006-08-15 13:43 by idior      
晕 我还没发首页呢(正在发布中) 怎么这么多阅读量

#3楼   回复  引用  查看    

2006-08-15 14:14 by Colin Han      
@ idior
名人嘛,自然关注的人比较多。
我可是你的忠实订阅用户啊。 :)

另外,如果你不打算提交到首页,把页面最下面的高级选项中的[发布]选项钩掉。这样将来提交的时候可以更新提交时间,确保将你的文章放在博客园的首页上。

#4楼[楼主]   回复  引用  查看    

2006-08-15 14:37 by idior      
@Colin Han
以为很快就可以发布好,偷懒没勾,没想到由于调代码格式的问题,发布了好久才成功。

谢谢支持!

#5楼   回复  引用  查看    

2006-08-15 16:39 by 木野狐      
请教一下:

感觉 ObjectBuilder 的用 Attribute 来指定所依赖对象的具体类型,以及用全 API 构造的方式来注入依赖,是否丧失了解耦的实际意义?
还是通过配置文件才能解耦吧?

#6楼   回复  引用  查看    

2006-08-15 16:47 by 浪子      
OB结合config文件,可以做到非常简单,并且配置文件的读入也可以作为OB的一个创建策略。
就像Entlib一样,它封装了一个EnterpriseLibraryFactory(Builder封装) 和 ConfigurationNameMappingStrategy(配置文件读取策略),调用上一样简单傻瓜话...

最近太忙,一直没有深入Enterlib和CAB,关注idior的后续文章...

#7楼   回复  引用  查看    

2006-08-15 18:07 by 浪子      
@木野狐

我倒是觉得解藕,并不是完全不知道依赖的对象,而是不要知道依赖的对象的构造方式。

以前如果出现依赖,我们可能需要自己构造一个依赖的对象出来。

在IoC里的概念,应该是我直接拿来用(当然用之前我需要知道它有些什么可以让我用),但是我不需要去构造它,IoC容器(OB、WindsorContainer、spring.net....)会构造它.

所以对于属性来指定依赖和通过配置文件来指定依赖,他们只是告诉容器依赖的对象是哪个,个人觉得他们在解藕方面的差别不是很大。

#8楼   回复  引用    

2006-08-16 09:20 by yifeng[未注册用户]
@浪子
相反,我不觉得解藕是隐藏构造方式,而是不知释具体类。
为了对该类隐藏对象的构造,只把构建逻辑转移到外部就可以了,犯不上IoC容器。
把具体类别编码到特性中,在我看来,上面演示的代码和下面的代码无异
public SimpleNewsletterService(
[Dependency(CreateType = typeof(SmtpEmailSender))]
SmtpEmailSender sender,
[Dependency(CreateType = typeof(NVelocityTemplateEngine))]
NVelocityTemplateEngine templateEngine)
{
}
所谓参数使用了接口 显得多余。

象这样实现不了依赖倒置。

#9楼[楼主]   回复  引用  查看    

2006-08-16 11:06 by idior      
所谓依赖倒置,控制反转的意思就是class A不再负责他所属成员(通常是接口定义)的创建,而把这个责任交给容器或构建器来处理。

但是我们还是需要告诉容器创建什么的对象,通过配置文件的方式这些信息将与class A完全分离。通过attribute则这部分信息放入了class A的源数据中,耦合关系不如配置文件的方式来的松。

为什么要降低耦合程度?目的就是为了能够方便的替换其实现,不管是attribute还是配置文件能达到这个目的就体现了ioc的价值。

如果你接触过castle的 activerecord你不觉得它的那些Attribute是对业务对象的污染吗? 但是AR仍旧那么流行,因为它方便,简单。

而且Attribute有一个优点那就是是类型安全的,不象配置文件一旦写错一个字母都要查半天。


#10楼   回复  引用    

2006-08-16 13:32 by yifeng[未注册用户]
@idior

理解有分歧。
我不认为依赖倒置只是解除成员构建逻辑(必要不充分)。

IoC是实现依赖倒置的一种手段。

上面我说的有问题。
我的代码在参数中不使用接口,和使用接口还是有分别。
分别在于单元测试时不用改动参数类型。

但很明显,你的代码依赖于特性中声明的类型,即使单元测试也要连带在一起,否则你就要注释掉特性的声明。
而一旦集成ObjectBuilder进行测试,更换下层实现类就必须改动上层代码。
如果要做到下层程序集依赖上层程序集中的业务接口定义,
而现在上层又依赖下层的具体类型的话,那就造成包的双向依赖了。

的确,改动还是很轻易。如果不是几百个参数特性分散在各地的话(是也不怕,查找替换)。

#11楼[楼主]   回复  引用  查看    

2006-08-16 16:13 by idior      
@yifeng
Dependency inject
sorry,我不小心说成了依赖倒置。应该是依赖注入,这样看来至少从定义上来看。 Ioc的主要表现是将对象构造,依赖关系的确定交由容器来完成。

不过使用属性确实会存在不必要的程序依赖,以及需要重新编译的问题。
不过正如我所说能够方便的替换接口的实现是ioc的最终目的,属性在此也不失为一种实现方式。

其实在现实中广泛存在这种不太完美,但是比较方便易行的解决方案,castle的AR也算是一个例子---在业务对象中包含了有关Data Access的attribue。




---
浪子兄写了不少有关OB的文章,可惜我在园子google的时候竟然没有找到,在此也推荐一下。

#12楼   回复  引用  查看    

2006-08-16 16:21 by 浪子      
@yifeng

我想“依赖倒置”,顾名思义只是倒置了而已,依赖还是需要存在的。

我觉得你讲的双向依赖有点不对。

首先,更换下层实现类我不必要改动上层代码,只是idior的示例代码不是太完善
因为他在用Dependecy Attribute的时候产生了具体依赖[Dependency(CreateType = typeof(SmtpEmailSender))],
在OB里面这里完全不必要产生具体依赖的。
我可以只是简单的指定[Dependency(Name="EmailSender")]

然后我在Locator里面注入这个对象
locator.Add(new DependencyResolutionLocatorKey(typeof(IEmailSender), "EmailSender"), new SmtpEmailSender());

当然这里在容器里直接New了一个SmtpEmailSender,同样可以使用OB再Build 一个 IEmailSender,至于具体是哪个IEmailSender 同样可以使用配置文件来设置。

我倒是觉得他们应该是都依赖于你注入时候申明的接口类而已。而不存在互相依赖的关系。


#13楼   回复  引用  查看    

2006-08-16 16:23 by 浪子      
@idior
被我改了静态页面名字之后,google就排到很后面了,基本上g不到了。。。呵呵

------------------------------------
你是不是g "ObjectBuild" :) -- 好像应该是"ObjectBuilder"

#14楼[楼主]   回复  引用  查看    

2006-08-16 16:38 by idior      
@浪子
没错,ob也支持象castle那样的名定位方式,采用这种方式可以更大程度上的解耦。


Ioc的最高境界确实是让对象不知具体类。

#15楼   回复  引用  查看    

2006-08-16 16:46 by Colin Han      
"解耦"应该是基于接口依赖而进行的,也就是说,所有的契约都在接口中定义了。耦合的双方都应该不知道对方是什么,只知道对方符合某个契约。
从这一点来说,配置文件和属性都可以实现解耦的目的。

另一方面来说,我们为什么要解耦?
一、降低复杂度
二、提高柔性

实现这两个目标,应该说封装变化是一个重要的途径。于是,我们需要将变化和复杂的耦合关系封装起来。从这一方面来说,配置文件应该说是优于属性的。因为配置文件将复杂度和变化全部集中在了一个点上(配置文件上)。因此最大化了解耦的价值。而属性,依然将复杂度分散在系统内部的很多点上,因此没有达到最大化解耦的价值。(当然,如果使用名定位,这个问题也得到了解决。)

#16楼[楼主]   回复  引用  查看    

2006-08-16 16:52 by idior      
Colin Han 做了总结陈词。

不过需要注意在OB中使用名定位的存在一个缺点---该对象必须事先已被加入容器。而castle由于对象图的存在,不存在该问题。不过相应的castle的实现比ob复杂了许多。



发表评论

昵称: [登录] [注册]

主页:

邮箱:(仅博主可见)

评论内容:

  登录  注册

[使用Ctrl+Enter键快速提交评论]

0 477258





相关文章:

相关链接: