HeadFirst设计模式之策略模式
最近看完了《HeadFirst设计模式》这本书,想趁着这个机会写点东西总结总结,因此准备开始从今天开始将各种设计模式捋一遍,记录下来为有需要的同学当做参考。
闲话少叙,今天首先来说一下策略模式。首先给出策略模式的定义:
**策略模式定义了算法蔟,分别封装起来,让他们之间可以相互替换,此模式让算法的变化独立于使用算法的客户。**
假设你想开发一个模拟鸭子的游戏,其中有各式各样的鸭子,鸭子拥有各种行为,因此你设计一个鸭子的超类,然后各种鸭子继承此超类。
public abstract class Duck {abstract public void quak(); //这些方法为鸭子的各种行为,包括游泳、呱呱叫等abstract public void swim();abstract public void display();
}class MallardDuck extends Duck{void display(){ //....实现 该鸭子为绿头鸭,展示起来头为绿色的}
}class RedHeadDuck extends Duck{void display(){ //....实现 该鸭子是红头的,展示起来为红色}
}
现在你的鸭子功能模拟器的功能增加了,现在需要鸭子会飞,因此你可能会想到在抽象基类加一个fly()方法。
public abstract class Duck {abstract public void quak(); //这些方法为鸭子的各种行为,包括游泳、呱呱叫等abstract public void swim();abstract public void display();abstract public void fly(); //新增飞翔行为
}
此时抽象基类继承的问题就暴露出来了,因为各个鸭子都继承了基类的抽象方法,假设现在有一只橡皮鸭子也继承了该基类,如下所示。
/***各个子类都要实现抽象基类中的抽象方法,表示为自己本身的实现。*/
class RubberDuck extends Duck{ void display(){};void swim(){};void quak(){};void fly(){// 可以对其进行空实现,什么也不做};
}
很明显橡皮鸭子是不会飞的,但是它仍然要实现你定义在抽象基类中的方法。当然你可以在fly()方法中对其进行空实现,什么也不做。
但是如果以后加入其他的鸭子,比如说诱饵鸭,这是一种假鸭子不回飞也不会叫,你就需要对其中的多个方法进行空实现。
class DecoyDuck extends Duck{ void display(){};void swim(){};void quak(){// 覆盖,但是空实现};void fly(){// 可以对其进行空实现,什么也不做// 覆盖,但是空实现};
}
但是如果我们以后要开发出越来越多种类的鸭子怎么办呢,这些新鸭子可能会有一些新的行为,如果我们每次向基类里添加一个新的行为,那我们都需要去所有这个基类的继承子类中去实现新的行为方法,而有的行为并不是所有鸭子共有的,这种每次新增行为都大量去更改已有代码显然是不能接受的。
那如果把飞行行为和叫声行为抽离出来成单独的接口是否可行呢?
public abstract class Duck {abstract public void swim();abstract public void display();
}public interface Quackable{void quack();
}public interface Flyable{void fly();
}
class RedHeadDuck extends Duck implements Quackable,Flyable{
void fly(){};
void quack(){};
void display(){};
void swim(){};
}
虽然这可以解决不再有会飞的鸭子这种现象,但是其中很多的方法比如说fly()方法,需要重复写很多代码,因为鸭子的飞行差不多都一样,如果我们有几十个实现子类,那么需要写几十次飞行方法,造成大量的重复代码。
我们的业务可能随时应对变化,因此要尽量将自己的代码能够应对变化性,在变化的时候能够尽量修改较少的代码。此时就对应到了设计模式的一个***设计原则,***我称为此原则为动静分离:**找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要辩护的代码混在一起。**也就是说将变化的代码抽离出来和不经常变化的代码分离开,这样就能在变化的时候尽量少的影响其他代码。
应用到鸭子类,我们知道duck类内的fly和quack方法会随着鸭子的不同而改变,因此将飞行和呱呱叫行为抽离出来,切每一组类将实现各自的动作。

将Quackable和Flyable抽离出来也体现出了针对接口编程,而非针对实现编程的原则。之前我们是由具体的鸭子子类来实现相应的行为,被实现绑的死死的,无法更改行为,现在我们可以依赖于行为接口的实现子类,将相应行为指定给鸭子子类,从而可以更改行为,而不在与实现绑死了。
比如说Flyable可以有两个相应的实现
interface FlyBehavior{
void fly();
}class FlyWithWings implements FlyBehavior{
void fly(){//实现鸭子的飞行动作。}
}
class FlyNoWay implements FlyBehavior{
void fly(){//什么都不做。}
}//QuackBehavior有三个实现
interface QuackBehavior{
void quack();
}class Quack implements QuackBehavior{
void quack(){//嘎嘎叫}
}class Squeak implements QuackBehavior{
void quack(){//吱吱叫}
}class MuteQuack implements QuackBehavior{
void quack(){//不叫}
}
通过这样设计,就可以让呱呱叫的动作 和飞行的动作被其他对象服用了,这些行为已经和鸭子类无关了
现在我们将鸭子基类中加入两个实例变量。
public abstract class Duck {FlyBehavior flyBehavior;QuackBehavior quackBehavior;abstract public void swim();abstract public void performQuack();abstract public void display();abstract public void performFly();
}现在在鸭子的实现类里我们想让鸭子拥有什么样的行为,我们就直接委托给鸭子子类
class MallardDuck extends Duck{public MallardDuck (){quackBehavior = new Quack();flyBehavior = new FlyWithWings();}//将该鸭子相对应的行为委托给该鸭子子类
public MallardDuck (QuackBehavior quackBehavior,FlyBehavior flyBehavior ){this.quackBehavior = quackBehavior ;this.flyBehavior = flyBehavior ;}
public void setFlyBehavior(FlyBehavior flyBehavior ){this.flyBehavior = flyBehavior ;
}
public void setQuackBehavior(QuackBehavior quackBehavior){this.quackBehavior = quackBehavior ;
}void performQuack(){ quackBehavior.quack(); 表现出委托的叫行为的叫声}void performFly(){ flyBehavior .fly(); 表现出委托的飞行行为的飞行方法}
}
上面的方法是不是就可以解决我们的问题了,再举个例子,比如说我们现在想生产一种飞起来利用火箭动力飞行的模型鸭子。
class ModelDuck extends Duck{public ModelDuck (){quackBehavior = new Quack();flyBehavior = new FlyNoWay();//一开始我们的模型鸭是不会飞的}//将该鸭子相对应的行为委托给该鸭子子类
public ModelDuck (QuackBehavior quackBehavior,FlyBehavior flyBehavior ){this.quackBehavior = quackBehavior ;this.flyBehavior = flyBehavior ;}
public void setFlyBehavior(FlyBehavior flyBehavior ){this.flyBehavior = flyBehavior ;
}
public void setQuackBehavior(QuackBehavior quackBehavior){this.quackBehavior = quackBehavior ;
}void performQuack(){ quackBehavior.quack(); 表现出委托的叫行为的叫声}void performFly(){ flyBehavior .fly(); 表现出委托的飞行行为的飞行方法}
}//定义一种利用火箭飞行行为
public class FlyRocketPowered implements FlyBehavior{public void fly(){System.out.println("I'm flying with a rocket");}
}现在让我们新建一直模型鸭
Duck modelDuck = new ModelDuck();
modelDuck.performFly();
modelDuck.setFlyBehavior(new FlyRocketPowered ());
modelDuck.performFly();执行结果:
模型鸭不会飞
I'm flying with a rocket"
以上的这些就是策略模式了,在本例中是将飞行行为和叫声行为抽离出来,来进行众多实现。在以组合的方式来实现多种行为的替换,这样就能在运行时更改鸭子的飞行和叫声行式,不再将鸭子子类和飞行叫声绑死,实现灵活替换,这些行为的实现子类就可以看成是算法蔟。今天的分享就到这里啦。
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
