Builder模式-加特性
其实这种设计思想也来源于生活,比如生活中,我们在淘宝上买哥电脑桌,我们要组装它,这类产品都有一个共同点就是会把零件上标好标志
1.定义在指导属性
创建特性类
/// <summary> /// 指导每个具体类型BuildPart过程目标方法和执行情况的属性 /// </summary> [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] public sealed class BuildStepAttribute : Attribute { int sequence; int times; public BuildStepAttribute(int sequence, int times) { if ((sequence <= 0) || (times <= 0)) throw new ArgumentOutOfRangeException(); this.sequence = sequence; this.times = times; } public BuildStepAttribute(int sequence) : this(sequence, 1) { } /// <summary> /// 执行的次序 /// </summary> public int Sequence { get { return this.sequence; } } /// <summary> /// 执行的次数 /// </summary> public int Times { get { return this.times; } } }
2.通过反射获取执行步骤的工具类型
public class BuildStep { public MethodInfo Method { get; set; } public int Times { get; set; } public int Sequence { get; set; } }
/// <summary> /// 通过反射获得某个类型相关BuildPart()步骤指导信息的工具类型 /// </summary> public class BuilderStepDiscovery { /// <summary> /// 缓冲已经解析过的类型信息 /// </summary> static IDictionary<Type, IEnumerable<BuildStep>> cache = new Dictionary<Type, IEnumerable<BuildStep>>(); /// <summary> /// 登记那些已经认定没有Build Step 属性的类型 /// </summary> static IList<Type> errorCache = new List<Type>(); /// <summary> /// 借助反射获得类型 T 所需执行BuildPart()的自动发现机制
,在.NET 4.0(当然也包括4.0以前的版本)下,用反射判断某个方法是否运用了自定义Attribute时,可以通过调用MethodInfo的IsDefined()方法进行确认。 /// </summary> /// <returns></returns> public IEnumerable<BuildStep> DiscoveryBuildSteps(Type type) { if (type == null) throw new ArgumentNullException("type"); if (errorCache.Contains(type)) return null; if (!cache.ContainsKey(type)) { var aType = typeof(BuildStepAttribute); var methods = from item in (from method in type.GetMethods() where method.IsDefined(aType, false) select new { M = method, A = (BuildStepAttribute)method.GetCustomAttributes(aType, false).First() } )orderby item.A.Sequence select new BuildStep { Method = item.M, Times = item.A.Times, Sequence = item.A.Sequence }; if (methods.Count() == 0) { errorCache.Add(type); // register invalidate type return null; } else { cache.Add(type, methods); // register validate type return methods; } } else return cache[type]; } }
3.依据标签装配过程
反射中借助linq获取每个执行步骤信息的查询
public interface IBuilder<T> where T : class, new() { T BuildUp(); } public class Builder<T> : IBuilder<T>where T : class, new() { public virtual T BuildUp() { var steps = new BuilderStepDiscovery().DiscoveryBuildSteps(typeof(T)); if (steps == null) return new T(); // 没有BuildPart步骤,退化为Factory模式 var target = new T(); foreach (var step in steps) for (var i = 0; i < step.Times; i++) step.Method.Invoke(target, null); return target; } }
测试
[TestClass] public class AttributedBuilderFixture { #region 采用BuildStep定义装配过程的测试类型 /// <summary> /// 用于显示测试结果的委托 /// </summary> /// <remarks> /// Action<string>是实际处理Build Step操作内容的委托 /// </remarks> static Action<string, Action<string>> buildPartHandler = (x, y) => { Trace.WriteLine("add " + x); y(x); }; class Car { public IList<string> Parts { get; private set; } public Car(){Parts = new List<string>();} /// <summary> /// 为汽车添加轮胎 /// </summary> /// <remarks> /// Attributed Builder第二个执行的Setp /// 执行4次,即为每辆汽车装配增加4个轮胎 /// </remarks> [BuildStep(2, 4)] public void AddWheel() { buildPartHandler("wheel", Parts.Add); } /// <summary> /// 为汽车装配引擎 /// </summary> /// <remarks> /// 没有通过Attribute标注的内容,因此不会被Attributed Builder执行 /// </remarks> public void AddEngine() { buildPartHandler("engine", Parts.Add); } /// <summary> /// 为汽车装配车身 /// </summary> /// <remarks> /// Attributed Builder第一个执行的Setp /// 执行1次,即为每辆汽车装配增加1个车身 /// </remarks> [BuildStep(1)] public void AddBody() { buildPartHandler("body", Parts.Add); } } class Computer { public string Bus { get; private set; } public string Monitor { get; private set; } public string Disk { get; private set; } public string Memory { get; private set; } public string Keyboard { get; private set; } public string Mouse { get; private set; } /// <summary> /// 缓存Computer类型的所有Property /// </summary> static PropertyInfo[] properties = typeof (Computer).GetProperties(); /// <summary> /// 获得Computer类型指定名称Property的Setter方法委托 /// </summary> /// <param name="target">Computer类型实例</param> /// <param name="name">Property名称</param> /// <returns>指定名称Property的Setter方法委托</returns> static Action<string> GetSetter(Computer target, string name) { var property = properties.Where(x => string.Equals(x.Name, name)).FirstOrDefault(); return x => property.SetValue(target, x, null); } [BuildStep(1)] public void LayoutBus() { buildPartHandler("bus", GetSetter(this, "Bus")); } [BuildStep(2)] public void AddDiskAndMemory() { buildPartHandler("disk", GetSetter(this, "Disk")); buildPartHandler("16G memory", GetSetter(this, "Memory")); } [BuildStep(3)] public void AddUserInputDevice() { buildPartHandler("USB mouse", GetSetter(this, "Mouse")); buildPartHandler("keyboard", GetSetter(this, "Keyboard")); } [BuildStep(4)] public void ConnectMonitor() { buildPartHandler("monitor", GetSetter(this, "Monitor")); } } #endregion [TestMethod] public void BuildComputerByAttributeDirection() { Trace.WriteLine("\nassembly computer"); var computer = new Computer(); Assert.IsNull(computer.Keyboard); Assert.IsNull(computer.Memory); computer = new Builder<Computer>().BuildUp(); Assert.IsNotNull(computer.Bus); Assert.IsNotNull(computer.Monitor); Assert.IsNotNull(computer.Disk); Assert.IsNotNull(computer.Memory); Assert.IsNotNull(computer.Keyboard); Assert.IsNotNull(computer.Mouse); } [TestMethod] public void BuildCarByAttributeDirection() { Trace.WriteLine("build car"); var car = new Builder<Car>().BuildUp(); Assert.IsNotNull(car); Assert.IsFalse(car.Parts.Contains("engine")); // 不会被执行的内容 Assert.AreEqual<string>("body", car.Parts.ElementAt(0)); for (var i = 1; i <= 4; i++) Assert.AreEqual<string>("wheel", car.Parts.ElementAt(i)); } }
有点抽象,有点小难,表示没理解,做个标记,改天再续
posted on 2013-03-29 13:51 HOT SUMMER 阅读(276) 评论(0) 编辑 收藏 举报