每个初学者的理解不同,我加上自己的理解大体是这样的:前提是处理大规模时,假设池塘中有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 {@Overridevoid display() {System.out.println("红头的");}@Overridepublic 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 {@Overridepublic 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。