Spiga

标签为重构的文章

一天一个重构方法(39):以多态取代条件式

2009-10-17 17:06:14

摘要:Replace conditional with Polymorphism:以多态取代条件式 多态(Polymorphism)是面向对象编程的基本概念之一。在这里,是指在进行类型检查和执行某些类型操作时,最好将算法封装在类中,并且使用多态来对代码中的调用进行抽象。 public abstract class Customer { } public class Employee : Customer { } public class NonEmployee : Customer { } public class OrderProcessor { public decimal ProcessOrder(Customer customer, IEnumerableProduct products) { // do some processing of order 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; } } 如你所见,我们没有利用已有的继承层次进行计算,而是使用了违反SRP 原则的执行方式。要进行重构,我们只需将百分率的计算置于实际的customer类型之中。我知道这只是一项补救措施,但我还是会这么做,就像在代码中那样。 public abstract class Customer { public abstract decimal DiscountPercentage { get; } } public class Employee : Customer { public override decimal DiscountPercentage { get { return 0.15m; } } } publi…… 阅读全文

一天一个重构方法(38):以委托取代继承

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

一天一个重构方法(37):折叠继承体系

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

一天一个重构方法(36):封装群集

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

一天一个重构方法(35):删除中间人

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

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