Spiga

分类为编程思维的文章

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

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

2009-08-13 23:58:24

摘要:Extract class:提炼类 你也许听过类似这样的教诲:一个class应该是一个清楚的抽象,处理一些明确的责任。但是在实际工作中,class会不断成长扩展。于是随着责任不断增加,这个class会变得很复杂。很快这个class就会变成一团乱麻。建立一个新的class,将相关的字段和方法从旧class搬移到新class。 public class Person { public string GetName() { //other code } public void SetName(string name) { //other code } public string GetTelephoneNumber() { //other code } public string GetOfficeAreaCode() { //other code } public void SetOfficeAreaCode(string areaCode) { //other code } public string GetOfficNumber() { //other code } public void SetOfficNumber(string number) { //other code } } 这个例子中,可以将与Office相关的行为分离到一个独立的class中。 public class Person { public string GetName() { //other code } public void SetName(string name) { //other code } public string GetTelephoneNumber() { //other code } } public class Office { public string GetOfficeAreaCode() { //other code } public void SetOfficeAreaCode(string areaCode) { //other code } public string GetOfficNumber() …… 阅读全文