7.3 代理
第三种关系称为代理,Java中并没有提供对它的直接支持。这是继承与组合之间的中庸之道。因为我们将一个成员对象置于所要构造的类中,就像组合一样,但与此同时我们在新类中暴露了该成员对象的所有方法,就像继承。例如,太空船需要一个控制模块:
publicclassSpaceShipControls { voidup(intvelocity) {} voiddown(intvelocity) {} voidleft(intvelocity) {} voidright(intvelocity) {} voidforward(intvelocity) {} voidback(intvelocity) {} voidturboBoost() {} }
构造太空船的一种方式是使用继承:
publicclassSpaceShipextendsSpaceShipControls { privateStringname; publicSpaceShip(Stringname) { this.name=name; } publicStringtoString() { returnname; } publicstaticvoidmain(String[] args) { SpaceShipprotector=newSpaceShip("NSEA Protector"); protector.forward(100); } }
然后,SpaceShip并非真正的SpaceShipControls类型,即便你可以告诉SpaceShip向前运动(forward())。更准确地讲,SpaceShip包含SpaceShipControls,与此同时,SpaceShipControls的所有方法在SpaceShip中都暴露了出来。代理解决了此难题:
publicclassSpaceShipDelegation { privateStringname; privateSpaceShipControlscontrols=newSpaceShipControls(); publicSpaceShipDelegation(Stringname) { this.name=name; } // Delegated methods: publicvoidback(intvelocity) { controls.back(velocity); } publicvoiddown(intvelocity) { controls.down(velocity); } publicvoidforward(intvelocity) { controls.forward(velocity); } publicvoidleft(intvelocity) { controls.left(velocity); } publicvoidright(intvelocity) { controls.right(velocity); } publicvoidturboBoost() { controls.turboBoost(); } publicvoidup(intvelocity) { controls.up(velocity); } publicstaticvoidmain(String[] args) { SpaceShipDelegationprotector=newSpaceShipDelegation("NSEA Protector"); protector.forward(100); } }
上面的形式看似是组合的一种,其实其要表达的意思远比组合要有深远意义。我们使用代理时可以拥有更多的控制力,新增一些我们自己的个性化逻辑。
7.4 结合使用组合和继承
同时使用组合和继承是很常见的事情。下例就展示了同时使用这两种技术,并配以必要的构造器初始化,来创建更加复杂的类:
importstaticnet.mindview.util.Print.*; classPlate { Plate(inti) { print("Plate constructor"); } } classDinnerPlateextendsPlate { DinnerPlate(inti) { super(i); print("DinnerPlate constructor"); } } classUtensil { Utensil(inti) { print("Utensil constructor"); } } classSpoonextendsUtensil { Spoon(inti) { super(i); print("Spoon constructor"); } } classForkextendsUtensil { Fork(inti) { super(i); print("Fork constructor"); } } classKnifeextendsUtensil { Knife(inti) { super(i); print("Knife constructor"); } } // A cultural way of doing something: classCustom { Custom(inti) { print("Custom constructor"); } } publicclassPlaceSettingextendsCustom { privateSpoonsp; privateForkfrk; privateKnifekn; privateDinnerPlatepl; publicPlaceSetting(inti) { super(i+1); sp=newSpoon(i+2); frk=newFork(i+3); kn=newKnife(i+4); pl=newDinnerPlate(i+5); print("PlaceSetting constructor"); } publicstaticvoidmain(String[] args) { PlaceSettingx=newPlaceSetting(9); } }
/* Output:
Custom constructor
Utensil constructor
Spoon constructor
Utensil constructor
Fork constructor
Utensil constructor
Knife constructor
Plate constructor
DinnerPlate constructor
PlaceSetting constructor
*///:~
7.5 在组合与继承之间选择
组合和继承都允许在新的类中放置子对象,组合是显示地这样做,而继承则是隐式地做。
组合技术通常用于想在新类中使用现有类的功能而非它的接口这种形式。
继承技术是使用一个现有类,并开发一个它的特殊版本。通常,这意味着你在使用一个通用类,并为了某种特殊需要而将其特殊化。
7.6 protected关键字
现在我们已介绍完了继承,关键字protected最终具有了意义。在理想世界中,仅靠关键字private就已经足够了。但是实际项目中,经常会想要将某些事物尽可能对这个世界隐藏起来,但是允许导出类的成员访问它们。
关键字protected就是起这个作用的。它指明“就类用户而言,这是private的,但是对于任何继承于此类的导出来或者其他任何位于同一个包内的类来说,它确实可以访问的。”
尽管可以创建protected域,但是最好的方式还是将域保持为private;你应该一直保留更改底层实现的权利。下面就是一个通过protected方法来控制类的继承者的访问权限:
//: reusing/Orc.java // The protected keyword. importstaticnet.mindview.util.Print.*; classVillain { privateStringname; protectedvoidset(Stringnm) { name=nm; } publicVillain(Stringname) { this.name=name; } publicStringtoString() { return"I’m a Villain and my name is "+name; } } publicclassOrcextendsVillain { privateintorcNumber; publicOrc(Stringname, intorcNumber) { super(name); this.orcNumber=orcNumber; } publicvoidchange(Stringname, intorcNumber) { set(name); // Available because it’s protected this.orcNumber=orcNumber; } publicStringtoString() { return"Orc "+orcNumber+": "+super.toString(); } publicstaticvoidmain(String[] args) { Orcorc=newOrc("Limburger", 12); print(orc); orc.change("Bob", 19); print(orc); } }
/* Output:
Orc 12: I’m a Villain and my name is Limburger
Orc 19: I’m a Villain and my name is Bob
*///:~