惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

K
Kaspersky official blog
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
AI
AI
SecWiki News
SecWiki News
宝玉的分享
宝玉的分享
Scott Helme
Scott Helme
D
Darknet – Hacking Tools, Hacker News & Cyber Security
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
Engineering at Meta
Engineering at Meta
博客园 - 叶小钗
The GitHub Blog
The GitHub Blog
Microsoft Azure Blog
Microsoft Azure Blog
N
News and Events Feed by Topic
Cloudbric
Cloudbric
B
Blog
Cisco Talos Blog
Cisco Talos Blog
V
Vulnerabilities – Threatpost
N
News and Events Feed by Topic
V
Visual Studio Blog
A
Arctic Wolf
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
U
Unit 42
S
Security @ Cisco Blogs
博客园 - 聂微东
T
Threat Research - Cisco Blogs
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Apple Machine Learning Research
Apple Machine Learning Research
Y
Y Combinator Blog
G
GRAHAM CLULEY
L
LINUX DO - 热门话题
量子位
NISL@THU
NISL@THU
Webroot Blog
Webroot Blog
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
T
Troy Hunt's Blog
Application and Cybersecurity Blog
Application and Cybersecurity Blog
T
Tenable Blog
月光博客
月光博客
S
Security Affairs
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
The Hacker News
The Hacker News
Spread Privacy
Spread Privacy
D
Docker
www.infosecurity-magazine.com
www.infosecurity-magazine.com
雷峰网
雷峰网
博客园 - 司徒正美
T
The Exploit Database - CXSecurity.com
Hugging Face - Blog
Hugging Face - Blog
Help Net Security
Help Net Security
D
DataBreaches.Net

博客园 - Kevin-moon

在IE中本地浏览与服务器浏览同一Html后,出现了奇异的结果 如何避免JQuery Dialog的内存泄露 JQuery另类视角-动态执行脚本的BUG JQuery另类视角-动态扩展对象 - Kevin-moon - 博客园 如何重构代码-重构计划 如何重构代码-思路和方法 谈谈多线程的思维方式 简单的数据移动算法(C#) [ASP.NET MVC]-理解Routing 笑死人的十项大奖 深入APM,实现自己的APM [Windbg基础]-了解Symbols 深入线程,实现自定义的SynchronizationContext 重温ASP.NET的配置信息(简单、常用) 奇妙的SynchronizationContext 线程之间的通讯---SynchronizationContext "对象"到"山寨对象"的完整转换 我真的就只能在远方看你吗?!!! "测试驱动开发(TDD)" 动态进行"自定义公式"计算
如何重构代码-简单、灵活的实现对象复制
Kevin-moon · 2010-05-14 · via 博客园 - Kevin-moon

  前两篇(思路和方法重构计划)从大的方面上谈了关于重构的话题,这次从小的代码上来看。我们来看下一个的代码如何从简单到复杂,然后重构这些代码。

单个对象复制


   在初步的需求中有个很简单的业务,就是定义销售合同,并且合同中可以配置产品设备数据,如下:

  其中有个业务功能就是需要对已经存在的销售合同进行复制、剪贴和粘贴的工作。

  对于程序来说,它其实就需要实现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<stringobject> mContextParams;/// <summary>
        
/// 等待被复制的对象
        
/// </summary>
        private List<object> mWaitToCopiedItems = new List<object>();/// <summary>
        
/// 被复制对象-新对象的索引关系
        
/// </summary>
        private Dictionary<objectobject> mItemIndexs = new Dictionary<objectobject>();/// <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<stringobject>();
            }
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);
            }

foreach (object newObj in mNewItems)
            {
                ICopyReference copiedObj 
= newObj as ICopyReference;
                
if (copiedObj != null)
                {
                    copiedObj.SetReference(
this.mContextParams, this.mItemIndexs);
                }
            }
            
            
return this.mNewItems;
        }
/// <summary>
        
/// 复制对象
        
/// </summary>
        
/// <param name="copeidItem">被复制的对象</param>
        
/// <returns>复制后的对象</returns>
        private object CopyToNewObj(object copeidItem)
        {
            Type copiedType 
= copeidItem.GetType();

            ConstructorInfo constc 

= this.GetConstructor(copiedType);

            List

<FieldInfo> fields = this.GetFieldInfos(copiedType);object newObj = constc.Invoke(null);
            
foreach (FieldInfo field in fields)
            {
                
object copiedValue = field.GetValue(copeidItem);
                field.SetValue(newObj, copiedValue);
            }
return newObj;
        }
