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

简介:   系列博客      1. 改善代码设计 —— 优化函数的构成(Composing Methods)      2. 改善代码设计 —— 优化物件之间的特性(Moving Features Between Objects)      3.

  系列博客

      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.

目录
相关文章
|
3天前
|
存储 编译器
.Net特性Attribute的高级使用
【10月更文挑战第14天】在.NET中,特性(Attribute)是一种强大的机制,用于在代码中添加元数据。本文介绍了特性的高级用法,包括自定义特性、通过反射读取特性、条件编译与特性结合、多个特性应用以及特性继承。通过示例展示了如何创建自定义特性类、应用自定义特性,并通过反射获取特性信息。此外,还介绍了如何利用条件编译符号实现不同版本的代码控制,以及如何在一个代码元素上应用多个特性。最后,探讨了如何通过`AttributeUsage`控制特性的继承行为。
|
29天前
|
人工智能 前端开发 开发工具
解读.NET 技术的开发潜力
本文全面介绍了.NET技术在软件开发领域的核心优势、创新应用及面临的挑战。.NET以其统一的开发平台、强大的工具和跨平台能力,成为企业级应用、Web应用乃至游戏开发的理想选择。然而,在性能优化、容器化及AI集成等方面仍需不断突破。通过积极拥抱开源和社区驱动模式,.NET将持续推动软件开发的进步。
47 1
|
1月前
|
人工智能 前端开发 Devops
.NET技术自发布以来,在软件开发领域发挥了重要作用
【9月更文挑战第12天】.NET技术自发布以来,在软件开发领域发挥了重要作用。本文分为三部分探讨其在现代开发中的应用:首先介绍.NET的核心价值,包括语言多样性、强大的开发工具支持、丰富的类库、跨平台能力和活跃的社区;接着分析其在企业级应用、Web开发、移动应用、云服务及游戏开发中的实际应用;最后讨论.NET面临的挑战与未来趋势,如性能优化、容器化、AI集成及跨平台框架竞争等。通过不断的技术创新和社区驱动,.NET将持续推动软件开发的进步。
32 4
|
1月前
|
人工智能 开发框架 算法
C#/.NET/.NET Core技术前沿周刊 | 第 2 期(2024年8.19-8.25)
C#/.NET/.NET Core技术前沿周刊 | 第 2 期(2024年8.19-8.25)
|
1月前
|
传感器 应用服务中间件 Linux
C#/.NET/.NET Core技术前沿周刊 | 第 3 期(2024年8.26-8.31)
C#/.NET/.NET Core技术前沿周刊 | 第 3 期(2024年8.26-8.31)
|
1月前
|
开发框架 前端开发 JavaScript
ASP.NET MVC 教程
ASP.NET 是一个使用 HTML、CSS、JavaScript 和服务器脚本创建网页和网站的开发框架。
32 7
|
1月前
|
存储 开发框架 前端开发
ASP.NET MVC 迅速集成 SignalR
ASP.NET MVC 迅速集成 SignalR
42 0
|
2月前
|
开发框架 前端开发 .NET
ASP.NET MVC WebApi 接口返回 JOSN 日期格式化 date format
ASP.NET MVC WebApi 接口返回 JOSN 日期格式化 date format
38 0
|
2月前
|
开发框架 前端开发 安全
ASP.NET MVC 如何使用 Form Authentication?
ASP.NET MVC 如何使用 Form Authentication?
|
2月前
|
开发框架 .NET
Asp.Net Core 使用X.PagedList.Mvc.Core分页 & 搜索
Asp.Net Core 使用X.PagedList.Mvc.Core分页 & 搜索
99 0