如何重构代码-简单、灵活的实现对象复制

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

 

单个对象复制

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

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

  对于程序来说,它其实就需要实现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);
            }

            
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 < 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();
            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 < string object >  contextParams, Dictionary < object object >  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、善于从不同的事物中去发现共同点,并将这个共同点抽象出来,从而达到代码复用的目的

 

 

你可能感兴趣的:(如何重构代码-简单、灵活的实现对象复制)