Spiga

分类为编程思维的文章

一天一个重构方法(34):提炼接口

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.…… 阅读全文

一天一个重构方法(33):提炼子类

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; } } 阅读全文

一天一个重构方法(32):提炼父类

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 { } 在建立这些具有共通性的类之前,你往往无法发现这样的共通性,因此你经常会在具体共通性的类存在后,再开始建立其间的继承结构。 阅读全文

一天一个重构方法(31):以常量取代特殊数值

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; } 阅读全文

一天一个重构方法(30):搬移方法

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 方法。当一个方法被其他类使用比在它所在类中的使用还要频繁时,我们就需要使用…… 阅读全文

一天一个重构方法(29):移除工具类

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…… 阅读全文

一天一个重构方法(28):下移方法

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 { } 阅读全文

一天一个重构方法(27):下移字段

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 { } 阅读全文

一天一个重构方法(26):方法上移

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 } 阅读全文

一天一个重构方法(25):字段上移

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 { } 本项重构从两方面减少重复:首先它去除了重复的数据声明;其次它使你可以将使用该字段的行为从子类移至父类,从而去除重复的行为。 阅读全文