在写完架构,改善程序复用性的设计~第三讲 实现一种功能的代码只能出现在一处 , 这篇文章后,得到了园友的反馈,说这种简单的业务逻辑还可以,但业务比较复杂时,根据就没法用这种方法。
针对这个问题,我觉得有必要再写一个续集了,呵呵!
上回说的主要核心内容是将公用的部分从一个方法中提取出来,生成一个新的方法,这个重构中叫做“提取到方法”
,另外一个核心内容就是方法的”单一职责“,即一个方法干一件事,将出现复杂事件时,将多个方法进行组合调用即可
这回主要说一个重构中的提取,其实不仅方法可以被提取,类,及整个项目也可以被提取,只要他们有被提取的必要!
一个例子:对于一个数据实体操作的基类,它包括了其它所有实体类共有的属性(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
通过这篇文章,我们知道了,对于代码重构,不仅仅只对于方法而言,对于重构,也不仅仅只对一个项目而言,它可能是项目与项目之间的重构。
作者:仓储大叔,张占岭,
荣誉:微软MVP