一个办法就是使用显式的包装器类,就像上面显示的那样。BuilderRobot类在其构造函数中获取一个Robot,并拦截workOn方法,确保在任何项目中使用的工具都没有破坏性。另外,由于BuilderRobot这一包装器实现了Robot接口,所以凡是能够使用一个Robot的任何地方,都能使用一个BuilderRobot实例。
对于这种包装器风格的BuilderRobot来说,一旦你想修改或扩展Robot接口,它的缺点就会暴露无遗。为Robot接口添加一个方法,就得为 BuilderRobot类添加一个包装器方法。为Robot添加10个方法,就得为BuilderRobot添加10个方法。如果 BuilderRobot、CrusherRobot、SpeedyRobot和SlowRobot都是Robot包装器类,就必须分别为它们添加10个方法。这显然是效率极差的一种方案。
public class BuilderRobot extends MyRobot {
public void workOn(Project p, Tool t) {
if (t.isDestructive()) {
t = Tool.RATCHET;
}
super.workOn(p, t);
}
}
上述代码是对 BuilderRobot进行编程的另一种方式。注意BuilderRobot变成了MyRobot的一个子类。这样可解决在第2段代码的包装器方案中出现的问题。也就是说,修改Robot接口不必修改BuilderRobot。但这又产生了一个新问题:只有MyRobot对象才能是 BuilderRobot。而在此之前,实现了Robot接口的任何对象都可以成为一个BuilderRobot。现在,由Java施加的“线性类出身限制”(linear class parentage restrictions)禁止我们将任意Robot(ArbitraryRobot)变成一个BuilderRobot。
动态代理也有限制
动态代理则综合了以上两种方案的优点。使用动态代理,你创建的包装器类不要求为所有方法都使用显式的包装器,创建的子类也不要求具有严格的出身,两者方法可任选一种你认为最好的。但是,动态代理仍然有一个限制。当你使用动态代理时,要包装/扩展的对象必须实现一个接口,该接口定义了准备在包装器中使用的所有方法。这一限制的宗旨是鼓励良好的设计,而不是为你带来更多的麻烦。根据经验,每个类都至少应该实现一个接口(nonconstant接口)。良好的接口用法不仅使动态代理成为可能,还有利于程序的模块化。