/// <summary>
        
/// 获取构造函数 
        
/// </summary>
        
/// <param name="copiedType">被复制对象的类型</param>
        
/// <returns>构造函数</returns>
        private ConstructorInfo GetConstructor(Type copiedType)
        {
            ConstructorInfo copiedConstc 
= null;if (this.mConstcCache.ContainsKey(copiedType))
            {
                copiedConstc 
= this.mConstcCache[copiedType];
            }
                
//该构造函数未加入缓存
            else
            {
                ConstructorInfo[] constructors 
= copiedType.GetConstructors(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public);//循环所有构造函数
                foreach (ConstructorInfo constc in constructors)
                {
                    ParameterInfo[] paramsInConst 
= constc.GetParameters();if (paramsInConst.Length == 0)
                    {
                        copiedConstc 
= constc;
                        
break;
                    }
                }
if (copiedConstc == null)
                {
                    
throw new Exception(string.Format("类 '{0}' 缺少无参的构造函数", copiedType.FullName));
                }
this.mConstcCache.Add(copiedType, copiedConstc);
            }
return copiedConstc;
        }
/// <summary>
        
/// 获取字段
        
/// </summary>
        
/// <param name="copiedType">被复制对象的字段</param>
        
/// <returns>字段</returns>
        private List<FieldInfo> GetFieldInfos(Type copiedType)
        {
            List
<FieldInfo> fields = null;if (this.mFieldInfoCache.ContainsKey(copiedType))
            {
                fields 
= this.mFieldInfoCache[copiedType];
            }
                
//该字段未加入缓存
            else
            {
                fields 
= new List<FieldInfo>();
                
                FieldInfo[] fieldsInCT 
= copiedType.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
                
foreach (FieldInfo fieldItem in fieldsInCT)
                {
                    
//过滤带有KeepFixedValueAttribute自定义属性的字段
                    if (fieldItem.GetCustomAttributes(typeof(KeepFixedValueAttribute), true).Length == 0)
                    {
                        fields.Add(fieldItem);
                    }
                }
this.mFieldInfoCache.Add(copiedType, fields);
            }
return fields;
        }
#endregion
    }
}

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<stringobject> contextParams, Dictionary<objectobject> 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();
            cc.AddToWaitCopiedItems(oldObj);
            List
<object> newItems = cc.Clone();

            Assert.AreEqual(newItems.Count, 

2);
        }
    }
public class TestObj : ICopyReference
    {
        
/// <summary>
        
/// 自动生成的主键
        
/// </summary>
        public int mKey = System.Guid.NewGuid().GetHashCode();/// <summary>
        
/// 上级对象
        
/// </summary>
        public TestObj Parent;/// <summary>
        
/// 下级对象列表
        
/// </summary>
        public List<TestObj> Child = new List<TestObj>();/// <summary>
        
/// 名称
        
/// </summary>
        public string Name;/// <summary>
        
/// 设置复制对象的引用关系
        
/// </summary>
        
/// <param name="contextParams">上下文环境参数</param>
        
/// <param name="mItemIndexs">被复制对象-新对象的索引关系</param>
        public void SetReference(Dictionary<stringobject> contextParams, Dictionary<objectobject> mItemIndexs)
        {
            List
<object> arrCopiedItems = new List<object>();foreach (TestObj obj in this.Child)
            {
                
if (mItemIndexs.ContainsKey(obj))
                {
                    arrCopiedItems.Add(mItemIndexs[obj]);
                }
                
else
                {
                    arrCopiedItems.Add(obj);
                }
            }
        }
    }
}

   虽然,每个支持复制操作的对象都需要实现ICopyReference接口,这是为了处理对象内的引用属性。但是整体而言,代码简单和清晰了很多。

  其实,该操作还有其他更好的优化方式可以实现,这里只是借这个例子来说明两点:

  1、写代码的时候不要因为前人怎么写,你也去模仿怎么写,这样会让你变得思维方式固化,一定要有新意,养成这种思维的习惯,并且合理地去实现它。

  2、善于从不同的事物中去发现共同点,并将这个共同点抽象出来,从而达到代码复用的目的