2009-10-11 16:18:28
                
                    
                        摘要:Replace Inheritance with Delegation:以委托取代继承
某个子类只使用父类接口中的一部分,或者根本不需要继承而来的数据。在子类中新建一个字段用以保存父类;调整子类方法,令它改成委托父类;然后去掉两者之间的继承关系。
public class Sanitation
{
	public string WashHands()
	{
		return Cleaned!;
	}
}
public class Child : Sanitation
{
}
在该例中,Child 并不是Sanitation,因此这样的继承层次是毫无意义的。我们可以这样重构:在Child 的构造函数里实现一个Sanitation实例,并将方法的调用委托给这个实例。如果你使用依赖注入,可以通过构造函数传递Sanitation实例,尽管在我看来还要向IoC容器注册模型是一种坏味道,但领会精神就可以了。继承只能用于严格的继承场景,并不是用来快速编写代码的工具。
public class Sanitation
{
	public string WashHands()
	{
		return Cleaned!;
	}
}
public class Child
{
	private Sanitation Sanitation { get; set; }
	public Child()
	{
		Sanitation = new Sanitation();
	}
	public string WashHands()
	{
		return Sanitation.WashHands();
	}
}
                        阅读全文
                    
                 
                
             
            
                
                2009-10-03 14:30:49
                
                    
                        摘要:Collapse Hierarchy:折叠继承体系
所谓重构继承体系,往往是将方法和字段在体系中上下移动。完成这些动作后,你很可能发现某个子类并未带来该有的价值,因此需要把两个类合并。
public class Website
{
	public string Title { get; set; }
	public string Description { get; set; }
	public IEnumerableWebpage Pages { get; set; }
}
public class StudentWebsite : Website
{
	public bool IsActive { get; set; }
}
这里的子类并没有过多的功能,只是表示站点是否激活。这时我们会意识到判断站点是否激活的功能应该是通用的。因此可以将子类的功能放回到Website 中,并删除StudentWebsite 类型。
public class Website
{
	public string Title { get; set; }
	public string Description { get; set; }
	public IEnumerableWebpage Pages { get; set; }
	public bool IsActive { get; set; }
}
                        阅读全文
                    
                 
                
             
            
                
                2009-09-30 18:54:22
                
                    
                        摘要:Encapsulate Collection:封装群集
有个方法返回一个群集。让这个方法返回该群集的一个只读映件,并在这个类中提供添加/删除(add/remove)群集元素的方法。因此,以可迭代但不直接在集合上进行操作的方式来暴露集合,是个不错的主意。我们来看代码:
public class Order
{
	private int _orderTotal;
	private ListOrderLine _orderLines;
	public IEnumerableOrderLine OrderLines
	{
		get { return _orderLines; }
	}
	public void AddOrderLine(OrderLine orderLine)
	{
		_orderTotal += orderLine.Total;
		_orderLines.Add(orderLine);
	}
	public void RemoveOrderLine(OrderLine orderLine)
	{
		orderLine = _orderLines.Find(o = o == orderLine);
		if (orderLine == null) return;
		_orderTotal -= orderLine.Total;
		_orderLines.Remove(orderLine);
	}
}
如你所见,我们对集合进行了封装,没有将Add/Remove 方法暴露给类的使用者。在.NET Framework中,有些类如ReadOnlyCollection,会由于封装集合而产生不同的行为,但它们各自都有防止误解的说明。这是一个非常简单但却极具价值的重构,可以确保用户不会误用你暴露的集合,避免代码中的一些bug。
                        阅读全文
                    
                 
                
             
            
                
                2009-09-25 11:58:55
                
                    
                        摘要:Remove Middle Man:删除中间人
