























前两篇(思路和方法、重构计划)从大的方面上谈了关于重构的话题,这次从小的代码上来看。我们来看下一个的代码如何从简单到复杂,然后重构这些代码。
单个对象复制
在初步的需求中有个很简单的业务,就是定义销售合同,并且合同中可以配置产品设备数据,如下:
其中有个业务功能就是需要对已经存在的销售合同进行复制、剪贴和粘贴的工作。
对于程序来说,它其实就需要实现IClone接口就可以了,
代码
/// <summary>
/// 复制
/// </summary>
public object Clone()
{
TestObj copiedObj = new TestObj();
copiedObj.Parent = null;
copiedObj.Name = this.Name;
foreach (TestObj obj in this.Child)
{
TestObj childCopiedObj = obj.Clone() as TestObj;
childCopiedObj.Parent = copiedObj;
copiedObj.Child.Add(childCopiedObj);
}
return copiedObj;
}
OK,这很好的实现了业务需求。
批量复制不同对象
业务需求发生了变化,现在对于前期简单的销售合同进行了完善,同时让它变得异常复杂,如下图,
现在要求在所有内部对象都支持复制、剪贴和粘贴,并且也要支持每个部分的数据可以独立导出为特殊的文件,作为数据模板,对于其他合同为了简化操作可以将该模块导入,这些动作在实现前都必须要执行复制,前面的动作不用说都可以理解,对于后面的导入和导出来说,虽然主要通过序列化来支持,但是由于业务特殊性也必须先做复制操作,然后需要对复制后的数据做特殊处理,然后才去序列化。
对于这次的需求来说,很多人的实现方式是对每个类都模仿Clone的方式来写自己的复制方法,这种方式一定是正确的,并且性能是最好的,不过它也是代码量最多的,同时随着业务的进一步完善,它的代码会越来越多。
按照上述的方式来实现,可能需要两周左右的时间,可以想想有没有办法减少开发时间呢?而且也让以后的开发时间减少?
想了很久,终于有了一种思路:
具体的实现代码看下这三个类的代码,
1、最核心的类CopyContainer
代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;namespace Kevin.CommonFunction.Clone
{
/// <summary>
/// 复制元素的容器
/// </summary>
/// <remarks>
/// 执行批量的复制,默认只复制对象的值类型,引用关系保留
/// </remarks>
public class CopyContainer
{
#region 属性/// <summary>
/// 上下文环境参数
/// </summary>
private Dictionary<string, object> mContextParams;/// <summary>
/// 等待被复制的对象
/// </summary>
private List<object> mWaitToCopiedItems = new List<object>();/// <summary>
/// 被复制对象-新对象的索引关系
/// </summary>
private Dictionary<object, object> mItemIndexs = new Dictionary<object, object>();/// <summary>
/// 构造函数的缓存
/// </summary>
private Dictionary<Type, ConstructorInfo> mConstcCache = new Dictionary<Type, ConstructorInfo>();/// <summary>
/// 字段的缓存
/// </summary>
private Dictionary<Type, List<FieldInfo>> mFieldInfoCache = new Dictionary<Type, List<FieldInfo>>();/// <summary>
/// 复制后的新对象
/// </summary>
private List<object> mNewItems = new List<object>();#endregion#region 构造函数/// <summary>
///
/// </summary>
public CopyContainer()
{
}#endregion#region 方法/// <summary>
/// 添加等待被复制的对象
/// </summary>
/// <param name="waitCopiedItem">需要复制的对象</param>
public void AddToWaitCopiedItems(object waitCopiedItem)
{
if (waitCopiedItem != null && !mWaitToCopiedItems.Contains(waitCopiedItem))
{
this.mWaitToCopiedItems.Add(waitCopiedItem);
}
}/// <summary>
/// 添加环境参数
/// </summary>
/// <param name="key">环境中的唯一主键</param>
/// <param name="contextParam">环境参数</param>
public void AddToContextParams(string key, object contextParam)
{
if (mContextParams == null)
{
mContextParams = new Dictionary<string, object>();
}this.mContextParams.Add(key, contextParam);
}/// <summary>
/// 复制对象
/// </summary>
/// <returns>所有复制后的对象列表</returns>
public List<object> Clone()
{
if (this.mWaitToCopiedItems == null)
{
return null;
}foreach (object obj in this.mWaitToCopiedItems)
{
object newObj = CopyToNewObj(obj);
mItemIndexs.Add(obj, newObj);
mNewItems.Add(newObj);
}
ConstructorInfo constc
= this.GetConstructor(copiedType);List
<FieldInfo> fields = this.GetFieldInfos(copiedType);object newObj = constc.Invoke(null);2、复制对象引用的操作接口ICopyReference
代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;namespace Kevin.CommonFunction
{
/// <summary>
/// 复制对象引用的操作接口
/// </summary>
/// <remarks>
/// 当在CopyContainer中执行复制的时候,对于复制对象引用的支持
/// /// </remarks>
public interface ICopyReference
{
/// <summary>
/// 设置复制对象的引用关系
/// </summary>
/// <param name="contextParams">上下文环境参数</param>
/// <param name="mItemIndexs">被复制对象-新对象的索引关系</param>
void SetReference(Dictionary<string, object> contextParams, Dictionary<object, object> mItemIndexs);
}
}
3、用于该复制操作的自定义属性KeepFixedValueAttribute
代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;namespace Kevin.CommonFunction.Clone
{
/// <summary>
/// 当在CopyContainer中执行复制的时候,保留对象特定字段的初始值
/// </summary>
public class KeepFixedValueAttribute : Attribute
{
}
}
上述是所有的实现代码,下面看下是如何使用的:
代码
using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;using Kevin.CommonFunction.Clone;
using Kevin.CommonFunction;namespace Kevin.NUnitTest.Clone
{
[TestClass]
public class T_CopyContainer
{
[TestMethod]
public void Clone()
{
CopyContainer cc = new CopyContainer();
TestObj oldObj
= new TestObj();Assert.AreEqual(newItems.Count,
2);虽然,每个支持复制操作的对象都需要实现ICopyReference接口,这是为了处理对象内的引用属性。但是整体而言,代码简单和清晰了很多。
其实,该操作还有其他更好的优化方式可以实现,这里只是借这个例子来说明两点:
1、写代码的时候不要因为前人怎么写,你也去模仿怎么写,这样会让你变得思维方式固化,一定要有新意,养成这种思维的习惯,并且合理地去实现它。
2、善于从不同的事物中去发现共同点,并将这个共同点抽象出来,从而达到代码复用的目的
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。