[浪子学编程][MS Enterprise Library]ObjectBuilder之创建策略祥解(三)
ObjectBuilder之创建策略祥解(三)
-Written by 浪子
摘要:在ObjectBuilder中除了应用了大量的设计模式之外,一个更大的亮点应该算是对IoC的实现.通过CustomerAttributes来实现依赖注入,是一个轻量级的IoC容器.本文将详细阐述ObjectBuilder如何实现IoC.
前言:
IoC即控制反转(Inversion of Control),它的目的就是实现分离,用面向对像的思想来说,就是分离责任.
它遵循一个最基本的原则:Don’t Call us, We will Call you!(好莱坞原则) .
举个简单的例子:
public class A
{
B b;
public void MethodA()
{
b.MethodB();
}
}
public class B
{
public void MethodB();
}
此时我们称MethodA的做法是主动调用,依赖与具体B.按设计模式的概念,我们应该”依赖于抽象,而不是依赖于具体”.我们可能会重构代码如下:
Public Interface IB
{
Void MethodB();
}
Public Class B:IB
{
Public void MethodB()
{
//TODO Something
}
}
Public Class A
{
IB b;
Public void MethodA()
{
b.MethodB();
}
}
可以看到这个重构的过程,其实是对依赖抽象的应用,即面向接口编程.但是这样就松耦合了嘛?不是的.
问 A中的b要如何获得它的实例?
new B()嘛?那不是依赖于具体的B了嘛?
创建类工厂BFactory.Create()?那不是依赖于具体的BFactory了嘛?
创建抽象的类工厂AbstrateFactory? 此时虽然是依赖于抽象了.但是其实已经超过A的职能范围,也就是越权了.它需要分心去关心本不该它关心的细节.它只是想使用B的MethodB方法,而为了用这个方法它不得不去了解B该如何创建?
而从IoC的编程思想来看.A只要知道B有MethodB是自己想要的就可以了.至于B如何创建它是不必关心的,它只要告诉组件组装者(IoC容器)我需要一个IB的实例就够了.IB实例的创建会由组件组装者来实现并传递给A使用.而ObjectBuilder就是这样一个组装者,它替代了以往简单的New方式.
而对象的注入方式有三种:
Type1接口注入(Interface Injection):
Public Class A
{
IB b;
Public void MethodA()
{
//从配置文件读入B的类型,并创建一个实例
Object obj = Activator.CreateInstance(Config.GetType(“B”));
b = (IB)obj;
b.MethodB();
}
}
Type2 设值注入(Setter Injection);:
Public Class A
{
IB b;
//设值注入
Public void SetIB(IB b)
{
this.b = b;
}
Public void MethodA()
{
b.MethodB();
}
}
Type3构造子注入(Constructor Injection):
Public Class A
{
IB b;
//构造子注入
Public void A(IB b)
{
this.b = b;
}
Public void MethodA()
{
b.MethodB();
}
}
用生活的角度来看.Class A就像是个孩子,而Interface IB 是孩子想要的各种东西(如玩具).孩子只会说我要什么什么,他根本不知道这个东西是怎么来的.而作为孩子的奶妈就要想方设法给孩子他想要的东西.ObjectBuilder正是扮演了奶妈这个角色.
关于奶妈如何去获取孩子需要的东西并把它交到孩子手中,就是我们所要讨论的,也正是ObjectBuilder的组装功能.
IoC的组装方式:
对象的组装方式有2种:
A. 通过属性
B. 通过XML.
ObjectBuilder是通过属性来实现对象的组装的.
ObjectBuilder的反射策略:
由此类图可看出,ObjectBuilder注入的具体方式有3种:
通过属性(PropertyReflectionStrategy);
通过构造器(ConstructorReflectionStrategy);
通过方法(MethodReflectionStrategy).
至于它们和(Type1,Type2,Type3)的对应关系并不那么的明确.主要应该是构造子注入和设值注入的实现.
我们按照默认的策略的顺序那详细的看下这几个策略是如何发挥作用的.
构造器反射策略(ConstractorReflectionStrategy):
涉及到的自定义Attribute:
[AttributeUsage(AttributeTargets.Constructor)]
public class InjectionConstructorAttribute : Attribute
{
}
类有可能有多个Constructor方法,所以需要通过Attribute指定哪个方法将被用来执行注入。
ConstructorReflectionStrategy的执行:
/// <summary>
/// Strategy that performs injection of constructor policies.
/// 执行构造子注入方针
/// </summary>
public class ConstructorReflectionStrategy : ReflectionStrategy<ConstructorInfo>
{
/// <summary>
/// See <see cref="ReflectionStrategy{T}.GetMembers"/> for more information.
/// </summary>
protected override IEnumerable<IReflectionMemberInfo<ConstructorInfo>> GetMembers(IBuilderContext context, Type typeToBuild, object existing, string idToBuild)
{
List<IReflectionMemberInfo<ConstructorInfo>> result = new List<IReflectionMemberInfo<ConstructorInfo>>();
//跟前文一样,每个策略都需要自己的一个对应的Policy来提供实际的执行方针
ICreationPolicy existingPolicy = context.Policies.Get<ICreationPolicy>(typeToBuild, idToBuild);
if (existing == null && (existingPolicy == null || existingPolicy is DefaultCreationPolicy))
{
ConstructorInfo injectionCtor = null;
ConstructorInfo[] ctors = typeToBuild.GetConstructors();
//只有默认构造器
if (ctors.Length == 1)
injectionCtor = ctors[0];
else
//多个构造器
{
foreach (ConstructorInfo ctor in ctors)
{
//是否有贴上构造子注入依赖标签InjectionConstructorAttribute
if (Attribute.IsDefined(ctor, typeof(InjectionConstructorAttribute)))
{
// Multiple decorated constructors aren't valid
// 多个构造器有贴上构造子注入依赖标签是非法的
if (injectionCtor != null)
throw new InvalidAttributeException();
injectionCtor = ctor;
}
}
}
if (injectionCtor != null)
result.Add(new ReflectionMemberInfo<ConstructorInfo>(injectionCtor));
}
//返回指定的构造方法
return result;
}
/// <summary>
/// See <see cref="ReflectionStrategy{T}.AddParametersToPolicy"/> for more information.
/// </summary>
protected override void AddParametersToPolicy(IBuilderContext context, Type typeToBuild, string idToBuild, IReflectionMemberInfo<ConstructorInfo> member, IEnumerable<IParameter> parameters)
{
ConstructorPolicy policy = new ConstructorPolicy();
//保存通过构造方法传递进来的参数到它对应的Policy里面,以便实例化时使用
foreach (IParameter parameter in parameters)
policy.AddParameter(parameter);
context.Policies.Set<ICreationPolicy>(policy, typeToBuild, idToBuild);
}
/// <summary>
/// See <see cref="ReflectionStrategy{T}.MemberRequiresProcessing"/> for more information.
/// </summary>
protected override bool MemberRequiresProcessing(IReflectionMemberInfo<ConstructorInfo> member)
{
return true;
}
属性反射策略(PropertyReflectionStrategy):
涉及到的自定义Attribute:
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Parameter, AllowMultiple = false, Inherited = true)]
public sealed class CreateNewAttribute : ParameterAttribute
{
/// <summary>
/// See <see cref="ParameterAttribute.CreateParameter"/> for more information.
/// </summary>
public override IParameter CreateParameter(Type annotatedMemberType)
{
return new CreationParameter(annotatedMemberType, Guid.NewGuid().ToString());
}
}
此Attribute指定ObjectBuilder创建这个参数的时候不从Locator里面取,而是创建一个新的实例。(不过,我发现好像用处不大,应该主要是针对哪些引用类型的实例,多次的引用会影响到其他地方的引用。此处目前还未完全搞懂:) )
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Parameter, AllowMultiple = false, Inherited = true)]
public sealed class DependencyAttribute : ParameterAttribute
{
private string name;
private Type createType;
private NotPresentBehavior notPresentBehavior = NotPresentBehavior.CreateNew;
private SearchMode searchMode;
/// <summary>
/// Initializes a new instance of the <see cref="DependencyAttribute"/> class.
/// </summary>
public DependencyAttribute()
{
}
/// <summary>
/// The name of the object to inject. Optional.
/// </summary>
public string Name
{
get { return name; }
set { name = value; }
}
/// <summary>
/// The type to be created, when <see cref="DependencyAttribute.NotPresentBehavior"/> is set
/// to <see cref="Microsoft.Practices.ObjectBuilder.NotPresentBehavior.CreateNew"/>
/// and an existing object cannot be found. Optional.
/// </summary>
public Type CreateType
{
get { return createType; }
set { createType = value; }
}
/// <summary>
/// Specifies how the dependency will be searched in the locator.
/// </summary>
public SearchMode SearchMode
{
get { return searchMode; }
set { searchMode = value; }
}
/// <summary>
/// The behavior when the object isn't found. Defaults to
/// <see cref="Microsoft.Practices.ObjectBuilder.NotPresentBehavior.CreateNew"/>.
/// </summary>
public NotPresentBehavior NotPresentBehavior
{
get { return notPresentBehavior; }
set { notPresentBehavior = value; }
}
/// <summary>
/// See <see cref="ParameterAttribute.CreateParameter"/> for more information.
/// </summary>
public override IParameter CreateParameter(Type annotatedMemberType)
{
return new DependencyParameter(annotatedMemberType, name, createType, notPresentBehavior, searchMode);
}
}
此Attribute则指定ObjectBuilder创建实例的时候直接从Locator里面获取,如果Locator中没有相关对象再重新创建一个实例。此参数对于我们注入初始值时起到关键作用。我们只要在Locator添加已经初始化好的实例,ObjectBuilder的创建机制自动会获取该值作为注入的参数值。
PropertyReflectionStrategy的执行:
public class PropertyReflectionStrategy : ReflectionStrategy<PropertyInfo>
{
/// <summary>
/// See <see cref="ReflectionStrategy{T}.GetMembers"/> for more information.
/// </summary>
protected override IEnumerable<IReflectionMemberInfo<PropertyInfo>> GetMembers(IBuilderContext context, Type typeToBuild, object existing, string idToBuild)
{
//获取被贴上自定义Attribute的所有属性
foreach (PropertyInfo propInfo in typeToBuild.GetProperties())
yield return new PropertyReflectionMemberInfo(propInfo);
}
/// <summary>
/// See <see cref="ReflectionStrategy{T}.AddParametersToPolicy"/> for more information.将相关表示注入的属性添加到策略对应的方针里面,以便实例化时使用
/// </summary>
protected override void AddParametersToPolicy(IBuilderContext context, Type typeToBuild, string idToBuild, IReflectionMemberInfo<PropertyInfo> member, IEnumerable<IParameter> parameters)
{
//跟前文一样,每个策略都需要自己的一个对应的Policy来提供实际的执行方针
PropertySetterPolicy result = context.Policies.Get<IPropertySetterPolicy>(typeToBuild, idToBuild) as PropertySetterPolicy;
if (result == null)
{
result = new PropertySetterPolicy();
context.Policies.Set<IPropertySetterPolicy>(result, typeToBuild, idToBuild);
}
foreach (IParameter parameter in parameters)
if (!result.Properties.ContainsKey(member.Name))
result.Properties.Add(member.Name, new PropertySetterInfo(member.MemberInfo, parameter));
}
/// <summary>
/// See <see cref="ReflectionStrategy{T}.MemberRequiresProcessing"/> for more information.
/// </summary>
protected override bool MemberRequiresProcessing(IReflectionMemberInfo<PropertyInfo> member)
{
return (member.GetCustomAttributes(typeof(ParameterAttribute), true).Length > 0);
}
}
方法反射策略(MethodReflectionStrategy):
涉及到的自定义Attribute:
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public sealed class InjectionMethodAttribute : Attribute
{
}
此Attribute指定此方法供注入执行。
MethodReflectionStrategy的执行:
public class MethodReflectionStrategy : ReflectionStrategy<MethodInfo>
{
/// <summary>
/// See <see cref="ReflectionStrategy{T}.GetMembers"/> for more information.
/// </summary>
protected override IEnumerable<IReflectionMemberInfo<MethodInfo>> GetMembers(IBuilderContext context, Type typeToBuild, object existing, string idToBuild)
{
//通理,收集贴上自定义Attribute的所有方法
foreach (MethodInfo method in typeToBuild.GetMethods())
yield return new ReflectionMemberInfo<MethodInfo>(method);
}
/// <summary>
/// See <see cref="ReflectionStrategy{T}.AddParametersToPolicy"/> for more information.
/// </summary>
protected override void AddParametersToPolicy(IBuilderContext context, Type typeToBuild, string idToBuild, IReflectionMemberInfo<MethodInfo> member, IEnumerable<IParameter> parameters)
{
//跟前文一样,每个策略都需要自己的一个对应的Policy来提供实际的执行方针
MethodPolicy result = context.Policies.Get<IMethodPolicy>(typeToBuild, idToBuild) as MethodPolicy;
if (result == null)
{
result = new MethodPolicy();
context.Policies.Set<IMethodPolicy>(result, typeToBuild, idToBuild);
}
//将相关方法保存到对应的方针里面
result.Methods.Add(member.Name, new MethodCallInfo(member.MemberInfo, parameters));
}
/// <summary>
/// See <see cref="ReflectionStrategy{T}.MemberRequiresProcessing"/> for more information.
/// </summary>
protected override bool MemberRequiresProcessing(IReflectionMemberInfo<MethodInfo> member)
{
return (member.GetCustomAttributes(typeof(InjectionMethodAttribute), true).Length > 0);
}
}
三个策略依次看下来,都只有执行收集任务,但是并没有真正的实现对象实例化,那到底对象的实例化,以及我们注入的东东什么时候才会发挥作用呢?请看下面的三个策略:
光看名字就知道了他们的作用了吧。
ConstructorReflectionStrategy注入的东东将在CreationStrategy执行:
private void InitializeObject(IBuilderContext context, object existing, string id, ICreationPolicy policy)
{
Type type = existing.GetType();
//从Policy中选取我们刚刚保存的构造方法
ConstructorInfo constructor = policy.SelectConstructor(context, type, id);
if (constructor == null)
{
if (type.IsValueType)
return;
throw new ArgumentException(Properties.Resources.NoAppropriateConstructor);
}
object[] parms = policy.GetParameters(context, type, id, constructor);
MethodBase method = (MethodBase)constructor;
Guard.ValidateMethodParameters(method, parms, existing.GetType());
if (TraceEnabled(context))
TraceBuildUp(context, type, id, Properties.Resources.CallingConstructor, ParametersToTypeList(parms));
method.Invoke(existing, parms);
}
PropertyReflectionStrategy注入的东东将在PropertySetterStrategy执行:
private void InjectProperties(IBuilderContext context, object obj, string id)
{
if (obj == null)
return;
Type type = obj.GetType();
IPropertySetterPolicy policy = context.Policies.Get<IPropertySetterPolicy>(type, id);
if (policy == null)
return;
//从Policy中获取我们刚刚保存的依赖注入的属性列表,并对相关参数赋值
foreach (IPropertySetterInfo propSetterInfo in policy.Properties.Values)
{
PropertyInfo propInfo = propSetterInfo.SelectProperty(context, type, id);
if (propInfo != null)
{
if (propInfo.CanWrite)
{
/
object value = propSetterInfo.GetValue(context, type, id, propInfo);
if( value != null )
Guard.TypeIsAssignableFromType(propInfo.PropertyType, value.GetType(), obj.GetType());
if (TraceEnabled(context))
TraceBuildUp(context, type, id, Properties.Resources.CallingProperty, propInfo.Name, propInfo.PropertyType.Name);
propInfo.SetValue(obj, value, null);
}
else
{
throw new ArgumentException(String.Format(
CultureInfo.CurrentCulture,
Properties.Resources.CannotInjectReadOnlyProperty,
type, propInfo.Name));
}
}
}
}
MethodReflectionStrategy注入的东东将在MethodExecutionStrategy执行:
private void ApplyPolicy(IBuilderContext context, object obj, string id)
{
if (obj == null)
return;
Type type = obj.GetType();
IMethodPolicy policy = context.Policies.Get<IMethodPolicy>(type, id);
if (policy == null)
return;
//从方针中获取我们刚才保存的方法列表,并执行
foreach (IMethodCallInfo methodCallInfo in policy.Methods.Values)
{
MethodInfo methodInfo = methodCallInfo.SelectMethod(context, type, id);
if (methodInfo != null)
{
object[] parameters = methodCallInfo.GetParameters(context, type, id, methodInfo);
Guard.ValidateMethodParameters(methodInfo, parameters, obj.GetType());
if (TraceEnabled(context))
TraceBuildUp(context, type, id, Properties.Resources.CallingMethod, methodInfo.Name, ParametersToTypeList(parameters));
methodInfo.Invoke(obj, parameters);
}
}
}
ObjectBuilder的IoC示例(超级无敌大奶妈):
using System;
using System.Collections.Generic;
using System.Collections;
using System.Text;
using Microsoft.Practices.ObjectBuilder;
namespace TestBuilder
{
class Program
{
static void
{
IReadWriteLocator locator;
//定位器准备工作
locator = new Locator();
LifetimeContainer lifetime = new LifetimeContainer();
locator.Add(typeof(ILifetimeContainer), lifetime);
Builder builder = new Builder();
PolicyList policyList = new PolicyList();
//设定创建策略方针
//类型映射方针
//policyList.Set<ITypeMappingPolicy>(new TypeMappingPolicy(typeof(宝宝), "baby"), typeof(MyAbstractClass), "baby");
//单件实例方针
policyList.Set<ISingletonPolicy>(new SingletonPolicy(true), typeof(宝宝), "baby");
//构造子注入方针
ValueParameter[] VParas = new ValueParameter[2];
VParas[0] = new ValueParameter(typeof(奶), new 奶("奶妈给你奶。"));
VParas[1] = new ValueParameter(typeof(玩具), new 玩具("奶妈给你玩具。"));
policyList.Set<ICreationPolicy>(new ConstructorPolicy(VParas), typeof(宝宝), "baby");
PropertySetterPolicy policy = new PropertySetterPolicy();
//注意此阿童木与locator中的阿童木的不通之处
policy.Properties.Add("阿童木", new PropertySetterInfo("阿童木", new ValueParameter(typeof(玩具), new 玩具("奶妈给你阿童木"))));
policy.Properties.Add("猪八戒", new PropertySetterInfo("猪八戒", new ValueParameter(typeof(玩具), new 玩具("奶妈给你猪八戒。"))));
locator.Add(new DependencyResolutionLocatorKey(typeof(玩具), "葫芦娃"), new 玩具("奶妈给你葫芦娃。"));
locator.Add(new DependencyResolutionLocatorKey(typeof(玩具), "阿童木"), new 玩具("奶妈给你阿童木。"));
policyList.Set<IPropertySetterPolicy>(policy, typeof(宝宝), "baby");
Console.WriteLine("-----------------------");
Console.WriteLine(" 超级无敌大奶妈 ");
Console.WriteLine("-----------------------");
宝宝 baby = builder.BuildUp<宝宝>(locator, "baby", null, policyList);
Console.WriteLine(baby.GetType().ToString());
Console.WriteLine(baby.奶.ToString());
Console.WriteLine(baby.玩具.ToString());
Console.WriteLine(baby.猪八戒.ToString());
Console.WriteLine(baby.阿童木.ToString());
Console.WriteLine(baby.阿童木.ToString());
Console.WriteLine(baby.葫芦娃.ToString());
Console.ReadLine();
}
}
public abstract class 婴儿
{
private 奶 m奶= new 奶("奶妈,我要吃奶!");
private 玩具 m玩具= new 玩具("奶妈,我要玩具!");
public 奶 奶
{
get { return this.m奶; }
set { this.m奶= value; }
}
public 玩具 玩具
{
get { return this.m玩具; }
set { this.m玩具= value; }
}
}
public class 宝宝 : 婴儿
{
private 玩具 m玩具= new 玩具("我要玩具:猪八戒");
private 玩具 m玩具= new 玩具("我要玩具:阿童木");
private 玩具 m玩具= new 玩具("我要玩具:阿童木");
public 宝宝()
{
}
public 宝宝(奶 p奶)
{
base.奶= p奶;
}
[InjectionConstructor]
public 宝宝(奶 p奶, 玩具 p玩具)
{
base.奶= p奶;
base.玩具= p玩具;
}
[CreateNew]
public 玩具 猪八戒
{
get { return this.m玩具; }
set { this.m玩具= value; }
}
public 玩具 阿童木
{
get { return this.m玩具; }
set { this.m玩具= value; }
}
[Dependency(Name = "阿童木")]
public 玩具 阿童木
{
get { return this.m玩具; }
set { this.m玩具= value; }
}
public 玩具 葫芦娃;
[InjectionMethod]
public void 我要葫芦娃([Dependency(Name = "葫芦娃")] 玩具 p葫芦娃)
{
//Console.WriteLine(" Sayself ");
this.葫芦娃= p葫芦娃;
}
}
public class 奶
{
private string mName;
public 奶(string pName)
{
this.mName = pName;
}
public override string ToString()
{
return this.mName;
}
}
public class 玩具
{
private string mName = "玩具";
public 玩具(string pName)
{
this.mName = pName;
}
public override string ToString()
{
return this.mName;
}
}
}
从这个示例中,我发现对于属性,如果需要注入即使不使用Attribute一样可以实现,只要在对应的PropertySetter方针中加入ID与属性名字一样的实例就可以了(请注意示例中的关于阿童木的注入)。对于CreateNew的Attribute尚有些不是太明白的地方。留待高手帮忙解答一下:)
TestBuilder 代码:https://files.cnblogs.com/walkingboy/TestBuilder.rar
ObjectBuilder的策略详解到此全部结束。由于本人水平有限,只是分享自己的学习过程,欢迎拍砖。
碰撞才能出火花,交流才能进步!
下文:如何使用ObjectBuilder写自己的Application Block(如 Entlib)。