首先我们从一个关于鸭子的案例来讨论面向对象之继承、接口和抽象类的关系
案例一:
这个是一个关于鸭子的游戏,实现各种形状和类别的鸭子都能够游泳和叫
案例分析:
依据对象的特性,开发人员Jim通过建立一个鸭子超类,然后在超类中实现swim、quake方法,然后定义一个抽象方法display(),用来处理不同鸭子的显示,子类通过继承超类从而拥有了鸭子的游泳和叫的行为,并通过实现抽象方法来显示鸭子的特性
案例的UML图:
案例的实现:
Duck.java
package com.duckPattern; public abstract class Duck { public Duck() { // TODO Auto-generated constructor stub } public void swim() { System.out.println("I Can Swimming"); } public void quack() { System.out.println("I Can Quacking"); } /** * define a abstract method display different duck */ abstract public void display(); }
GreenHeadDuck.java
package com.duckPattern; public class GreenHeadDuck extends Duck { public GreenHeadDuck() { // TODO Auto-generated constructor stub } public void display() { System.out.println("I Am a GreenHead Duck"); }
RedHeadDuck.java
package com.duckPattern; /** * @author Administrator * */ public class RedHeadDuck extends Duck { public RedHeadDuck() { // TODO Auto-generated constructor stub } public void display() { System.out.println("I Am a Red Head Duck"); } }
main.java
package com.duckPattern; public class main { /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub GreenHeadDuck gDuck = new GreenHeadDuck(); gDuck.display(); gDuck.swim(); gDuck.quack(); RedHeadDuck rDuck = new RedHeadDuck(); rDuck.display(); rDuck.swim(); rDuck.quack(); } }
体会:
通过继承的方式,子类拥有了父类的公共行为,减低了开发了冗余度
案例一的延伸:
需求人员:
Jim,客户有新需求了,要在游戏中添加鸭子的飞行为。
Jim :
小Case啊,我只要在超类中添加一个fly的方法就可以搞定问题了
测试人员登场了:<测试人员自己写了测试用例>
Jim,搞什么飞机了, 整个屏幕都是在飞的鸭子,那些哑巴鸭子也能叫,旱鸭子也能飞
设计模式登场:
继承的缺陷:通过继承,子类会被强加拥有父类的所有行为。
Jim :
那我可以在自己中添加空的fly方法,覆盖父类的方法,这样子类就不拥有父类的行为了啊
设计模式:
那当超类扩展有100个行为了,某个子类只具有10个行为,难道你要写90个覆盖方法?Jim你累不累啊。。。。。。。
Jim:
。。。。。。。。。。。。
经过一天的冥思苦想以后,Jim提出了通过接口的方式来实现鸭子的行为
UML图:
UML图分析:
我们定义了一个超类Duck类,然后再Duck类中实现了swim公共方法,以及定义抽象方法display(),然后把可变的飞行为和叫行为定义为两个接口。当定义我们的子类时,则继承我们的超类Duck,并依据类得特征来决定需要实现那些接口。
Code展示:
package com.duckPattern.one; /** * @author zhangweia * */ public abstract class Duck { public Duck() { // TODO Auto-generated constructor stub } public void swim() { System.out.println("I Can Swimming"); } public abstract void display() ; }
package com.duckPattern.one; public interface Flyable { public void fly(); }
package com.duckPattern.one; public interface Quackable { public void quack(); }
/** * */ package com.duckPattern.one; /** * @author Administrator * */ public class GreenHeadDuck extends Duck implements Flyable, Quackable { /** * */ public GreenHeadDuck() { // TODO Auto-generated constructor stub } @Override public void fly() { // TODO Auto-generated method stub System.out.println("I Can flying"); } @Override public void quack() { // TODO Auto-generated method stub System.out.println("I Can quacking"); } @Override public void display() { // TODO Auto-generated method stub System.out.println("I am a green head Duck"); } }
/** * */ package com.duckPattern.one; /** * @author zhangweia * */ public class RedHeadDuck extends Duck implements Flyable { /** * */ public RedHeadDuck() { // TODO Auto-generated constructor stub } @Override public void display() { // TODO Auto-generated method stub System.out.println("I am a red head Duck"); } @Override public void fly() { // TODO Auto-generated method stub System.out.println("I can flying"); } }
package com.duckPattern.one; public class main { /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub GreenHeadDuck gDuck = new GreenHeadDuck(); gDuck.display(); gDuck.fly(); gDuck.quack(); RedHeadDuck redHeadDuck = new RedHeadDuck(); redHeadDuck.display(); redHeadDuck.fly(); } }
体会:
通过定义接口,我们可以把可变化的部分抽取出来封装成为接口,然后子类对接口编程。
问题:
1. 由于每个接口都在子类实现,增加代码的复杂度
2. 如果飞这个行为发生了改变,则所有子类的行为都需要改变.比如说:开始飞的行为是直线飞,后来领导需要把他改为无规则飞
天使设计模式进场了! --CHANGE
A. Identify the aspects of your application that vary and separate them from what stays the same
识别你应用程序变化的部分,然后把他们从不变的部分分离出来。此案例中我们可变的部分是飞和叫的行为
B. Program to an interface,not an implementation
程序应该针对接口编程,而不是针对实现编程。针对接口编程意思是针对超类编程
C. Favor composition over inheritance
组合更有利于继承
使用设计模式来重新构造鸭子类
UML图
UML图说明
1. 把变化的部分飞行为和叫行为抽象为接口
2. 所有飞和叫的类实现飞和叫的接口
3. 超类定义飞和叫的接口,并实现飞和叫的方法
Code实现
-----Duck.java
/* Duck类具有飞、叫、游泳的方法以及显示的抽象方法,把变化的飞行为和叫行为通过接口类来处理*/
package com.dPattern; public abstract class Duck { //采用组合的方式,添加飞和叫的行为接口 protected com.dPattern.Flyable flyBeharvir; //定义飞行为接口 protected com.dPattern.Quackable quackBeharvior; //定义叫行为接口 public Duck() { // TODO Auto-generated constructor stub } //实现叫行为 public void performQuack() {
quackBeharvior.quack(); } //实现飞行为 public void performFly() { flyBeharvir.fly(); } public void swim() { System.out.println("I can Swimming"); } public abstract void display() ; }
------Flyable.java
package com.dPattern; public interface Flyable { public void fly(); }
package com.dPattern; public class FlyNoWing implements Flyable { public FlyNoWing() { // TODO Auto-generated constructor stub } @Override public void fly() { // TODO Auto-generated method stub System.out.println("I Cannot fly "); } }
package com.dPattern; public class FlyWithWing implements Flyable { public FlyWithWing() { // TODO Auto-generated constructor stub } @Override public void fly() { // TODO Auto-generated method stub System.out.println("I Can Fly"); } }
------叫行为接口和实现
package com.dPattern; public interface Quackable { public void quack(); }
package com.dPattern; public class QuackHavaSound implements Quackable { @Override public void quack() { // TODO Auto-generated method stub System.out.println("I Can QuackHavaSound"); } }
package com.dPattern; public class QuackNoSound implements Quackable { public QuackNoSound() { } @Override public void quack() { System.out.println("I Can QuackNoSound"); } }
-----具体的鸭子
public class RedHeadDuck extends Duck { public RedHeadDuck() { flyBeharvir = new FlyNoWing(); quackBeharvior = new QuackHavaSound(); } @Override public void display() { // TODO Auto-generated method stub System.out.println("I am a RedHeadDuck"); } }
public class GreenHeadDuck extends Duck { public GreenHeadDuck() { flyBeharvir = new FlyWithWing(); quackBeharvior = new QuackNoSound(); } @Override public void display() { System.out.println("I am a GreenHeadDuck"); } }
----主方法
package com.dPattern; public class main { public static void main(String[] args) { // Duck gDuck = new GreenHeadDuck(); 针对接口和超类编程,只是鸭子其中的一种鸭子
//GreenHeadDuck gDuck = new GreenHeadDuck(); //针对实现编程:指定了具体的那种鸭子
Duck gDuck = new GreenHeadDuck(); gDuck.display(); gDuck.performFly(); gDuck.performQuack(); System.out.println("************"); Duck rDuck = new RedHeadDuck(); rDuck.display(); rDuck.performFly(); rDuck.performQuack(); } }
到现在我们的鸭子已经能够快乐的适应各种变化了,可是如果我要想动态的修改鸭子的行为该怎么处理了,比如要动态的设定
鸭子的分行状态从不能飞行到能飞行状态了?
要实现该功能,我们只需要在超级类鸭子中添加设置鸭子的行为就可以了!
修改后鸭子的超级类代码为:
package com.dPattern; public abstract class Duck { protected com.dPattern.Flyable flyBeharvir; protected com.dPattern.Quackable quackBeharvior; public Duck() { // TODO Auto-generated constructor stub } public void performQuack() { quackBeharvior.quack(); } public void performFly() { flyBeharvir.fly(); } public void setFlyBeharvior(Flyable fb){ flyBeharvir = fb; } public void setQuackBeharvior(Quackable qb){ quackBeharvior = qb; } public void swim() { System.out.println("I can Swimming"); } public abstract void display() ; }
主方法调用中:
public class main { /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub Duck gDuck = new GreenHeadDuck(); gDuck.display(); gDuck.performFly(); gDuck.performQuack(); gDuck.setFlyBeharvior(new FlyNoWing()); gDuck.performFly(); System.out.println("************"); Duck rDuck = new RedHeadDuck(); rDuck.display(); rDuck.performFly(); rDuck.performQuack(); } }
到这里鸭子模型我们已经基本讲完了,从中我们了解到了设计模式的3条规则
1. 识别出程序中变化的部分,把他们抽取为接口
2. 针对接口编程,而不是针对实现编程
3. 组合大于继承
看帖--感谢回帖交流您的意见和想法!