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……
阅读全文
2009-08-23 19:02:42
摘要:Push Down Method:下移方法
与下移字段一样,如果父类中某个方法只与部分子类有关,将这个方法移到相关的那些子类去。
public abstract class Animal
{
public void Bark()
{
// code to bark
}
}
public class Dog : Animal
{
}
public class Cat : Animal
{
}
这里的基类有一个Bark方法。或许我们的猫咪们一时半会也没法学会汪汪叫(bark),因此Cat 类中不再需要这个功能了。尽管基类不需要这个方法,但在显式处理Dog 类时也许还需要,因此我们将Bark 方法“下移”到Dog 类中。这时,有必要评估Animal基类中是否还有其他行为。如果没有,则是一个将Animal抽象类转换成接口的好时机。因为契约中不需要任何代码,可以认为是一个标记接口。
public abstract class Animal
{
}
public class Dog : Animal
{
public void Bark()
{
// code to bark
}
}
public class Cat : Animal
{
}
阅读全文
2009-08-19 16:32:34
摘要:Push Down Field:下移字段
与上移字段相反的重构是下移字段,如果父类中某个字段只被部分子类用到,将这个字段移到需要它的那些子类去。
public abstract class Task
{
protected string _resolution;
}
public class BugTask : Task
{
}
public class FeatureTask : Task
{
}
在这个例子中,基类中的一个字符串字段只被一个子类使用,因此可以进行下移。只要没有其他子类使用基类的字段时,就应该立即执行该重构。保留的时间越长,就越有可能不去重构而保持原样。
public abstract class Task
{
}
public class BugTask : Task
{
private string _resolution;
}
public class FeatureTask : Task
{
}
阅读全文
2009-08-16 18:16:45
摘要:Pull Up Mothod:方法上移
今天介绍的重构方法和上一篇字段上移十分类似。我们今天处理的不是字段,而是方法。
有些方法,在各个子类中产生完全相同的结果,将该方法移至到父类。
public abstract class Vehicle
{
// other methods
}
public class Car : Vehicle
{
public void Turn(Direction direction)
{
// code here
}
}
public class Motorcycle : Vehicle
{
}
public enum Direction
{
Left,
Right
}
如你所见,目前只有Car类中包含Turn方法,但我们也希望在Motorcycle 类中使用。因此,如果没有基类,我们就创建一个基类并将该方法“上移”到基类中,这样两个类就都可以使用Turn 方法了。这样做唯一的缺点是扩充了基类的接口、增加了其复杂性,因此需谨慎使用。只有当一个以上的子类需要使用该方法时才需要进行迁移。如果滥用继承,系统将会很快崩溃。这时你应该使用组合代替继承。重构之后的代码如下:
public abstract class Vehicle
{
public void Turn(Direction direction)
{
// code here
}
}
public class Car : Vehicle
{
}
public class Motorcycle : Vehicle
{
}
public enum Direction
{
Left,
Right
}
阅读全文
2009-08-15 17:44:12
摘要:Pull Up Field:字段上移
两个子类拥有相同的字段,将此一字段上移到父类。
public abstract class Account
{
}
public class CheckingAccount : Account
{
private decimal _minimumCheckingBalance = 5m;
}
public class SavingsAccount : Account
{
private decimal _minimumSavingsBalance = 5m;
}
在这个例子中,两个子类中包含重复的常量。为了提高复用性我们将字段上移到基类中,并简化其名称。
public abstract class Account
{
protected decimal _minimumBalance = 5m;
}
public class CheckingAccount : Account
{
}
public class SavingsAccount : Account
{
}
本项重构从两方面减少重复:首先它去除了重复的数据声明;其次它使你可以将使用该字段的行为从子类移至父类,从而去除重复的行为。
阅读全文