改善代码设计 —— 优化物件之间的特性(Moving Features Between Objects)
1. Move Method (函数搬家)
解释:
如果 ClassA 的某个函数对 ClassB 有过多的依赖, 可以考虑将这个函数搬到 ClassB 中, 在 ClassA 的这个函数中直接调用 ClassB中这个函数的返回值.
这样做的好处是减少物件与物件之间的耦合度, 很多情况下这样做更利于进一步的重构.
冲动前:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 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 ; } } |
冲动后:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 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 这个物件中作为员工的一个属性.
这样做可使程序扩展性变得更好, 最明显的是我可以设置不同员工的起薪了.
冲动前:
1 2 3 4 5 6 7 8 9 10 11 | class EmployeeSalary { private double baseSalary = 15000.0; public double Salary() { double salary = baseSalary; //do some compution with salary return salary; } } |
冲动后:
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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.
冲动前:
1 2 3 4 5 6 7 8 9 10 11 12 | 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; } } |
冲动后:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | 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() 就行了.
冲动前:
1 2 3 4 5 6 7 8 | class Department { public string Manger { get ; set ; } } class Employee { public Department Department { get ; set ; } } |
冲动后:
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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 的方法 , 并且实际中多数情况下这种重构用于引用类型
冲动前:
1 2 3 | int num = 1; //I want to get num's next int nextNum = num + 1; |
冲动后:
1 2 3 4 5 6 7 | 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.
本文链接: http://www.cnblogs.com/technology/archive/2011/05/11/2043307.html
作者:Create Chen
出处:http://technology.cnblogs.com
说明:文章为作者平时里的思考和练习,可能有不当之处,请博客园的园友们多提宝贵意见。
本作品采用知识共享署名-非商业性使用-相同方式共享 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架