Switch 声明(Switch Statements)你有一个复杂的 switch 语句或 if 序列语句。
Switch 声明(Switch Statements)
switch
if
面向对象程序的一个最明显特征就是:少用 switch 和 case 语句。从本质上说,switch 语句的问题在于重复(if 序列也同样如此)。你常会发现 switch 语句散布于不同地点。如果要为它添加一个新的 case 子句,就必须找到所有 switch语句并修改它们。面向对象中的多态概念可为此带来优雅的解决办法。
case
大多数时候,一看到 switch 语句,就应该考虑以多态来替换它。
提炼函数(Extract Method)
搬移函数(Move Method)
以子类取代类型码(Replace Type Code with Subclass)
以状态/策略模式取代类型码(Replace Type Code with State/Strategy)
以多态取代条件表达式(Replace Conditional with Polymorphism)
以明确函数取代参数(Replace Parameter with Explicit Methods)
引入 Null 对象(Introduce Null Object)
工厂方法模式(Factory Method)
抽象工厂模式(Abstract Factory)
问题
你有一段代码可以组织在一起。
void printOwing() { printBanner();
//print details System.out.println("name: " + name); System.out.println("amount: " + getOutstanding()); }
解决
移动这段代码到一个新的函数中,使用函数的调用来替代老代码。
void printOwing() { printBanner(); printDetails(getOutstanding()); }
void printDetails(double outstanding) { System.out.println("name: " + name); System.out.println("amount: " + outstanding); }
你的程序中,有个函数与其所驻类之外的另一个类进行更多交流:调用后者,或被后者调用。
在该函数最常引用的类中建立一个有著类似行为的新函数。将旧函数变成一个单纯的委托函数,或是旧函数完全移除。
你有一个不可变的类型码,它会影响类的行为。
以子类取代这个类型码。
你有一个类型码,它会影响类的行为,但你无法通过继承消除它。
以状态对象取代类型码。
你手上有个条件表达式,它根据对象类型的不同而选择不同的行为。
class Bird { //... double getSpeed() { switch (type) { case EUROPEAN: return getBaseSpeed(); case AFRICAN: return getBaseSpeed() - getLoadFactor() * numberOfCoconuts; case NORWEGIAN_BLUE: return (isNailed) ? 0 : getBaseSpeed(voltage); } throw new RuntimeException("Should be unreachable"); } }
将这个条件表达式的每个分支放进一个子类内的覆写函数中,然后将原始函数声明为抽象函数。
abstract class Bird { //... abstract double getSpeed(); }
class European extends Bird { double getSpeed() { return getBaseSpeed(); } } class African extends Bird { double getSpeed() { return getBaseSpeed() - getLoadFactor() * numberOfCoconuts; } } class NorwegianBlue extends Bird { double getSpeed() { return (isNailed) ? 0 : getBaseSpeed(voltage); } }
// Somewhere in client code speed = bird.getSpeed();
你有一个函数,其中完全取决于参数值而采取不同的行为。
void setValue(String name, int value) { if (name.equals("height")) { height = value; return; } if (name.equals("width")) { width = value; return; } Assert.shouldNeverReachHere(); }
针对该参数的每一个可能值,建立一个独立函数。
void setHeight(int arg) { height = arg; } void setWidth(int arg) { width = arg; }
你需要再三检查某对象是否为 null。
if (customer == null) { plan = BillingPlan.basic(); } else { plan = customer.getPlan(); }
将 null 值替换为 null 对象。
class NullCustomer extends Customer { Plan getPlan() { return new NullPlan(); } // Some other NULL functionality. }
// Replace null values with Null-object. customer = (order.customer != null) ? order.customer : new NullCustomer();
// Use Null-object as if its normal subclass. plan = customer.getPlan();
临时栏位(Temporary Field)的值只在特定环境下有意义,离开这个环境,它们就什么也不是了。
有时你会看到这样的对象:其内某个实例变数仅为某种特定情况而设。这样的代码让人不易理解,因为你通常认为对象在所有时候都需要它的所有变数。在变数未被使用的情况下猜测当初设置目的,会让你发疯。 通常,临时栏位是在某一演算法需要大量输入时而创建。因此,为了避免函数有过多参数,程序员决定在类中创建这些数据的临时栏位。这些临时栏位仅仅在演算法中使用,其他时候却毫无用处。 这种代码不好理解。你期望查看对象栏位的数据,但是出于某种原因,它们总是为空。
提炼类(Extract Class)
以函数对象取代函数(Replace Method with Method Object)
某个类做了不止一件事。
建立一个新类,将相关的栏位和函数从旧类搬移到新类。
你有一个过长函数,它的局部变数交织在一起,以致于你无法应用提炼函数(Extract Method) 。
class Order { //... public double price() { double primaryBasePrice; double secondaryBasePrice; double tertiaryBasePrice; // long computation. //... } }
将函数移到一个独立的类中,使得局部变数成了这个类的栏位。然后,你可以将函数分割成这个类中的多个函数。
class Order { //... public double price() { return new PriceCalculator(this).compute(); } }
class PriceCalculator { private double primaryBasePrice; private double secondaryBasePrice; private double tertiaryBasePrice;
public PriceCalculator(Order order) { // copy relevant information from order object. //... }
public double compute() { // long computation. //... } }
异曲同工的类(Alternative Classes with Different Interfaces)两个类中有著不同的函数,却在做著同一件事。
异曲同工的类(Alternative Classes with Different Interfaces)
这种情况往往是因为:创建这个类的程序员并不知道已经有实现这个功能的类存在了。
函数改名(Rename Method)
添加参数(Add Parameter)
令函数携带参数(Parameterize Method)
提炼超类(Extract Superclass)
函数的名称未能恰当的揭示函数的用途。
class Person { public String getsnm(); }
修改函数名。
class Person { public String getSecondName(); }
问题 某个函数需要从调用端得到更多信息。
class Customer { public Contact getContact(); }
解决 为此函数添加一个对象函数,让改对象带进函数所需信息。
class Customer { public Contact getContact(Date date); }
若干函数做了类似的工作,但在函数本体中却包含了不同的值。
建立单一函数,以参数表达哪些不同的值。
两个类有相似特性。
为这两个类建立一个超类,将相同特性移至超类。
被拒绝的馈赠(Refused Bequest)子类仅仅使用父类中的部分方法和属性。其他来自父类的馈赠成为了累赘。
被拒绝的馈赠(Refused Bequest)
有些人仅仅是想重用超类中的部分代码而创建了子类。但实际上超类和子类完全不同。
以委托取代继承(Replace Inheritance with Delegation)
某个子类只使用超类介面中的一部分,或是根本不需要继承而来的数据。