有时你的代码里可能会存在一些“Phantom”或“Ghost”类,Fowler 称之为“中间人(Middle Man)”。这些中间人类仅仅简单地将调用委托给其他组件,除此之外没有任何功能。就应该删除这个类,让客户直接调用受托类。
public class Consumer
{
	public AccountManager AccountManager { get; set; }
	public Consumer(AccountManager accountManager)
	{
		AccountManager = accountManager;
	}
	public void Get(int id)
	{
		Account account = AccountManager.GetAccount(id);
	}
}
public class AccountManager
{
	public AccountDataProvider DataProvider { get; set; }
	public AccountManager(AccountDataProvider dataProvider)
	{
		DataProvider = dataProvider;
	}
	public Account GetAccount(int id)
	{
		return DataProvider.GetAccount(id);
	}
}
public class AccountDataProvider
{
	public Account GetAccount(int id)
	{
		// get account
	}
}
最终结果已经足够简单了。我们只需要移除中间人对象,将原始调用指向实际的接收者。
public class Consumer
{
	public AccountDataProvider AccountDataProvider { get; set; }
	public Consumer(AccountDataProvider dataProvider)
	{
		AccountDataProvider = dataProvider;
	}
	public void Get(int id)
	{
		Acc……
                        阅读全文
                    
                 
                
             
            
                
                2009-09-22 23:17:08
                
                    
                        摘要:Extract Interface:提炼接口
如果你发现多于一个类使用另外一个类的某些方法,引入接口解除这种依赖往往十分有用。该重构实现起来非常简单,并且能够享受到松耦合带来的好处。
public class ClassRegistration
{
	public void Create()
	{
		// create registration code
	}
	public void Transfer()
	{
		// class transfer code
	}
	public decimal Total { get; private set; }
}
public class RegistrationProcessor
{
	public decimal ProcessRegistration(ClassRegistration registration)
	{
		registration.Create();
		return registration.Total;
	}
}
在下面的代码中,你可以看到我提取出了消费者所使用的两个方法,并将其置于一个接口中。现在消费者不必关心和了解实现了这些方法的类。我们解除了消费者与实际实现之间的耦合,使其只依赖于我们创建的契约。
public interface IClassRegistration
{
	void Create();
	decimal Total { get; }
}
public class ClassRegistration : IClassRegistration
{
	public void Create()
	{
		// create registration code
	}
	public void Transfer()
	{
		// class transfer code
	}
	public decimal Total { get; private set; }
}
public class RegistrationProcessor
{
	public decimal ProcessRegistration(IClassRegistration registration)
	{
		registration.Create();
		return registration.……
                        阅读全文
                    
                 
                
             
            
                
                2009-09-18 20:00:47
                
                    
                        摘要:Extract Subclass:提炼子类
当一个类中的某些方法并不是面向所有的类时,新建一个子类,将只属于某些实体的方法提炼到新建的子类中。
public class Registration
{
	public NonRegistrationAction Action { get; set; }
	public decimal RegistrationTotal { get; set; }
	public string Notes { get; set; }
	public string Description { get; set; }
	public DateTime RegistrationDate { get; set; }
}
当使用了该类之后,我们就会意识到问题所在——它应用于两个完全不同的场景。属性NonRegistrationAction和Notes 只有在处理与普通注册略有不同的NonRegistration 时才会使用。因此,我们可以提取一个子类,并将这两个属性转移到NonRegistration类中,这样才更合适。
public class Registration
{
	public decimal RegistrationTotal { get; set; }
	public string Description { get; set; }
	public DateTime RegistrationDate { get; set; }
}
public class NonRegistration : Registration
{
	public NonRegistrationAction Action { get; set; }
	public string Notes { get; set; }
}
                        阅读全文
                    
                 
                
             
            
                
                2009-09-11 15:55:52
                
                    
                        摘要:Extract Superclass:提炼父类
两个类做类似的事情。可以为这个两个类建立一个父类,将相同特性移至到父类。
public class Dog
{
	public void EatFood()
	{
		// eat some food
	}
	public void Groom()
	{
		// perform grooming
	}
}
重构之后,我们仅仅将需要的方法转移到了一个新的基类中。这很类似“Pull Up”重构,只是在重构之前,并不存在基类。
public class Animal
{
	public void EatFood()
	{
		// eat some food
	}
	public void Groom()
	{
		// perform grooming
	}
}
public class Dog : Animal
{
}
在建立这些具有共通性的类之前,你往往无法发现这样的共通性,因此你经常会在具体共通性的类存在后,再开始建立其间的继承结构。
                        阅读全文
                    
                 
                
             
            
                
                2009-09-07 11:21:48
                
                    
                        摘要:Replace Magic Number with Symbolic Constant:以常量取代特殊数值
在计算科学中,经常会出现一些特殊数值,比如圆周率。如果你需要在不同的地方引用一个特殊数值,不要直接用这个字面数值,请创造一个常量,根据其意义为它命名,并将这个字面数值替换为这个常量。
public double PotentialEnergy(double mass, double height)
{
	return mass * 9.81 * height;
}
将上面实例中的9.81替换成一个常量。许多语言都允许声明常量,常量不会造成任何性能开销,却可以大大提高代码的可读性。上面这个实例中,如果9.81这个特殊数值发生变化,你没有引入常量的话,就需要全部修改一遍。而引入常量就只需要在一个地方做修改。
public double PotentialEnergy(double mass, double height)
{
	return mass * GRAVITATIONAL * height;
}
                        阅读全文
                    
                 
                
             
            
                
                2009-09-03 16:09:16
                
                    
                        摘要:Move Method:搬移方法
你的程序中,有个方法与所驻class之外的另一个class进行更多交流:调用后者,或被后者调用。那么请将方法迁移到合适的class中。
public class BankAccount
{
	public BankAccount(int accountAge, int creditScore,
	AccountInterest accountInterest)
	{
		AccountAge = accountAge;
		CreditScore = creditScore;
		AccountInterest = accountInterest;
	}
	public int AccountAge { get; private set; }
	public int CreditScore { get; private set; }
	public AccountInterest AccountInterest { get; private set; }
	public double CalculateInterestRate()
	{
		if (CreditScore  800)
			return 0.02;
		if (AccountAge  10)
			return 0.03;
		return 0.05;
	}
}
public class AccountInterest
{
	public BankAccount Account { get; private set; }
	public AccountInterest(BankAccount account)
	{
		Account = account;
	}
	public double InterestRate
	{
		get { return Account.CalculateInterestRate(); }
	}
	public bool IntroductoryRate
	{
		get { return Account.CalculateInterestRate()  0.05; }
	}
}
这里值得注意的是BankAccount.CalculateInterest 方法。当一个方法被其他类使用比在它所在类中的使用还要频繁时,我们就需要使用……
                        阅读全文
                    
                 
                
             
            
                
                2009-08-30 14:23:03
                
                    
                        摘要:Remove God Classes:移除工具类
在传统的代码库中,我们常常会看到一些违反了SRP原则的类。这些类通常以Utils或Manager结尾,有时也没有这么明显的特征而仅仅是普通的包含多个功能的类。这种God 类还有一个特征,使用语句或注释将代码分隔为多个不同角色的分组,而这些角色正是这一个类所扮演的。久而久之,这些类成为了那些没有时间放置到恰当类中的方法的垃圾桶。这时的重构需要将方法分解成多个负责单一职责的类。
public class CustomerService
{
	public decimal CalculateOrderDiscount(IEnumerableProduct products, Customer customer)
	{
		// do work
	}
	public bool CustomerIsValid(Customer customer, Order order)
	{
		// do work
	}
	public IEnumerablestring GatherOrderErrors(IEnumerableProduct products, Customer customer)
	{
		// do work
	}
	public void Register(Customer customer)
	{
		// do work
	}
	public void ForgotPassword(Customer customer)
	{
		// do work
	}
}
使用该重构是非常简单明了的,只需把相关方法提取出来并放置到负责相应职责的类中即可。这使得类的粒度更细、职责更分明、日后的维护更方便。上例的代码最终被分解为两个类:
public class CustomerOrderService
{
	public decimal CalculateOrderDiscount(IEnumerableProduct products, Customer customer)
	{
		// do work
	}
	public bool CustomerIsValid(Customer customer, Order order)
	{
		// do work
	}
	public IEnumerablestri……
                        阅读全文