对一个排序程序的不断重构



最近写了一个排序的程序
需求从最开始的只要能对数组进行升序排序不断的进化
最后成为以下的样子:

 1 可以升序和降序排序
 2 能够自由的扩充排序算法而不需改动原有程序
 3 能纪录排序的比较次数、交换次数、排序用时
 4 通过扩展,能够对任意对象进行排序
 5 通过扩展,能够将排序过程相关的信息打印在任何位置

所以代码从最开始的一个简单的函数被重构成了
包含3个 接口、N个类的包




程序的输出入下:

**********************************************
2005-10-12 0:16:57
The original array is :
2005-01-01 6:06:06,2004-01-01 6:06:06,2006-01-01 6:06:06,2005-01-01 6:06:07

Now using BubbleSorter, direction : Descending
Sort was finished.
Sort took 40.0576 ms.
Sort compared 6 times.
Sort swapped 4 times.

The sorted array is :
2006-01-01 6:06:06,2005-01-01 6:06:07,2005-01-01 6:06:06,2004-01-01 6:06:06

**********************************************
2005-10-12 0:16:57
The original array is :
1,3,2,4,5,6.5,7,8

Now using BubbleSorter, direction : Ascending
Sort was finished.
Sort took 20.0288 ms.
Sort compared 13 times.
Sort swapped 1 times.

The sorted array is :
1,2,3,4,5,6.5,7,8

………………(其余部分省略)


起初,BubbleSorter的代码如下

/// <summary>
    
/// 冒泡排序算法,使用了策略模式避免了对具体比较器的依赖
    
/// </summary>

    public class BubbleSorter : SorterBase
    
{
        
/// <summary>
        
/// 构造器
        
/// </summary>

         public BubbleSorter():this(new CommonComparer(),new ConsolePrinter())
        
{
         }


        
/// <summary>
        
/// 构造器
        
/// </summary>
        
/// <param name="comparer">比较器实例</param>

         public BubbleSorter(IComparer comparer):this(comparer,new ConsolePrinter())
        
{
         }


        
/// <summary>
        
/// 构造器
        
/// </summary>
        
/// <param name="comparer">比较器实例</param>
        
/// <param name="printer">打印器实例</param>

        public BubbleSorter(IComparer comparer,IPrinter printer)
        
{
            Initialize(comparer,printer);
        }



         
public override void Sort(Array arrayToSort,SortDirection sortDirection)
        
{
            
if(arrayToSort == nullthrow new ArgumentNullException("arrayToSort");

            StartSort(
this,arrayToSort,sortDirection);
            
            
bool isSwap = false ; 

            
try
            
{
                
for(int i = 0 ; i <=  arrayToSort.Length - 1  ; i ++)
                
{
                    isSwap 
= false ;
                    
for(int j = arrayToSort.Length-2 ; j >= i  ; j --)
                    
{
                         
if(CompareAndSwap(arrayToSort,j,j+1))
                            isSwap 
= true;
                     }

                    
if(!isSwap) break ;
                }


                FinishSort(arrayToSort); 
            }

            
catch(Exception ex)
            
{
                RegisterException(ex);
                
throw;
            }

         }
 
     }


当只有冒泡排序算法的时候,我对上面的程序感觉相当满意。
但是,在我实现插入排序的话,我发现了几个很不爽的事情

1。基本我要实现跟冒泡排序一样的3个构造方法
2。对于Sort方法 我必须作参数校验
3。为了显示排序信息,我必须在排序过程前后调用StartSort(this,arrayToSort,sortDirection)和FinishSort(arrayToSort)方法
4。在BubbleSorter类中和SortBase类中,很多方法都在传递arrayToSort这个参数

于是我做了几件事
1。用Extra Method将真正排序的那段代码提取到一个叫做DoSort()的方法中
2。将Sort方法提取到Super Class中,在Sort中调用抽象的 DoSort方法。--- 这刚好是模板模式。
3。将arrayToSort提升为域字段
4。去除了3个烦躁的构造函数,转而使用工厂进行构造

经过3步重构后,排序的算法保持得很纯洁,如下:

/// <summary>
    
/// 冒泡排序算法
    
/// </summary>

    public class BubbleSorter : SorterBase
    
{
         
protected override void DoSort()
        
{
               
bool isSwap = false ; 

             
for(int i = 0 ; i <=  arrayToSort.Length - 1  ; i ++)
            
{
                isSwap 
= false ;
                
for(int j = arrayToSort.Length-2 ; j >= i  ; j --)
                
{
                     
if(CompareAndSwap(j,j+1))
                        isSwap 
= true;
                 }

                
if(!isSwap) break ;
            }

           }

     }



插入排序的代码

/// <summary>
    
/// 直接插入排序
    
/// </summary>

    public class InsertSorter : SorterBase
    
{
        
/// <url>element://model:project::Sorter/design:view:::zm86v8buarhrn1i_v</url>
        
/// <url>element://model:project::Sorter/design:view:::kg8jy20srni144i_v</url>

         protected override void DoSort()
        
{
             
for(int i=1;i< arrayToSort.Length;i++)
            
{
                
//依次与前面的记录进行比较,如比它小就交换
                for(int j=i;j>0;j--)
                
{
                    
if!CompareAndSwap( j-1 , j) ) break;
                }

            }

         }

    }

编码采用了TDD,如果没有单元测试,重构会是一场噩梦。
对于用于测试的代码也经过了重构
最开始只有一个类用于测试冒泡排序代码
后来加入了插入排序算法以后
发现原先的测试代码没有办法重用,于是抽象出了一个测试基类TestSorterBase,这个类里面存在大量的测试方法,但是这个类没有被标记[TestFixture]
从里面派生出的两个类 TestInsertSorter & TestBubbleSorter,被标记为[TestFixture],却没有声明任何[Test]特性的方法
代码如下

        /// <summary>
    
/// TestInsertSort 的摘要说明。
    
/// </summary>

    [TestFixture]
    
public class TestBubbleSorter : TestSortBase
    
{
        
protected override SorterBase CreateSorter()
        
{
            
return SorterFactory.GetSorter( typeof( BubbleSorter ) );
        }

     }

这个稍候再作详细的介绍。

呵呵 偶还在增加需求 继续重构
posted @ 2005-10-11 23:54  quitgame  阅读(886)  评论(2编辑  收藏  举报