标签:
设想有一个游戏,游戏中有各种鸭子,它们可以飞,也可以呱呱叫。这样一个游戏该怎样设计呢?
一、使用继承:
鸭子虽然有不同的种类,但是也有一定的相同之处,所以我们可以从鸭子中提取一个父类Duck,让不同种类的鸭子类继承自父类,将所有鸭子共有的属性和行为放到父类中。
这样做看似没有错误,但实际上存在以下两个方面的错误:
1、子类的可用行为可能远大于我们的期待范围。把所有方法和属性都定义在Duck父类中,再由所有子类去继承这个父类,那么所有的子类就都具备父类的方法和属性。就拿这个游戏的例子来说,如果加入一个方法fly(),那么就算是橡皮鸭(RubberDuck)类也一定会具备fly()的行为。
2、这种方法并不能有效的解决代码重复问题。拿上一条中的fly()方法来说,既然不同的鸭子具有不同的飞行方式,则fly()方法的方法体一定是写在子类中;而不同种类的鸭子的飞行方式也可能是相同的,所以我们就可能不得不在多个鸭子中使用相同的fly()方法,而在另外一些鸭子中使用另一种fly()方法。
二、增加接口:
我们可以把Duck父类中的fly()方法提取出来定义成接口,称为Flyable,其中定义一个抽象方法fly(),让可以飞的Duck子类实现这个接口并实现fly()方法。
使用接口有效的解决了上面的第一个问题,即可以有效的控制各个子类的行为范围,不会出现“子类具有父类的所有方法”的情况了。但是,JAVA中的接口中不具备代码实现功能,所以这样做并没有解决代码重复的问题。
三、解决问题:
我们需要找出应用中可能需要变化的地方,把它们独立出来,不要和那些不需要变化的代码混在一起,即把会变化的部分取出并封装起来,一边以后可以轻易的改动或扩展此部分,而不影响不需要变化的部分。
在上面的两次尝试(使用继承、增加接口)中,我们都只是把共有的属性和行为抽出来封装成父类或接口,但在这里,我们要尽量两次封装,即在业务类上面还有两层封装类。对于一种行为,我们首先将这种行为抽取出来作为一个总的行为接口,然后再在这个接口下面定义多个不同的实现类,最后再在业务类中调用父类完成业务。简单的说,在上面两种尝试中,我们只是定义了两个互不相关的行为,而在这种方法中,我们定义的是一组有关联的行为。
拿我们这个例子中的fly()方法举例,飞,可能是用翅膀飞(FlyWithWings),也可能是坐火箭飞(FlyRocketPower)。因此,我们定义一个总的接口FlyBehavior,在这个接口中定义一个抽象方法fly()。再定义两个实现FlyBehavior接口的类FlyWithWings和FlyRocketPower,这两个类实现FlyBehavior接口,并实现接口中的fly()方法。另外,我们在Duck父类中定义一个FlyBehavior的接口变量,在生成鸭子实体的时候,给其FlyBehavior赋值,以此决定鸭子的飞行方式。我们还可以在Duck父类中定义一个设置飞行方式的方法setFlyBehavior(),来动态的改变鸭子的飞行方式。
以下贴出DEMO代码:
接口FlyBehavior中的代码:
1 public interface FlyBehavior { 2 void fly(); 3 }
FlyBehavior接口的实现类FlyWithWings(用翅膀飞)类中的代码:
1 public class FlyWithWings implements FlyBehavior { 2 public void fly() { 3 System.out.println("我用翅膀飞!"); 4 } 5 }
FlyBehavior接口的实现类FlyRocketPower(坐火箭飞)类中的代码:
1 public class FlyRocketPower implements FlyBehavior { 2 public void fly() { 3 System.out.println("我坐火箭飞!"); 4 } 5 }
FlyBehavior接口的实现类FlyNoWay(不会飞)类中的代码:
1 public class FlyNoWay implements FlyBehavior { 2 public void fly() { 3 System.out.println("我不会飞!"); 4 } 5 }
Duck父类中的代码:
1 public abstract class Duck { 2 private FlyBehavior flyBehavior; // 飞行行为接口对象 3 // 设置鸭子的飞行行为方式 4 public void setFlyBehavior(FlyBehavior flyBehavior) { 5 this.flyBehavior = flyBehavior; 6 } 7 // 让鸭子飞行 8 public void performFly() { 9 this.flyBehavior.fly(); 10 } 11 // 输出鸭子的样子 12 public abstract void display(); 13 }
Duck的实体类RedHeadDuck(红头鸭)类中的代码:
1 public class RedHeadDuck extends Duck { 2 public RedHeadDuck() { 3 this.display(); 4 super.setFlyBehavior(new FlyWithWings()); 5 } 6 7 public void display() { 8 System.out.println("我是红头鸭!"); 9 } 10 }
Duck的实体类RubberDuck(橡皮鸭)类中的代码:
1 public class RubberDuck extends Duck { 2 public RubberDuck() { 3 this.display(); 4 super.setFlyBehavior(new FlyRocketPower()); 5 } 6 7 public void display() { 8 System.out.println("我是橡皮鸭!"); 9 } 10 }
主函数Main中的代码:
1 public class Main { 2 public static void main(String args[]) { 3 // 第一只鸭子:红头鸭 4 Duck duck1 = new RedHeadDuck(); 5 duck1.performFly(); 6 // 第二只鸭子:橡皮鸭(由坐火箭飞改变为不会飞) 7 Duck duck2 = new RubberDuck(); 8 duck2.performFly(); 9 duck2.setFlyBehavior(new FlyNoWay()); 10 duck2.performFly(); 11 } 12 }
四、总结:
本例主要用了策略模式。所谓策略模式,即定义算法族(几组行为),每组行为封装一种行为的不同表现形式,让他们之间可以互相替换。这种模式让算法的变化独立于使用算法的客户。
标签:
原文地址:http://www.cnblogs.com/blog-wzy/p/5116793.html