每个初学者的理解不同,我加上自己的理解大体是这样的:前提是处理大规模时,假设池塘中有10000头小鸭子,有红头鸭,野鸭子,木头鸭子等等,会叫会游泳,肯定是定义一个父类Duck,拥有这样的基本行为属性;现在要求鸭子中400头,可以飞,该怎么做?
有句话叫:当你处理大规模时,一定要写抽象层次编程。
问题:400只小鸭子可以飞,怎么做?
1.最直接想到的就是 父类中定义fly()方法,全部的子鸭子去重写,要飞的就子类自己实现飞这个行为,不要飞的子类可以空实现。目的实现了,存在的问题:①全部子类都要改代码 ②实现的fly方法 具体实现无论是不是空,都要在栈中保留方法的引用 而维护方法的指针需要成本尤其是大量时。 基于这两点肯定不合适。
总结:通过继承实现在父类中声明的行为,主要有以下缺点:
the followings are the disadvantages of using inheritance to provide Duck behavior
①Code is duplicated across subclasses (代码在多个子类中重复)
② Runtime behavior changes are difficult (运行时行为不易改变)
③Changes can unintentionally affect other ducks (改变会牵一发而动全身,造成部分子类型不想要的改变)
2.定义一个FlyAble接口,然后定义400只需要飞的小鸭子实现这个接口,然后实现自己的方法,有没有发现问题的所在,需要每一个子类都去实现这个接口,然后写一样的fly代码;代码如下:
public interface Flyable {
void fly();
}
public class RedHeadDuck extends Duck implements Flyable {
@Override
void display() {
System.out.println("红头的");
}
@Override
public void fly() {
System.out.println("要飞的");
}
}
图示:
这就是“java的接口不具有实现代码,实现接口无法达到代码的复用”,也就意味着“当你需要修改某个行为,你需要往下追踪,并修改每一个定义了此行为的类。” 这两句话,可以细细体会,就是接口不具有代码的实现,无法达到代码的复用。
实现Flyable接口的方式到底有什么坏处?
假设400个鸭子要能飞,那要400个鸭子实现这个接口,每个都重写400次fly方法,然后定义飞的行为,要是不同的鸭子有了高高飞,擦地飞,旋转着飞,是不是,每个鸭子要写不同的飞方法,比如
a =》高高飞
b =》擦地飞
c =》旋转着飞
现在d e f g 也要旋转飞 那肯定要每一个都写旋转飞的方法 这属于“接口不具有实现代码,所以实现接口无法达到代码的复用”。
如果有专门的高高飞行为类,擦地飞行为类,旋转着飞行为类,都去实现flybehavior行为接口,重写fly表达行为,那每次用的时候,就不用写这么多重复的代码了。 而且当你想修改方法的时候只需要改一处就好了。
可以试想,将变化的内容定义形成接口可以实现变化内容和不变内容的剥离。 其接口的实现类可以实现变化内容的重用,即理解为
这些实现类并非Duck.java的子类型,而是专门的一组实现类,称之为 行为类。由行为类而不是Duck.java的子类型实现接口。这样才能保证变化的行为独立于不变的内容。
代码如下:
public interface FlyBehavior {
void fly();
}
//具体的行为类 用翅膀飞
public class FlyWithWings implements FlyBehavior {
@Override
public void fly() {
System.out.println("==带翅膀飞");
}
}
/**
* 父类
*/
public abstract class Duck {
FlyBehavior flyBehavior;
String color;
int age;
public void performFly() {
flyBehavior.fly();
}
void performSwim() {
System.out.println("==所有的鸭子都会游泳呢");
}
}
/**
* 红头鸭
*/
public class RedheadDuck extends Duck {
public RedheadDuck() {
flyBehavior = new FlyWithWings();
}
}
/**
* 测试类
*/
public class TestDuck {
public static void main(String[] args) {
System.out.println("=============红头鸭子=========");
showRedHeadDuck();
}
private static void showRedHeadDuck() {
Duck duck = new RedheadDuck();
duck.performFly();
}
}
这样就实现了鸭子类和fly这一行为的分离,两着没有了关系。
以前的做法是:行为是继承自Duck超类的具体实现而来,或者实现某个接口并由子类自行实现而来。 这两种方法都是依赖于实现。我们被实现绑的死死的,没有办法更改行为(除非写更多的代码)。
这种方法和继承的不同之处在于,小鸭子的行为不是继承而来,而是和适当的行为对象结合。这是一个很重要的技巧,其实使用了策略模式中的第三个设计原则,多用组合 少用继承。
当你处理大规模时 一定要写抽象层次编程。
整个过程就是从is-a转成has-a。