面向实现编程的弊端
举一个简单的动物界的例子,我们需要写一段代码,包含以下要素:鸭子类,鸭子的若干子类,实现一些方法。
惯用的编程思路:
1、写一个鸭子类Duck,写几个方法,比如鸭子飞行的方法fly(),鸭子叫的方法quack()。
2、写鸭子的子类,比如绿头鸭MallardDuck,继承Duck类。
3、在子类中重写以上两个方法。
这种编程思路的弊端:
1、代码冗杂:
在代码量少且代码稳定不变更的情况下似乎不会有什么问题,在实际运行程序时,也是完全不会出错的。但是,假设我们把鸭子的数量增加,增加到一万种鸭子类,我们是不是要重写一万次方法呢?显然这是很费事费力的。另外,假设我们要调用一万次fly()方法,是不是要写一万次调用的程序呢?即:对象名.fly() 。这也会导致代码的冗杂。
2、变更性弱:
假设当你下定决心去写那一万种鸭子类,并且快要写完的时候,老板和你说,这个fly()方法不应该这样写,需要改一下,你是不是就要崩溃了,代码几乎是死的,没法变更!
简单分析:这种类型的代码属于硬代码,不仅写的时候代码量繁杂,而且写完后,几乎是没法动的,自然不能适应现在软件需要不断更新版本的要求,需要我们转变编程思路。
面向接口编程的引入
我们一个个地解决这些问题。
1、解决代码冗杂的问题:减少代码量的最好方式就是相同代码的复用,假设一万种鸭子中,有一部分鸭子的fly()方法(行为)是一致的,那么我们可以试着去减少它重复的书写。fly()既需要满足被多处复用的要求,又需要满足可以被特殊地实现的要求,于是我们可以把这种飞行的行为单独地拿出来,使它脱离鸭子类,写一个接口flyBehavior,让fly()方法成为它的成员方法,fly()方法在这里是一种抽象方法,实例化必须改写内容。然后,我们可以写若干子类继承flyBehavior接口,实现行为方法fly(),然后在需要用的时候实例化调用就可以了。似乎这还没有解决一万次调用方法的代码冗杂问题,一万个对象,一万个对象名.fly()肯定是不现实的,我们希望的duck.fly()。解决的方法是在创建对象的时候让子类对象向上转型为父类,通过父类的引用去调用被子类重写后的方法,那么这个问题也解决了。
2、解决变更性弱的问题:这个问题其实已经被上述方法解决,我们修改fly()方法的内容,只要在相应的行为类中修改就可以了,操作起来很方便。其他行为如quack()行为也可以作为接口。除此之外,我们还可以做一些别的事情,比如实现鸭子飞行行为的动态更改,只要在鸭子类中写一个set方法,修改flyBehavior的引用,使得flyBehavior调用不同的fly()方法,实现在程序运行中多态修改飞行参数以及飞行姿态。
小结:以上就体现了面向接口编程的特点:
1、反复被使用的行为被抽象为接口,利用接口的多实现使得行为多实现。
2、行为的反复重写变为了对已经实现的行为的多次调用。
3、父类对象调用子类方法体现了自动转型的思想。
4、实现了多态。
面向接口编程实践
1、Duck类:
public abstract class Duck {
FlyBehavior flyBehavior;
QuackBehavior quackBehavior;
/**
* 鸭子的外观
*/
public abstract void display();
/**
* 鸭子的身份
*/
public void isDuck()
{
System.out.println("我是鸭子");
}