简化函数调用之十三 :Encapsulate Downcast(封装「向下转型」动作)

来源:互联网 发布:app的数据库设计 编辑:程序博客网 时间:2024/04/29 12:44

某个函数返回的对象,需要由函数调用者执行「向下转型」(downcast)动作。

将向下转型(downcast)动作移到函数中。

Object lastReading() {

  return readings.lastElement();

}

 

Reading lastReading() {

  return (Reading) readings.lastElement();

}

动机(Motivation)

在强型别(strongly typed)OO语言中,向下转型是最烦人的事情之一。之所以很烦人,是因为从感觉上来说它完全没有必要:你竟然越俎代庖地告诉编译器某些应该由编译器自己计算出来的东西。但是,由于「计算对象型别」的动作往往比较麻烦,你还是常常需要亲自告诉编译器「对象的确切型别」。向下转型在Java 特别盛行,因为Java 没有template(模板)机制,因此如果你想从群集(collection)之中取出一个对象,就必须进行向下转型。

向下转型也许是一种无法避免的罪恶,但你仍然应该尽可能少做。如果你的某个函数返回一个值,并且你知道「你所返回的对象」其型别比函数签名式(signature) 所昭告的更特化(specialized;译注:意指返回的是原本声明之return type 的subtype),你便是在函数用户身上强加了非必要的工作。这种情况下你不应该要求用户承担向 下转型的责任,应该尽量为他们提供准确的型别。

以上所说的情况,常会在「返回迭代器(iterator)或群集(collection)」的函数身上发生。此时你就应该观察人们拿这个迭代器干什么用,然后针对性地提供专用函数。

作法(Mechanics)

·找出「必须对函数调用结果进行向下转型」的地方。
Ø这种情况通常出现在「返回一个群集(collection)或迭代器(iterator)」 的函数中。

·将向下转型动作搬移到该函数中。
Ø针对返回群集(collection)的函数,使用Encapsulate Collection 。

范例:(Example)

下面的例子中,我以Reading 表示「书籍」。我还拥有一个名为lastReading() 的函数,它从一个用以「保存Reading 对象」的Vector 中返回其最后一个元素:

Object lastReading() {

  return readings.lastElement();

}

我应该将这个函数变成:

Reading lastReading() {

  return (Reading) readings.lastElement();

}

当我拥有一个群集时,上述那么做就很有意义。如果「保存Reading 对象」的群集被放在Site class 中,并且我看到了如下的代码(客户端):

Reading lastReading = (Reading) theSite.readings().lastElement()

我就可以不再把「向下转型」工作推给用户,并得以向用户隐藏群集:

Reading lastReading = theSite.lastReading();

class Site...

   Reading lastReading() {

       return (Reading) readings().lastElement();

   }

如果你修改函数,将其「返回型别」(return type )改为原返回型别的subclass,那就是改变了函数签名式(signature),但并不会破坏客户端代码,因为编译器知道 它总是可以将一个subclass 自动向上转型为superclass 。当然啦你必须确保这个subclass 不会破坏superclass 带来的任何契约(contract)。(译注:在OO设计中,继承关系代表is-a 关系,因此subclass is-a superclass ,因此正确设计之subclass 决不会破坏superclass 带来的任何契约。)

原创粉丝点击