架构,改善程序复用性的设计~第三讲 实现一种功能的代码只能出现在一处(续)
在写完架构,改善程序复用性的设计~第三讲 实现一种功能的代码只能出现在一处 , 这篇文章后,得到了园友的反馈,说这种简单的业务逻辑还可以,但业务比较复杂时,根据就没法用这种方法。
针对这个问题,我觉得有必要再写一个续集了,呵呵!
上回说的主要核心内容是将公用的部分从一个方法中提取出来,生成一个新的方法,这个重构中叫做“提取到方法”
,另外一个核心内容就是方法的”单一职责“,即一个方法干一件事,将出现复杂事件时,将多个方法进行组合调用即可
这回主要说一个重构中的提取,其实不仅方法可以被提取,类,及整个项目也可以被提取,只要他们有被提取的必要!
一个例子:对于一个数据实体操作的基类,它包括了其它所有实体类共有的属性(DB)和方法(SubmitChanges),这可以理解了”提取到类“,当然这也是类的继承及面向对象的一个例子。
1 /// <summary> 2 /// LINQ数据库操作基类 3 /// </summary> 4 public abstract class RepositoryBase 5 { 6 public RepositoryBase(DataContext db) 7 { 8 DB = db; 9 } 10 protected System.Data.Linq.DataContext DB { get; private set; } 11 12 #region DBContext SubmitChanges 13 /// <summary> 14 /// XXB默认提交【重写时候可能需要写入自定义的类似约束的逻辑】 15 /// </summary> 16 protected virtual void SubmitChanges() 17 { 18 ChangeSet cSet = DB.GetChangeSet(); 19 if (cSet.Inserts.Count > 0 20 || cSet.Updates.Count > 0 21 || cSet.Deletes.Count > 0) 22 { 23 try 24 { 25 DB.SubmitChanges(System.Data.Linq.ConflictMode.ContinueOnConflict); 26 } 27 catch (System.Data.Linq.ChangeConflictException ex) 28 { 29 foreach (System.Data.Linq.ObjectChangeConflict occ in DB.ChangeConflicts) 30 { 31 // 使用当前数据库中的值,覆盖Linq缓存中实体对象的值 32 occ.Resolve(System.Data.Linq.RefreshMode.OverwriteCurrentValues); 33 // 使用Linq缓存中实体对象的值,覆盖当前数据库中的值 34 occ.Resolve(System.Data.Linq.RefreshMode.KeepCurrentValues); 35 // 只更新实体对象中改变的字段的值,其他的保留不变 36 occ.Resolve(System.Data.Linq.RefreshMode.KeepChanges); 37 } 38 DB.SubmitChanges(); 39 } 40 } 41 } 42 43 #endregion 44 }
还有一种更大程序上的提取,即”提取到项目“,就是说,它的整个项目都是其它项目公用的部分,所有把整个项目抽象出来
Entity.Commons这个项目是对所有解决方案的所有实体层进行的抽象,它里面有对实体的分页,实体参数组织,实体消息返回及实体统一验证等功能,都在Entity.Commons里实现
EntityBase.cs代码如下:
View Code
1 /// <summary> 2 /// 实体基类,与linq to sql数据映射对应 3 /// </summary> 4 [Serializable] 5 public abstract class EntityBase /*: INotifyPropertyChanging, INotifyPropertyChanged*/ 6 { 7 8 public EntityBase() 9 { 10 this.IsRealDeleted = true; 11 } 12 #region 实体相关 13 /// <summary> 14 /// 实体主键 15 /// 在子类中对它赋值,在其它类中可以访问到这个主键属性 16 /// </summary> 17 public abstract object[] PrimaryKey { get; } 18 /// <summary> 19 /// 是否执行真删除,默认为true,如果设为false,则更新实体的status字段 20 /// </summary> 21 public virtual bool IsRealDeleted { get; protected set; } 22 /// <summary> 23 /// 记录修改的列信息 24 /// 子类可以根据需要,去复写记录数据的方式 25 /// </summary> 26 /// <param name="sender"></param> 27 /// <param name="e"></param> 28 protected virtual void PropertyChangedEvent(object sender, PropertyChangedEventArgs e) 29 { 30 #region 添加修改字段记录 31 // VLog.IVLog log = new VLog.SqlVLog(); 32 // log.Write(string.Format("被修改的字段{0}", e.PropertyName)); 33 #endregion 34 #region 记录修改的字段和修改成的值 35 Type t = this.GetType(); 36 PropertyInfo pi = t.GetProperty(e.PropertyName); 37 object value = pi.GetValue(this, null); 38 this.OnPropertyChanged(e.PropertyName, value); 39 #endregion 40 41 } 42 #endregion 43 44 #region 实体验证 45 46 /// <summary> 47 /// 验证的字段名集合,为NULL表示验证所有字段 48 /// </summary> 49 public string[] ValidFields { get; set; } 50 51 /// <summary> 52 /// 数据验证(是否成功) 53 /// 虚属性,子类可以根据自己的逻辑去复写 54 /// </summary> 55 public virtual bool IsValid { get { return this.GetRuleViolations().Count() == 0; } } 56 /// <summary> 57 /// 获取验证失败的信息枚举,默认提供了非空验证 58 /// 它使用了简单的迭代器,如果GetRuleViolations有错误则返回迭代列表 59 /// </summary> 60 /// <returns></returns> 61 public virtual IEnumerable<RuleViolation> GetRuleViolations() 62 { 63 PropertyInfo[] propertyInfo = this.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance); 64 if (ValidFields != null) propertyInfo = propertyInfo.Where(i => ValidFields.Contains(i.Name)).ToArray(); 65 foreach (var i in propertyInfo) 66 { 67 if (i.GetCustomAttributes(typeof(System.Data.Linq.Mapping.ColumnAttribute), false) != null 68 && i.GetCustomAttributes(typeof(System.Data.Linq.Mapping.ColumnAttribute), false).Count() > 0 69 && !((System.Data.Linq.Mapping.ColumnAttribute)i.GetCustomAttributes(typeof(System.Data.Linq.Mapping.ColumnAttribute), false)[0]).CanBeNull 70 && !((System.Data.Linq.Mapping.ColumnAttribute)i.GetCustomAttributes(typeof(System.Data.Linq.Mapping.ColumnAttribute), false)[0]).IsPrimaryKey) 71 if (i.GetValue(this, null) == null || string.IsNullOrEmpty(i.GetValue(this, null).ToString())) 72 yield return new RuleViolation("*", i.Name); 73 } 74 } 75 #endregion 76 77 #region 重写linq to sql的一些东西 78 79 #region INotifyPropertyChanged and INotifyPropertyChanging Members 80 81 public event PropertyChangedEventHandler BasePropertyChanged; 82 public event PropertyChangingEventHandler BasePropertyChanging; 83 protected virtual void OnPropertyChanging(String propertyName) 84 { 85 if ((this.BasePropertyChanging != null)) 86 { 87 this.BasePropertyChanging(this, new PropertyChangingEventArgs(propertyName)); 88 } 89 } 90 protected virtual void OnPropertyChanged(String propertyName, object newValue) 91 { 92 if ((this.BasePropertyChanged != null)) 93 { 94 this.BasePropertyChanged(this, new PropertyChangedEventArgs(propertyName)); 95 } 96 97 if (_changeList == null) 98 return; 99 100 if (_changeList.ContainsKey(propertyName)) 101 { 102 _changeList.Remove(propertyName); 103 } 104 _changeList.Add(propertyName, newValue); 105 } 106 protected bool IsPropertyChanged(string name) 107 { 108 return _changeList != null && _changeList.ContainsKey(name); 109 } 110 #endregion 111 112 #region Change tracking 113 114 private Dictionary<string, object> _changeList; 115 116 public Dictionary<string, object> GetChanges() 117 { 118 return _changeList; 119 } 120 121 private void StartTrackChanges() 122 { 123 if (_changeList != null) 124 { 125 throw new InvalidOperationException("This object is already tracking changes"); 126 } 127 _changeList = new Dictionary<string, object>(); 128 } 129 130 private bool _IsAlreadySaved = false; 131 132 public bool IsAlreadySaved() 133 { 134 return _IsAlreadySaved; 135 } 136 /// <summary> 137 /// 保存实体 138 /// </summary> 139 public void MarkEntitySaved() 140 { 141 _IsAlreadySaved = true; 142 } 143 /// <summary> 144 /// 实体初始化,开始跟踪实体的变化 145 /// </summary> 146 public virtual void Initialization() 147 { 148 this.StartTrackChanges(); 149 } 150 151 #endregion 152 153 #endregion 154 } 155 #region 子类更新需要实现它的分部方法,内容如下 156 //partial void OnCreated() 157 // { 158 // base.IsRealDeleted = false;//假删除 159 // base.Initialization();//基类的某些属性初始化 160 // this.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(base.PropertyChangedEvent);//初始实体时,先订阅列修改的事件 161 // } 162 #endregion
通过这篇文章,我们知道了,对于代码重构,不仅仅只对于方法而言,对于重构,也不仅仅只对一个项目而言,它可能是项目与项目之间的重构。