首先來看一下在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)的注入方式,在此不再舉例。