博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
重构初体验
阅读量:3606 次
发布时间:2019-05-21

本文共 5832 字,大约阅读时间需要 19 分钟。

设计大师Martin Fowler 在《重构——改善既有代码的设计》一书中,以其精妙的概括能力,彻底对重构技术作了全方位的总结。该书既具备大百科全书般提纲挈领的重构大纲,同时更通过实例展现了在软件设计中重构的魅力。

有感于重构艺术予我的震撼,我逐渐尝试在项目设计中开始重构之旅。在这个旅程中,存在尝试的犹豫和领悟的感动,然而最终却令我折服。如今,我希望能通过一个实际的例子,让读者也能初次体验重构的魅力。举例来说,我打算作一个容器,用来存放每个整数的阶乘
结果。最初的设计是这样:
public class FactorialContainer
{


       public FactorialContainer()
       {

              factorialList = new ArrayList();
       }

       public FactorialContainer(int capacity)
       {


              capa = capacity;
              factorialList = new ArrayList(capacity);
       }

       private ArrayList factorialList;
       private int capa;

       public ArrayList FacotorialList
       {


              get {return factorialList;}
              set {factorialList = value;}
       }
       public int Capacity
       {

              get {return capa;}
              set {capa = value;}
       }
       public long this[int index]
       {

              get {return (long)factorialList[index];}
              set {factorialList[index] = value;}
       }
       public void Add()
       {

              long result = 1;
              int seed = factorialList.Count + 1;
              for (int i=1;i< =seed;i++)
              {

                     result*=i;
              }
              factorialList.Add(result);
       }         
       public void Clear()
       {

              factorialList.Clear();
       }
       public void RemoveAt(int index)
       {

              factorialList.RemoveAt(index);
       }
}

熟悉重构的人是否已经嗅到了代码的坏味道了呢?是的,在Add()方法里,将计算阶乘的算法也放到了里面。由于这些代码实现了独立的算法,因此应该利用Extract Method规则,将这些代码提炼出来,形成独立的方法:
public void Add()
{


       long result = CountFactorial();           
       factorialList.Add(result);
}           
private long CountFactorial()
{

       long result = 1;
       int seed = factorialList.Count + 1;
       for (int i=1;i<=seed;i++)
       {

              result*=i;
       }
       return result;
}

我们还可以进一步简化Add()方法:
public void Add()
{


       factorialList.Add(Count());
}

现在我希望扩充这个容器的功能,加入菲波那契数列的计算。由于两者的计算方式是完全不同的,因此需要重新创建一个菲波那契容器:
public class FibonacciContainer
{


       public FibonacciContainer()
       {

              fibonacciList = new ArrayList();
       }
       public FibonacciContainer(int capacity)
       {

              capa = capacity;   
              fibonacciList = new ArrayList();
       }

       private ArrayList fibonacciList;
       private int capa;

       public ArrayList FibonacciList
       {


              get {return fibonacciList;}
              set {fibonacciList = value;}
       }
       public int Capacity
       {

              get {return capa;}
              set {capa = value;}
       }
       public long this[int index]
       {

              get {return (long)fibonacciList[index];}
              set {fibonacciList[index] = value;}
       }
       public void Add()
       {

              fibonacciList.Add(CountFibonacci ());
       }
       public void RemoveAt(int index)
       {

              fibonacciList.RemoveAt(index);
       }
       public void Clear()
       {

              fibonacciList.Clear();
       }
       private long CountFibonacci ()
       {

              long result = 0;
              int seed = fibonacciList.Count;
              if (seed == 0 || seed == 1)
              {

                     result = 1;
              }
              else
              {

                     result = this[seed-1] + this[seed-2];
              }
              return result;
       }
}

比较上面两段容器的代码,会有很多相似之处。又是时候拿起重构的利器了。首先我们根据name Method规则,将计算阶乘和菲波那契数列的方法改名为统一的名字。为什么要改名呢?既然两个容器有着相似之处,为什么不能定义一个基类,然后从其派生出各自的类呢?为了保证类方法的一致性,当然有必要对方法重新命名了。实际上,我们不仅要重命名方法名,而且还要改变属性的名字。

FacotorialList 、FibonacciList:改名为MathList;
CountFactorial()、CountFibonacci():改名为Count();

然后再通过Extract Class和Extract SubClass规则抽象出基类MathClass。

最后的代码为:
基类:MathContainer
public abstract class MathContainer
{


       public MathContainer()
       {

              mathList = new ArrayList();
       }      
       public MathContainer(int capacity)
       {

              capa = capacity;
              mathList = new ArrayList(capacity);
       }
       private ArrayList mathList;
       private int capa;
       public ArrayList MathList
       {

              get {return mathList;}
              set {mathList = value;}
       }
       public int Capacity
       {

              get {return capa;}
              set {capa = value;}
       }
       public long this[int index]
       {

              get {return (long)mathList[index];}
              set {mathList[index] = value;}
       }
       public void Add()
       {

              mathList.Add(Count());
       }
       public void RemoveAt(int index)
       {

              mathList.RemoveAt(index);
       }
       public void Clear()
       {

              mathList.Clear();
       }
       protected abstract long Count();     
}

