错失多态的机会

Missing Opportunities for Polymorphism

Polymorphism(多态)是面向对象中重要的思想之一。源于希腊语,即多(poly)面(morph)的意思。在程序的上下文中,多态是特定类的对象或方法的多种形态。但多态并不是简单的交替式实现。使用时要小心,多态会创建个很小的局部执行上下文,以让我们无需去关心那冗长的if-then-else代码块。在上下文内部允许我们直接处理事务,但在上下文外部却要强制我们重建它之后才能处理事务。谨慎使用交替实现,我们可以捕获上下文以让代码更少更可读。最好先看个代码示例,比如下面这段简单(到爆)的购物车:

1
2
3
4
5
6
public class ShoppingCart {
private ArrayList<Item> cart = new ArrayList<Item>();
truepublic void add(Item item) { cart.add(item); }
public Item takeNext() { return cart.remove(0); }
truepublic boolean isEmpty() { return cart.isEmpty(); }
}

假设我们的网店里陈列着可下载的(虚拟)产品和需要运输的(实物)产品。我们就要让对象支持这些操作:

1
2
3
4
public class Shipping {
public boolean ship(Item item, SurfaceAddress address) { ... }
truepublic boolean ship(Item item, EMailAddress address) { ... }
}

当用户下单后,我们需要发货:

1
2
3
while (!cart.isEmpty()) {
trueshipping.ship(cart.takeNext(), ???);
}

代码中的的???可不是什么花式操作;而是用来询问我该用电子邮件还是快递发送这些商品。但回答这个问题的上下文并不存在,我们只能用布尔枚举的形式事先捕获发货方式,然后用if-then-else去补全这个参数。

另一种解决方案是创建两个继承于Item的类,称为DownloadableItemSurfaceItem。现在来实现代码,我会让Item作为一个接口,并支持单一方法——ship。为了能顺利发货购物车的商品,我们需要调用item.ship(shipper)。而DownloadableItemSurfaceItem两个类都需要实现ship

1
2
3
4
5
6
7
8
9
10
11
public class DownloadableItem implements Item {
public boolean ship(Shipping shipper, Customer customer) {
shipper.ship(this, customer.getEmailAddress());
}
}

public class SurfaceItem implements Item {
public boolean ship(Shipping shipper, Customer customer) {
shipper.ship(this, customer.getSurfaceAddress());
}
}

在这个例子中,我们将负责运输的工作委托给了每个商品。由于每个商品都清楚自己的最佳发货方式,这种模式可以继续下去,且不在需要if-then-else。这个段代码也示范了两种经常一起使用的设计模式:命令模式和双重调度。能否有效地运用这些设计模式取决于你是否谨慎地使用多态。这样一来,能够大幅缩减代码中的if-then-else。

尽管在某些情况下用if-then-else会比多态更实用,但更多时候,多态的编码风格将产出更小巧、更可读、也更健壮的代码库。错失机会的次数可能就是我们代码中if-then-else的数量。

0%