我的隐式生活(My Implicit Life)

继续搞“对象”,玩OO.

首页 新随笔 联系 聚合 管理
  11 Posts :: 1 Stories :: 39 Comments :: 0 Trackbacks

创建和销毁对象

重点关注对象的创建和销毁:什么时候、如何创建对象,什么时候、什么条件下应该避免创建对象,如何保证对象在合适的方式下被销毁,如何在销毁对象之前操作一些必须的清理行为。

尝试用静态工厂方法代替构造器

如果一个 client 要实例化一个对象来使用,傻 b 都知道应该先调用类的构造器来 new 一个对象,之后再调用相应的方法。除了这个方式, Java Effective 还建议了另一种方法:用静态工厂方法来提供一个类的实例。以下的例子不反映两者的优劣,只是反映两者在代码实现上的不同,优劣之后再谈:

假设咱们要一个颜色为黑色、长度为 50cm 的锤子,自然就用构造器创建一个

Hammer myHammer =  new Hammer(Color.BLACK, 50);

而用静态工厂方法来实例化一个对象,如下

Hammer myHammer = Hammer.factory(Color.BLACK,50);

也可以用专门的一个工厂类来实例化

Hammer myHammer = Toolkit.factory(“Hammer”, Color.BLACK,50);  

单纯从上面的代码上看,真的只有傻 b 才会选择静态工厂的方法,完全就是多此一举,直接 new 又快又爽,搞这么麻烦做莫斯(武汉话“什么”的意思)?

别急,别急,你急个莫 b (武汉粗话:基本就是“你急个毛”的意思)?

下面就说说用静态工厂代替构造器的好处( advantage )和不好处( disadvantage )。

第一个好处,讲你都不信,行家们认为,构造器有一个不好的地方就是:这个方法的签名( signture )太固定了。

构造器的名字是固定的,生个 Hammer ,构造器的名字就是 Hammer (……),唯一能变化的地方就是参数,假设我的这个锤子有两个很变态的构造需要:

1 :第一个参数是颜色( Color 型),第二个参数是锤子头的重量( int 型)。

Hammer Color c, int kg {

//remainder omited

}

2 :第一个参数是颜色( Color 型),第二个参数是锤子的长度( int 型)。

Hammer Color c, int cm {

//remainder omited

}

感觉满足需要了,但是细心一看,完了,构造器的参数列表类型重复了,肯定编译通不过,这是面向对象构造器天生的缺陷——唯一的变化就是参数,参数都分辨不了,就真的分辨不了。

而另外就算参数能分辨的了,构造器一多,它的参数一多,您根本就不知道每个参数是用来干什么的,只能去查阅文档,在您已经眼花缭乱的时候再去查文档,一个一个的对,折磨人的活。

这个时候,您就可以考虑用静态工厂方法来实例化对象了。因为静态工厂方法有一个最简单的特点就是:他有可以变化的方法名(构造器的名字变不了)。用名字的不同来代表不同的构造需要,这么简单的普通的特点在这里就是它相对于构造器的 advantage

如上面的锤子的例子可以这样:

1 Hammer.produceByWeight (Color c, int kg){

//remainder omited

}

2 Hammer.produceByHeight (Color c, int cm){

//remainder omited

}

这是不是一目了然多了。嗯,我是这样认为的。

第二个好处,“静态工厂方法不需要每次都真的去实例化一个对象”——其实这也是另一些优化方法的前提。

构造器的每次 invoke 必定会产生一个新的对象,而静态工厂方法经过一定的控制,完全可以不用每次 invoke 都生成一个新的对象。

为什么不每次都生成一个对象的原因就不必说了,因为原因太明显。这个原因就是为什么要“共享”对象的原因。

下面讲讲通常使用的两种共享具体策略,也就是具体方法了:

1 :单例模式的需要,一旦需要某个对象有单例的需要,必定对于这类对象的构造只能用静态工厂方法了。

2 flyweight 模式和不变( immutable 模式的需要,这两个模式很多时候都说一起使用的,一旦一些对象我们认为是不变的,那自然就想拿来重用,也就说共享,而 flyweight 就是用来重用这些小粒度对象的。

Boolean.valueOf (boolean) 方法:

Boolean a = Boolean.valueOf (100);

Boolean b = Boolean.valueOf (100);

 a,  b两个引用都是指向同一个对象。

这些对象都是不变的,而 valueOf 的控制就是用的 flyweight 方法。

这种一个状态(如上面一个数字)对应的对象只有一个还有一个好处,就是可以直接通过比较“引用”来判断他们是否 equel (这里的 equel 是逻辑相等的意思),以前需要 a.equels(b) ,而一旦用“ flyweight 模式和不变( immutable 模式”后,避免了产生多余的相同对象,用 a==b 就可以达到 a.equels(b) 的目的了。这样当然优化了 performance   

第三个好处,其实就是工厂方法的核心好处——我把它称为“抽象类型构造器”。它可以为我们提供一个抽象类型的实例,同时必要的隐藏了抽象类型的具体结构。这是 new 怎么都达不到的。

这种模式的好处其实就是面向对象的最核心的好处,抽象和具体可以分离,一旦抽象定义好了,具体的东西可以慢慢的变化,慢慢的拓展——开闭原则。

Collections Framework API ,都是描述集合类型的接口,也就是对于客户端来看,只有 Collection 这个类要认识,而实际上,实现这个接口的 Collection 是多种多样的。如果要让用户都知道这些具体实现的 Collection ,就增加了复杂度。

这时,通过一个静态工厂方法,就可以隐藏各种 Collection 的具体实现,而让 Client 只使用返回的 Collection 对象就可以了。

这里还可以加上一些权限控制,如这些实现只要对于工厂来讲是可以访问的,不用是 public 的,而他们只要通过 public 的工厂就可以提供给用户。非常有利于代码的安全。

静态工厂方法的第一个缺点就是:使用静态工厂方法创建的类的构造器经常都是非公共或非 protected 的。 这样,以后这些类就没有办法被继承了。不过也有人说,不用继承就用 composition 呗。也是!呵呵。

静态工厂方法的第二个缺点是:在 jdk 文档里,这些静态工厂方法很难跟别的静态方法相区别。 而文档中,构造器是很容易看到的。

为了一定程度解决这个问题,我们可以用一些比较特别的名字来给这类静态工厂方法来命名。最常用的有:

valueOf —— 用来放回跟参数“相同值”的对象。

getInstance —— 返回一个对象的实例。单例模式中,就是返回单例对象。

总结:静态工厂方法和构造器都有各自的特点。最好在考虑用构造器之前能先考虑一下静态工厂方法,往往,后者更有用一点。如果权衡了以后也看不出那个好用一些,那就用构造器,毕竟简单本分多了。

posted on 2006-07-15 12:35 marco 阅读(613) 评论(0)  编辑  收藏 所属分类: -=Java API=--=Java Techs=-

只有注册用户登录后才能发表评论。


网站导航: