在寫完架構,改善程序復用性的設計~第三講 實現一種功能的代碼只能出現在一處 , 這篇文章后,得到了園友的反饋,說這種簡單的業務邏輯還可以,但業務比較復雜時,根據就沒法用這種方法。
針對這個問題,我覺得有必要再寫一個續集了,呵呵!
上回說的主要核心內容是將公用的部分從一個方法中提取出來,生成一個新的方法,這個重構中叫做“提取到方法”
,另外一個核心內容就是方法的”單一職責“,即一個方法干一件事,將出現復雜事件時,將多個方法進行組合調用即可
這回主要說一個重構中的提取,其實不僅方法可以被提取,類,及整個項目也可以被提取,只要他們有被提取的必要!
一個例子:對於一個數據實體操作的基類,它包括了其它所有實體類共有的屬性(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代碼如下:

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
通過這篇文章,我們知道了,對於代碼重構,不僅僅只對於方法而言,對於重構,也不僅僅只對一個項目而言,它可能是項目與項目之間的重構。