16,封装条件概念:本文中的“封装条件”是指条件关系比较复杂时,代码的可读性会比较差,所以这时我们应当根据条件表达式是否需要参数将条件表达式提取成可读性更好的属性或者方法,如果条件表达式不需要参数则可以提取成属性,如果条件表达式需要参数则可以提取成方法。
总结:这个重构在很大程度上能改善代码的可读性,尤其是在一个逻辑很复杂的应用中,把这些条件判断封装成一个有意义的名字,这样很复杂的逻辑也会立刻变得简单起来。
17,提取父类概念:本文中的“提取父类”是指类中有一些字段或方法,你想把它们提取到父类中以便同一继承层次的其它类也可以访问他们,这个和之前的很多重构有异曲同工之处。
总结:这个重构是典型的继承用法,很多程序员都会选择这样做,但是要注意正确的使用,不要造成过度使用了继承,如果过度使用了,请考虑用
接口、组合和聚合来实现。
18,使用条件判断代替异常概念:本文中的“使用条件判断代替异常”是指把没有必要使用异常做判断的条件尽量改为条件判断。
总结: 这个重构在项目代码中也经常用到,因为对于一部分程序员,是很难把握什么时候用try catch ,什么地方该用try catch 。记得之前大家还专门讨论过这些,比如如何用好以及在大中型项目中应该把它放在哪一个组件中等。
19,提取工厂类概念:本文中的“提取工厂类”是指如果要创建的对象很多,则代码会变的很复杂。一种很好的方法就是提取工厂类。
总结:这个重构经常会在项目中使用,如果要创建的对象是一个,你可以采用简单工厂,但是这种方式还是会存在很多依赖,维护起来也比较不方便。所以推荐使用工厂方法模式,把实例化延迟到子类。如果你要创建一系列的对象,那么就推荐你使用抽象工厂模式,但是要注意不要过度设计,只要能满足不断变化的需求和给以后的维护和重构带来方便即可。
20,提取子类概念:本文中的”提取子类”是指把基类中的一些不是所有子类都需要访问的方法调整到子类中。
总结:这个重构方法经常用来规范类的职责,和之前的一些重构方法也有些类似。
21,合并继承概念:本文中的”合并继承”是指如果子类的属性和方法也适合于基类,那么就可以移除子类,从而减少依赖关系。
正文:上一篇我们讲到“提取子类”重构是指当基类中的一个责任不被所有的子类所需要时,将这些责任提取到合适的子类中。而我们今天所要讲的的“合并继承”重构一般用在当我们觉得不需要子类的时候。
总结: 这篇和上篇其实最主要论述了子类和父类的继承关系以及如何判断什么时候需要使用继承,一般我们都能处理好这些关系,所以相对比较简单。
22,分解方法概念:本文中的”分解方法”是指把我们所做的这个功能不停的分解方法,直到将一个大方法分解为名字有意义且可读性更好的若干个小方法。
总结:其实这个重构和我们前面讲的“提取方法”和“提取方法对象”如出一辙,尤其是“提取方法”,所以大家只要知道用这种思想重构就行。
23,引入参数对象概念:本文中的“引入参数对象”是指当一个方法的参数过多或者过为复杂时,可以考虑把这些参数封装成一个单独的类。
正文:如果一个方法所需要的参数大于5个,理解该方法的签名就变得比较困难,因为这样感觉参数很长、样式不好并且没有分类,所以我们有必要把参数进行封装。
总结:这种重构很重要,尤其是当一个方法的参数比较多的时候,不管是大中型项目还是小型项目,都会遇到这种场景,所以建议大家多使用这个重构。这种封装的思想在SOA 里面也经常运用到,封装输入Message,封装输出Message,消息来和消息去以及消息间的交互就构成了整个应用体系。
24,分解复杂判断概念:本文中的”分解复杂判断”是指把原来复杂的条件判断等语句用尽快返回等方式简化代码。
正文:简单的来说,当你的代码中有很深的嵌套条件时,花括号就会在代码中形成一个长长的箭头。我们经常在不同的代码中看到这种情况,并且这种情况也会扰乱代码的可读性。那么重构很简单,如果有可能的话,我们让代码在做处理任务之前先检查条件,如果条件不满足就尽快返回,不继续执行。
总结:这个重构很重要,它和后面讲的”尽快返回“有些类似,我们在做复杂的处理过程时,要经常考虑这个重构,用好了它,会对我们的帮助很大。
25, 引入契约式设计概念:本文中的”引入契约式设计”是指我们应该对输入和输出进行验证,以确保系统不会出现我们所想象不到的异常和得不到我们想要的结果。
正文:契约式设计规定方法应该对输入和输出进行验证,这样你便可以保证你得到的数据是可以工作的,一切都是按预期进行的,如果不是按预期进行,异常或是错误就应该被返回。
总结:微软在处理代码乃至产品的时候,很喜欢应用此重构,你如果认真看它的代码库,认真看一下WCF 的设计,就不难发现了。这个重构建议大家经常使用,这会增强整个系统的稳定性和健壮性。
26,避免双重否定概念:本文中的”避免双重否定”是指把代码中的双重否定语句修改成简单的肯定语句,这样即让代码可读,同时也给维护带来了方便。
正文:避免双重否定重构本身非常容易实现,但我们却在太多的代码中见过因为双重否定降低了代码的可读性以致于非常让人容易误解真正意图。存在双重否定的代码具有非常大的危害性,因为这种类型的代码容易引起错误的假设,错误的假设又会导致书写出错误的维护代码,最终会导致bug 产生。
举个例子:某类中有个属性IsNotFlagged,意思就是“是否没有标记”,那么我们在判断“有标记”时,就要if(!IsNotFlagged),这很让人费解,因为!就代表否定的意思,而这里的!IsNotFlagged却代表肯定。正确的做法是将IsNotFlagged属性改为IsFlagged。
总结: ”双重否定“很容易让人产生错误的判断,也很难让人理解你的代码,所以这个重构在我们的代码中是很重要的,尤其是在判断条件很多且业务复杂的时候。
27, 去除上帝类概念:本文中的”去除上帝类”是指把一个看似功能很强且很难维护的类,按照职责把自己的属性或方法分派到各自的类中或分解成功能明确的类,从而去掉上帝类。
正文:我们经常可以在一些原来的代码中见到一些类明确违反了SRP 原则(单一原则),这些类通常以“Utils”或“Manager”或“Tools”后缀结尾,但有时这些类也没有这些特征,它仅仅是多个类多个方法的组合。另一个关于上帝类的特征是通常这些类中的方法被用注释分隔为不同的分组。那么久而久之,这些类被转换为那些没有人愿意进行归并到合适类的方法的聚集地,对这些类进行重构是将类中的代码按照职责分派到各自的类中,这样就解除了上帝类,也减轻了维护的负担。
总结: “去除上帝类”是我们经常容易造成的,第一是因为简便,看到有一个现成的类,大家都会喜欢把代码往里面写,最后导致越写越大,并且声明功能都有,这样即降低了可读性,也造成了维护的负担。
28,为布尔方法命名概念:本文中的”为布尔方法命名”是指如果一个方法带有大量的bool 参数时,可以根据bool参数的数量,提取出若干个独立的方法来简化参数。
正文:我们现在要说的重构并不是普通字面意义上的重构,它有很多值得讨论的地方。当一个方法带有大量的bool 参数时,会导致方法很容易被误解并产生非预期的行为,
public class BankAccount
{
public void CreateAccount(Customer customer, bool withChecking, bool withSavings, bool withStocks)
{
// do work
}
}
我们可以将上面的bool 参数以独立方法的形式暴露给调用端以提高代码的可读性,同时我们还需要将原来的方法改为private 以限制其可访问性。显然我们关于要提取的独立方法会有一个很大的排列组合,这是一大缺点,所以我们可以考虑引入”参数对象“重构。
public class BankAccount
{
public void CreateAccountWithChecking(Customer customer)
{
CreateAccount(customer, true, false);
}
public void CreateAccountWithCheckingAndSavings(Customer customer)
{
CreateAccount(customer, true, true);
}
private void CreateAccount(Customer customer, bool withChecking, bool withSavings)
{
// do work
}
}
总结:“为布尔方法命名”这个重构在很多时候都不常用,如果用户的参数可枚举,我们一般会枚举它的值,不过使用这种重构也有好处,就是分解开来以后,方法多了,参数少了,代码维护起来方便了一些。
29,去除中间人对象概念:本文中的”去除中间人对象”是指把 在中间关联而不起任何其他作用的类移除,让有关系的两个类直接进行交互。
正文:有些时候在我们的代码会存在一些”幽灵类“,设计模式大师Fowler 称它们为“中间人”类,“中间人”类除了调用别的对象之外不做任何事情,所以“中间人”类没有存在的必要,我们可以将它们从代码中删除,从而让交互的两个类直接关联。
总结: “去除中间人对象”很多时候都会很有作用,尤其是在误用设计模式的代码中最容易见到,设计模式中的适配器模式和代理模式等都用中间的类是两者进行关联,这是比较合理的,因为中间类做了很多事情,而对于没有任何作用的中间类应该移除。
30,尽快返回概念: 本文中的”尽快返回”是指把原来复杂的条件判断等语句用尽快返回的方式简化代码。
正文:如首先声明的是前面讲的”分解复杂判断“,简单的来说,当你的代码中有很深的嵌套条件时,花括号就会在代码中形成一个长长的箭头。我们经常在不同的代码中看到这种情况,并且这种情况也会扰乱代码的可读性。
有些隐形的判断,可能很多情况下看不出来,也需要多注意,如下示例:
public class Order
{
public Customer Customer { get; private set; }
public decimal CalculateOrder(Customer customer, IEnumerable<Product > products, decimal discounts)
{
Customer = customer;
decimal orderTotal = 0m;
if (products.Count() > 0)
{
orderTotal = products.Sum(p = > p.Price);
if (discounts > 0)
{
orderTotal -= discounts;
}
}
return orderTotal;
}
}
重构后如下:
public class Order
{
public Customer Customer { get; private set; }
public decimal CalculateOrder(Customer customer, IEnumerable<Product > products, decimal discounts)
{
if (products.Count() == 0)
return 0;
Customer = customer;
decimal orderTotal = products.Sum(p = > p.Price);
if (discounts == 0)
return orderTotal;
orderTotal -= discounts;
return orderTotal;
}
}
总结:这个重构很重要,它和前面讲的”分解复杂判断“有些类似,我们在做复杂的处理过程时,要经常考虑这个重构,用好了它,会对我们的帮助很大。
31,使用多态代替条件判断概念:本文中的”使用多态代替条件判断”是指如果你需要检查对象的类型或者根据类型执行一些操作时,一种很好的办法就是将算法封装到类中,并利用多态性进行抽象调用。
正文:本文展示了面向对象编程的基础之一“多态性”, 有时你需要检查对象的类型或者根据类型执行一些操作时,一种很好的办法就是将算法封装到类中,并利用多态性进行抽象调用。
如下面示例:
public abstract class Customer
{ }
public class Employee : Customer
{ }
public class NonEmployee : Customer
{ }
public class OrderProcessor
{
public decimal ProcessOrder(Customer customer, IEnumerable<Product > products)
{
decimal orderTotal = products.Sum(p = > p.Price);
Type customerType = customer.GetType();
if (customerType == typeof(Employee))
{
orderTotal -= orderTotal * 0.15m;
}
else if (customerType == typeof(NonEmployee))
{
orderTotal -= orderTotal * 0.05m;
}
return orderTotal;
}
}
该示例我们在OrderProcessor类的ProcessOrder方法中判断客户的类型,然后根据类型给予不同的折扣。
重构后的代码如下:
public abstract class Customer
{
public abstract decimal DiscountPercentage { get; }
}
public class Employee : Customer
{
public override decimal DiscountPercentage
{
get { return 0.15m; }
}
}
public class NonEmployee : Customer
{
public override decimal DiscountPercentage
{
get { return 0.05m; }
}
}
public class OrderProcessor
{
public decimal ProcessOrdedr(Customer customer, IEnumerable<Product > products)
{
decimal orderTotal = products.Sum(p = > p.Price);
orderTotal -= orderTotal * customer.DiscountPercentage;
return orderTotal;
}
}
重构后我们每种类型的客户就已经包含了自己的折扣率,对使用者ProcessOrdedr来说,就简单多了。
总结:“使用多态代替条件判断”这个重构在很多时候会出现设计模式中(常见的工厂家族、策略模式等都可以看到它的影子),因为运用它可以省去很多的条件判断,同时也能简化代码、规范类和对象之间的职责。