该模式应用场景:某公司要开发一套模拟鸭子的游戏,鸭子会一边游泳,一边呱呱叫。使用面向对象的方式设计鸭子模型。
第一种实现方式:
设计一个Duck抽象超类。
Duck |
quack(){}//实现鸭子的叫声 swim(){}//鸭子都会游泳 display();//鸭子外观抽象类 |
如果需要绿色鸭子,就继承Duck超类,实现里面的display()方法,如果需要红色鸭子类,也继承Duck超类,实现display()方法,想要什么颜色的鸭子就可以直接继承父类去实现,是不是很完美呢??
突然有一天,需要一种会飞的鸭子,如果是你,该怎么实现呢,一个粗心的程序员是这样实现的:在Duck类中有增加了一个fly()方法,让鸭子会飞。这样继承Duck类的鸭子就都能飞了,但实际的需求并不是让所有的鸭子都会飞,这样设计当然就不合理了,也许现在你会说,那没有关系,子类直接覆盖父类的fly();方法,如果鸭子不需要飞,就空覆盖fly方法。
这样做有什么缺点呢?
代码在多个类中重复,很难知道所有鸭子的全部行为,如果又有一种新行为的鸭子,难道又要在Duck类中增加属性,然后再去覆盖所有的子类?
也许这个时候你会想到使用接口了,你把fly()从Duck类中抽出,作为一个接口,让鸭子们自己实现fly方法,但是又并不是所有的鸭子都需要会飞的属性。这样就会导致代码无法重用。
怎么样设计才是做合理的设计呢?请看下面的设计方式(代码实现):
IFlyBehavior:所有飞行类都实现该接口的fly方法
package com.wgy.cl.Interface; public interface IFlyBehavior { public void fly();//飞行 }
IQuackBehavior:不同鸭子的叫声都实现该接口的quack方法
package com.wgy.cl.Interface; public interface IQuackBehavior { public void quack();//叫声 }
下面在定义鸭子所有的飞行方式。
FlyWithWingsImpl使用翅膀飞行类
package com.wgy.cl.Impl; import com.wgy.cl.Interface.IFlyBehavior; public class FlyWithWingsImpl implements IFlyBehavior { @Override public void fly() { System.out.println("I am flying with Wings"); } }
FlyNoWayImpl不能飞行的鸭子类
package com.wgy.cl.Impl; import com.wgy.cl.Interface.IFlyBehavior; public class FlyNoWayImpl implements IFlyBehavior { @Override public void fly() { System.out.println("I can't fly ~~"); } }
NoQuackImpl:不会叫的鸭子属性
package com.wgy.cl.Impl; import com.wgy.cl.Interface.IQuackBehavior; public class NoQuackImpl implements IQuackBehavior { @Override public void quack() { System.out.println("I am 不会叫~~"); } }
QuackImpl:呱呱呱叫的鸭子
package com.wgy.cl.Impl; import com.wgy.cl.Interface.IQuackBehavior; public class QuackImpl implements IQuackBehavior { @Override public void quack() { System.out.println("I am 呱呱叫~~"); } }
SqueakImpl:吱吱叫的鸭子
鸭子的超类Duck实现:所有鸭子都继承该类
package com.wgy.cl; import com.wgy.cl.Interface.IFlyBehavior; import com.wgy.cl.Interface.IQuackBehavior; /** * 让不同类型的鸭子都继承该抽象类 * @author SAMSUNG SDS * */ public abstract class Duck { public IFlyBehavior iFlyBehavior; public IQuackBehavior iQuackBehavior; public void perFormFly(){//鸭子的飞行方式 iFlyBehavior.fly();// } public void perFormQuack(){//鸭子的叫声 iQuackBehavior.quack(); } public void swim(){//所有鸭子都会游泳 System.out.println("All Duck Can swim~~"); } public abstract void display();//每个鸭子的外形不一样 //动态设置鸭子的飞行模式,可以改变飞行方式,nb不nb public void setFlyBehavior(IFlyBehavior fl){ iFlyBehavior = fl; } //可以改变鸭子的叫声 public void setQuackBehavior(IQuackBehavior qb){ iQuackBehavior = qb; } }
下来就来造一只绿头鸭MallardDuck
package com.wgy.cl; import com.wgy.cl.Impl.FlyWithWingsImpl; import com.wgy.cl.Impl.QuackImpl; /** * 绿头鸭 * @author SAMSUNG SDS */ public class MallardDuck extends Duck { //绿头鸭继承抽象类Duck public MallardDuck(){ iFlyBehavior = new FlyWithWingsImpl();//翅膀飞行 iQuackBehavior = new QuackImpl();//呱呱叫 } public void display() { System.out.println("I am MallardDuck ~~ "); } }
测试绿头鸭
package com.wgy.cl; import com.wgy.cl.Impl.NoQuackImpl; public class TestDuck { /** * 鸭子的测试类 * @param args */ public static void main(String[] args) { Duck mallard = new MallardDuck();//实例化一个绿鸭子 mallard.display();//鸭子的外形 mallard.perFormFly();//鸭子的飞行方式 mallard.perFormQuack();//鸭子的叫声 } }
运行结果:
I am MallardDuck ~~
I am flying with Wings
I am 呱呱叫~~
如果让绿头鸭的叫声动态改变(不修改MallardDuck类),该怎么实现呢?其实很简单。
package com.wgy.cl; import com.wgy.cl.Impl.NoQuackImpl; public class TestDuck { /** * 鸭子的测试类 * @param args */ public static void main(String[] args) { Duck mallard = new MallardDuck();//实例化一个绿鸭子 mallard.display();//鸭子的外形 mallard.perFormFly();//鸭子的飞行方式 mallard.perFormQuack();//鸭子的叫声 //动态改变鸭子的叫声 mallard.setQuackBehavior(new NoQuackImpl()); mallard.perFormQuack(); } }
像创建MallardDuck鸭一样,我们可以创建具有不同属性的鸭子,只需要修改鸭子构造器里面的属性就可以了。
public MallardDuck(){ iFlyBehavior = new FlyWithWingsImpl();//翅膀飞行(根据鸭子的属性不同,可以选择不同的行为属性) iQuackBehavior = new QuackImpl();//呱呱叫 }
这就是策略模式的应用场景,设计原则就是通过接口编程,而不是针对于实现编程,多用组合,少用继承的原则!