代码改变世界

一起谈.NET技术,改善代码设计 —— 优化物件之间的特性(Moving Features Between Objects)

2011-09-01 23:57  狼人:-)  阅读(137)  评论(0编辑  收藏  举报

  系列博客

      1. 改善代码设计 —— 优化函数的构成(Composing Methods)

      2. 改善代码设计 —— 优化物件之间的特性(Moving Features Between Objects)

      3. 改善代码设计 —— 组织好你的数据(Composing Data)

      4. 改善代码设计 —— 简化条件表达式(Simplifying Conditional Expressions)

      5. 改善代码设计 —— 简化函数调用(Making Method Calls Simpler)

      6. 改善代码设计 —— 处理概括关系(Dealing with Generalization)

  1. Move Method (函数搬家)

  解释:

        如果 ClassA 的某个函数对 ClassB 有过多的依赖, 可以考虑将这个函数搬到 ClassB 中, 在 ClassA 的这个函数中直接调用 ClassB中这个函数的返回值.

        这样做的好处是减少物件与物件之间的耦合度, 很多情况下这样做更利于进一步的重构.

  冲动前:

class EmployeeSalary
{
private double baseSalary = 15000.0;

public double Salary(Employee employee)
{
return baseSalary + 10000 / employee.Level;
}
// other method with baseSalary
}
class Employee
{
public int Level { get; set; }
}

  冲动后:

class EmployeeSalary
{
private double baseSalary = 15000.0;

public double Salary(Employee employee)
{
return employee.Salary(baseSalary);
}
// other method with baseSalary
}
class Employee
{
public int Level { get; set; }
public double Salary(double baseSalary)
{
return baseSalary + 10000 / Level;
}
}

  2. Move Field (值域搬家)

  解释:

      有一天发现公司原来计算员工工资的方法不合适了, 比如不是所有的员工起薪 (baseSalary) 都是一万五, 我想把 baseSalary 搬到 Employee 这个物件中作为员工的一个属性.

      这样做可使程序扩展性变得更好, 最明显的是我可以设置不同员工的起薪了.

  冲动前:

class EmployeeSalary
{
private double baseSalary = 15000.0;

public double Salary()
{
double salary = baseSalary;
//do some compution with salary
return salary;
}
}

  冲动后:

class EmployeeSalary
{
public double Salary(Employee employee)
{
double salary = employee.BaseSalary;
//do some compution with salary
return salary;
}
}
class Employee
{
public double BaseSalary { get; set; }
}

  3. Extract Class (提炼类)

  解释:

      当某个物件做的事情过多, 这样的物件往往含有大量的字段, 属性和方法. 应该由两个或更多个物件来分担这些责任, 这时需要使用 Extract Class.

  冲动前:

class Employee
{
public double BaseSalary { get; set; }
public double Level { get; set; }

public double Salary()
{
double salary = BaseSalary;
//do some complex compution with salary
return salary;
}
}

  冲动后:

class EmployeeSalary
{
public double Salary(Employee employee)
{
double salary = employee.BaseSalary;
//do some complex compution with salary
return salary;
}
}
class Employee
{
public double BaseSalary { get; set; }
public double Level { get; set; }

public double Salary()
{
EmployeeSalary salary
= new EmployeeSalary();
return salary.Salary(this);
}
}

  4. Inline Class (将类内联)

  解释:

      Inline Class 和 Extract Class 正好相反. 当一个物件没有做它应该做的事情, 还专门使用了另一个物件来协助它完成这个职责, 这时可以考虑使用 Inline Class.

      如上面所示的例子, 如果我觉得 Employee 这个物件本身就应该实现 Salary 的计算工作, 而不是专门写一个 Salary 的计算物件来帮助它计算, 可以使用 Inline Class 将 Salary 内联到 Employee 中去, 也就是"冲动后"的代码重构成"冲动前"代码的样子.

  5. Hide Delegate (隐藏委托关系)

  解释:

      试想这么一个情况: 有一个 Employee 类, 这个类中含有一个部门 (Department) 属性, 并且 Department 是一种类. 如果我想知道某职工所在部门的经理人是谁的时候, 我需要通过 xxEmployee.Department.Manger 来访问. 但这样做有个缺点是对于其它代码,  Department 是 public 的, 其它代码能够访问到 Department 里的其它特性. 可以在 Employee 类中写一个 GetManger() 方法进行封装, 在调用的时候只需要xxEmployee.GetManger() 就行了.

  冲动前:

class Department
{
public string Manger { get; set; }
}
class Employee
{
public Department Department { get; set; }
}

  冲动后:

class Department
{
public string Manger { get; set; }
}
class Employee
{
private Department Department;

public string GetManger()
{
return Department.Manger;
}
}

  6. Remove Middle Man (干掉中间人)

  解释:

      这一条与上一条 Hide Delegate 是相反的. 当我们要访问 Department 的其它很多特性时, 我们用 Hide Delegate 写了一条条简单的委托访问函数, 当这些函数多到几乎访问遍了 Department 里的内容, 可以考虑使用 Remove Middle Man 方法将这些访问函数干掉.

      如上面的例子, 就是将"冲动后"的代码重构成"冲动前"代码的样子.

  7. Introduce Foreign Method (引入外加函数)

  解释:

      Introduce Foreign Method 有很深的 C#3.0 中扩展方法的味道, 但扩展方法比 Introduce Foreign Method 好在: 扩展方法就好象是被扩展的那个类型自己的方法一样, 而 Introduce Foreign Method 的函数还需要传递这个类型的参数, 但其实编译器编译扩展方法后还是会把参数传进去的, 扩展方法只是一种语法糖.

      它的主要目的是实现被调用的类中没有实现的功能, 注意在进行本项重构时, 如果引入一个外加函数, 这说明这个函数本应该在被调用的类中实现. 下面举一个简单到不能再简单的例子, 这个例子只是说明怎么使用 Introduce Foreign Method, 我并不是说 Int32类型就应该有一个 NextNum 的方法 , 并且实际中多数情况下这种重构用于引用类型微笑

  冲动前:

int num = 1;
//I want to get num's next
int nextNum = num + 1;

  冲动后:

int num = 1;
int nextNum = NextNum(num);

private static int NextNum(int arg)
{
return arg + 1;
}

  8. Introduce Local Extension (引入本地扩展)

  解释:

      如果我不想使用 Introduce Foreign Method, 我觉得它就本来应该实现某某功能, 如果被调用的类不是密封 (sealed) 的话, 可以自定义一个数据类型, 继承这个类, 在自己定义的数据类型中实现我想要它实现的功能, 这就是 Introduce Local Extension.