针对接口编程
设计原则:针对接口编程,而不是针对实现编程。
“针对接口编程”真正的意思是“针对超类型(Supertype)编程”,这个接口可能指的是abstract class,可能指的是interface。
“针对接口编程’,关键就在多态。利用多态,程序可以针对超类型编程,执行时会根据
实际状况执行到真正的行为,不会被绑死在超类型的行为
上。
“针对超类型编程”这句话,可以更明确地说成“变量的声明类型应该是超类型,通常是一个抽象类或者是一个接口,如此,只要是具体实现此超类型的类所产生的对
象,都可以指定给这个变量。这也意味着,声明类时不用
理会以后执行时的真正对象类型!"
看看下面这个简单的多态例子:假设有一个抽象类Animal,有两个具体的实现(Dog与Cat)继承Animal。
做法如下:
设计鸭子的行为
这样的做法迥异于以往,以前的做法是:行为来自Duck超类的具体实现,或是继承某个接口并由子类自行实现而来。这两种做法都是依赖于“实现”,被实现绑得死死的,没办法更
改行为(除非写更多代码)。
在我们的新设计中,鸭子的子类将使用接口(FlyBehavior与QuackBebavior)所表示的行为,所以实际的“实现”不会被绑死在鸭子的子类中。(换句话说,特定的具体行为编写在实现了
FlyBehavior与QuakcBehavior的类中)。
实现鸭子的行为
这样的设计,可以让飞行和呱呱叫的动作被其他对象复用,因为这些行为已经与鸭子无关了。
也可以新增一些行为,不会影响到已有的行为类。
这样的设计,有了继承的“复用”好处,却没有继承所带来的包袱。
整合鸭子的行为
关键在于,鸭子现在会将飞行和呱呱叫的动作“委托”(de1egate)别人处理,而不是直接使用定义在Duck类(或子类)内的呱呱叫和飞行方法。
做法是这样的:
①、首先,在Duck类中加入两个实例变量,分别为“flyBehavior,与“quackBehavior",声明为接口类型(而不是具体类实现类型),每个鸭子对象都会动态地设置这些变量以在运行时引用正确的行为类型(例如:FlywithWings、
Squeak等)。
②、实现perfomrQuack();
publicabstract class Duck{
//为行为接口类型声明两个引用变量,所有鸭子子类都继承他们
FlyBehaviorflyBehavior;
QuackBehaviorquackBehavior;
publicDuck(){
}
//展示行为
publicvoid performFly(){
//委托给行为类
flyBehavior.fly();
}
publicvoid performQuack(){
//委托给行为类
quackBehavior.quack();
}
}
③、设定flyBehavior、quackBehavior的实例变量。MallardDuck类:
publicclassMallardDuckextendsDuck{
//构造器中出现针对实现编程:正在制造一个具体的Quack实现类的实例。
//稍后会用Setter方法解决这个问题
publicMallardDuck(){
quackBehavior=new Quack();
flyBehavior =newFlyWithWings();
}
publicvoiddisplay(){
System.out.println("I'm a real Mallard duck");
}
}