然后从基类分别派生出计算阶乘和菲波那契数列的容器类:
派生类:FactorialContainer
public class FactorialContainer:MathContainer
{


       public FactorialContainer():base(){}
       public FactorialContainer(int capacity):base(capacity){}   
       protected override long Count()
       {

              long result = 1;
              int seed = MathList.Count + 1;
              for (int i=1;i<=seed;i++)
              {

                     result*=i;
              }
              return result;
       }
}

派生类:FibonacciContainer
public class FibonacciContainer:MathContainer
{


       public FibonacciContainer():base(){}
       public FibonacciContainer(int capacity):base(capacity){}
       protected override long Count()
       {

              long result = 0;
              int seed = MathList.Count;
              if (seed == 0 || seed == 1)
              {

                     result = 1;
              }
              else
              {

                     result = this[seed-1] + this[seed-2];
              }
              return result;
       }
}

UML类图如下:

对于这样的程序结构,要扩展起来是很容易的,例如素数的容器,我们只需要定义PrimeNumberContainer类,然后重写Count()方法,并派生MathContainer类即可。
 
经过重构,程序的结构变得愈发清晰。如果我们再仔细分析现在的结构,对于算法的扩展是非常容易的,但如何创建每个容器的实例,似乎还有更好的方法,那就是通过工厂来管理每个实例的创建。因为产品只有一类,所以可以参用工厂方法模式(Factory Method)。首先我们来看看UML类图:

实现代码如下:
基类工厂:MathFacotry类
public abstract class MathFactory
{


       public abstract MathContainer CreateInstance();
       public abstract MathContainer CreateInstance(int capacity);
}

阶乘容器工厂:FactorialFactory
public class FactorialFactory:MathFactory
{


       public override MathContainer CreateInstance()
       {

              return new FactorialContainer();
       }
       public override MathContainer CreateInstance(int capacity)
       {

              return new FactorialContainer(capacity);
       }
}

菲波那契数列容器工厂:
public class FibonacciFactory:MathFactory
{


       public override MathContainer CreateInstance()
       {

              return new FibonacciContainer();
       }
       public override MathContainer CreateInstance(int capacity)
       {

              return new FibonacciContainer(capacity);
       }
}

有了工厂,就可以通过工厂来创建我们所需要的具体容器类对象了:
[STAThread]
static void Main(string[] args)
{


       MathFactory factory1 = new FactorialFactory();
       MathFactory factory2 = new FibonacciFactory();

       MathContainer factorial = factory1.CreateInstance();
       MathContainer fibonacci = factory2.CreateInstance();

       Console.WriteLine("Count Factorial form 1 to 8:");
       for (int i=1;i<=8;i++)
       {


              factorial.Add();
       }
       for (int i=0;i&lt;8;i++)
       {

              Console.WriteLine(factorial[i].ToString());
       }

       Console.WriteLine();
       Console.WriteLine("Count Fibonacci form 1 to 8:");

       for (int i=1;i<=8;i++)
       {


              fibonacci.Add();
       }
       for (int i=0;i&lt;8;i++)
       {

              Console.WriteLine(fibonacci[i].ToString());
       }
       Console.ReadLine();
}

本来是一个简单的例子,似乎到后来越来越复杂了。然后仔细分析程序结构,你会发现这个的扩充性和灵活性是很好的。通过重构,并运用设计模式的工厂模式,我们逐步创建了这样一个渐趋完美的数学运算容器。大家可以试试为这个容器添加其他算法。也许在这个过程中你会发现结构还存在一些不足,那么不用担心,运用重构的武器吧。虽然这个武器可能比不上CS高手手里的AK47,不过对于对付大多数问题,也足以所向披靡了。

模仿STL:实现容器与算法的分离,利用迭代器模式

转载地址:http://ekxzn.baihongyu.com/

你可能感兴趣的文章
Django的模板引擎与模板使用
查看>>
liunx安装chrome浏览器
查看>>
tensorflow矩阵的运算
查看>>
tensorflow更新变量
查看>>
sublime 显示文件编码
查看>>
filter,map,reduce
查看>>
sublime text3打开浏览器
查看>>
django中url参数的转换器
查看>>
url命名与反转,应用命名空间与实例命名空间
查看>>
django自定义URL(PATH)转换器
查看>>
随机请求头
查看>>
python中threading多线程以及传参
查看>>
pandas中按照某一列进行排序
查看>>
python中pyodbc连接sql server数据库
查看>>
django2.0,python3.7连接sql_server
查看>>
Python 生成requirement及使用requirements.txt安装类库
查看>>
multiprocessing.pool多线程的使用
查看>>
非计算机专业本科毕业如何迅速成长为一名算法工程师
查看>>
关于自然语言处理(NLP)的个人学习资料
查看>>
BERT
查看>>