需求:游戏中有各种各样的鸭子,一边游泳戏水,一边呱呱叫。
解决方案:设计了一个鸭子超类,并让各个鸭子继承此类。
Duck.java
public abstract class Duck {
public abstract void display();
public void quack(){
System.out.println("呱呱叫!");
}
public void swim(){
System.out.println("我会游泳");
}
}
MallardDuck.java
public class MallardDuck extends Duck{
@Override
public void display() {
System.out.println("我是绿头鸭!");
}
}
ReadHeadDuck.java
public class ReadHeadDuck extends Duck{
@Override
public void display() {
System.out.println("我是红头鸭!");
}
}
需求变更:游戏需要有创新的地方,鸭子会飞。
解决方案:在Duck超类中加入fly()方法。
问题:因为所有的鸭子子类继承了Duck超类,橡皮鸭子竟然也会飞,不符合现实。
进一步的解决方案:在橡皮鸭子子类中覆盖父类的quack()方法和fly()方法。
Duck.java
public abstract class Duck {
public abstract void display();
public void quack(){
System.out.println("呱呱叫!");
}
public void swim(){
System.out.println("我会游泳");
}
public void fly(){
System.out.println("我会飞!");
}
}
RubberDuck.java
public class RubberDuck extends Duck{
@Override
public void display() {
System.out.println("我是橡皮鸭,我会吱吱叫,不会飞!");
}
public void quack(){
System.out.println("吱吱叫!");
}
public void fly(){
System.out.println();
}
}
问题:现在又有一种鸭子为诱饵鸭,是木头假鸭,即不会叫也不会飞。
此时该如何解决了,如果再去覆盖父类中的方法,会发现这样会写很多无用的、重复的代码。
新的解决思路:使用接口,把fly()从超类中取出来,放进一个Flyable接口中,只有会飞的鸭子才实现此接口。
同样的方式,也可以设计一个Quackable接口,只有会叫的鸭子才实现此接口。
不过又有新的问题:使用接口,会造成代码不能复用,每个实现此接口的子类,都必须实现相应接口的方法。
再进一步思考:
把问题归零,唯一不变得就是变化。
把Duck类中的fly()和quack()方法取出来,建立一组新类代表每个行为。
那么如何设计那组能实现飞行和呱呱叫的行为的类呢?
我们希望一切都能有弹性,我们能够“指定”行为到鸭子的实例。
例如:我们要产生一个新的绿头鸭实例,并产生特定“类型”的飞行行为给它。让鸭子的行为可以动态地改变。
换句话说,我们应该在鸭子类中包含设定行为的方法,这样就可以在“运行时”动态地“改变”绿头鸭的飞行行为。
FlyBehavior.java
public interface FlyBehavior {
public abstract void fly();
}
FlyNoWay.java
public class FlyNoWay implements FlyBehavior{
@Override
public void fly() {
System.out.println("我不会飞!");
}
}
FlyWithWings.java
public class FlyWithWings implements FlyBehavior{
@Override
public void fly() {
System.out.println("我会飞!");
}
}
QuackBehavior.java
public interface QuackBehavior {
public abstract void quack();
}
Quack.java
public class Quack implements QuackBehavior{
@Override
public void quack() {
System.out.println("呱呱叫");
}
}
Squeak.java
public class Squeak implements QuackBehavior{
@Override
public void quack() {
System.out.println("吱吱叫!");
}
}
MuteQuack.java
public class MuteQuack implements QuackBehavior{
@Override
public void quack() {
}
}
整合鸭子的行为:关键在于,鸭子现在会将飞行和呱呱叫的动作“委托”别人处理,而不是使用定义在
Duck类内的呱呱叫和飞行方法
Duck.java
public abstract class Duck {
FlyBehavior flyBehavior;
QuackBehavior quackBehavior;
public abstract void display();
public void swim(){
System.out.println("我会游泳");
}
public void performFly(){
flyBehavior.fly();
}
//鸭子对象不亲自处理呱呱叫行为,而是委托给quackBehavior引用的对象
public void performQuack(){
quackBehavior.quack();
}
}
MallardDuck.java
public class MallardDuck extends Duck{
public MallardDuck() {
//绿头鸭使用Quack类处理呱呱叫,所以当performQuack()被调用时,
//叫的职责被委托给Quack对象,而我们得到了真正的呱呱叫。
quackBehavior = new Quack();
flyBehavior = new FlyWithWings();
}
@Override
public void display() {
System.out.println("我是绿头鸭!");
}
}
问题:我们的原则是不对具体实现编程,但是在构造器里仍然制造了一个具体的Quack实现类的实例!
思考:如何实现一个其行为可以在运行时改变的鸭子?
1.Duck超类添加set方法
Duck.java
public abstract class Duck {
FlyBehavior flyBehavior;
QuackBehavior quackBehavior;
public Duck(){
}
public abstract void display();
public void swim(){
System.out.println("我会游泳");
}
public void performFly(){
flyBehavior.fly();
}
//鸭子对象不亲自处理呱呱叫行为,而是委托给quackBehavior引用的对象
public void performQuack(){
quackBehavior.quack();
}
//添加set方法,以后可以“随时”调用这两个方法改变鸭子的行为
public void setFlyBehavior(FlyBehavior flyBehavior) {
this.flyBehavior = flyBehavior;
}
public void setQuackBehavior(QuackBehavior quackBehavior) {
this.quackBehavior = quackBehavior;
}
}
2.添加一个火箭动力飞行的委托执行类
FlyRocketPowered.java
public class FlyRocketPowered implements FlyBehavior{
@Override
public void fly() {
System.out.println("火箭动力飞行!");
}
}
3.创建模型鸭
ModelDuck.java
public class ModelDuck extends Duck{
public ModelDuck() {
flyBehavior = new FlyNoWay();//一开始,我们的模型鸭是不会飞的。
quackBehavior = new Quack();
}
@Override
public void display() {
System.out.println("我是模型鸭!");
}
}
4.测试
Test.java
public class Test {
public static void main(String[] args) {
// MallardDuck mduck = new MallardDuck();
// mduck.display();
// mduck.performFly();
// mduck.performQuack();
// RubberDuck rd = new RubberDuck();
// rd.display();
// rd.quack();
// rd.fly();
//改变测试类,加上模型鸭,并使模型鸭具有火箭动力
Duck model = new ModelDuck();
model.performFly();
model.setFlyBehavior(new FlyRocketPowered());
model.performFly();
}
}
策略模式总结:定义了算法簇,分别封装起来,让它们之间可以互相替换,此模式
让算法的变化独立于使用算法的客户。