浅谈设计模式之策略模式
今天,小菜将更新一篇博文关于设计模式中的一个经典模式,也是一个比较简单的模式,策略模式。我们从一个例子开始,逐步深入的探讨该模式。
在现实生活中,我们都知道鸭子这种动物。鸭子会飞、会叫、会游泳。不同种类的鸭子他的外观表现也不相同,有的有一个红色的头顶,白白的身体,有的是黑色的头顶,五彩的身子。显示生活中不仅有活生生的鸭子,还有玩偶鸭子,机械鸭子,木头鸭子等等。他们当中,有的会飞、会叫、会游泳。有的只会这其中的一种或两种。假如,我们想编写一个程序,让各种鸭子一边游泳,一边呱呱叫。我先写出其中的一种方式,如上图所示。
让各种类型的鸭子继承超类Duck,然后呢,各类鸭子就自动的获得了鸣叫的quack()方法和游泳swim()方法。这个程序实现十分简单,在这里呢,就不做详细的实现了。现在出现一个新的问题,假如,我想增加一个fly()方法,这该怎么办呢?很多人的想法就是在父类Duck中直接添加fly()方法,然后所有继承该父类的子类鸭子自然获得了fly()方法。
这么做看起来非常完美,只是在父类做了一个小小的改动,就在每个子类中完美的实现了fly()方法。不过,在OtherDuck中,有的鸭子并不会飞,他们不会飞反而赋予了他们飞行的能力,这么做似乎不妥。虽然,我只是在父类Duck中做了简单的局部修改,但是所产生的影响并非是局部的。
有的人会想,既然做了继承,我就可以重写。每个初级程序员都知道我们可以重写父类的方法,用自己的实现覆盖父类中的方法。其他不会飞的鸭子,我们让他在fly()方法中什么也不做。这个想法貌似很完美的样子。也能实现我们的要求。但是,在OtherDuck中不仅有不会飞的鸭子,还有不会叫,不会游泳的鸭子。这该怎么办呢?难道说也一个一个的去覆盖么?如果一个一个的去覆盖父类的方法是不是显得有点笨拙呢。没出现一种新型的鸭子就要进行无休止的修改。
到这里,有的基本功非常扎实的人可能想到了接口,创建Flyable和Quack接口。让继承Duck的鸭子根据需要实现自己需要的接口,会叫就实现Flyable接口,会叫就实现Quack接口。问题立刻解决了,似乎很完美的样子。但是,细心地人就会发现,我们所用的代码是不可以复用的。似乎,现在没有任何好的办法来解决这个问题。在这个时候,策略模式Strategy该闪亮登场了。
我们从新分析鸭子的问题,根据需求,似乎不同的鸭子只有fly和quack的区分。我们可不可以把这两个始终可能变化的部分抽象出来呢?首先,我们先引出一个设计原则:找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起。针对我们目前的问题而言,我们就要把fly和quack分别封装起来,让其他的部分不受到影响。上面这几句简单的话似乎是每个设计模式背后的精神所在哦(这句话是从大神的书上抄袭的)。就是说,让系统中的某一处改变不会影响到其他任何一处的改变。似乎这个就是设计模式的真谛。
好滴,接下来小菜就要来设计一下这个问题了。首先要把fly和quack分别封装起来成为接口,分别对应FlyBehavior和QuackBehavior。对于Flyable接口,可以分出几种具体的实现,会飞,FlyWithWings(用翅膀飞),FlyWithOthers(用其他方式飞),FlyNoWay(肯定不会飞).对于Quack接口,可以分出几种具体的实现,Quack(呱呱的叫),Squack(吱吱的叫),MuteQuack(肯定不会叫)。在这里有人会问,这么做有什么好处。这里的好处是非常明显的,对于fly和quack而言,如果出现了新的鸭子种类,只要实现新的种类的fly和quack实现就可以了。
现在整合鸭子的行为,具体做法不多说废话,上代码。
public abstract class Duck {
FlyBehavior flyBehavior;
QuackBehavior quackBehavior;
public Duck(){
}
public void swim() {
System.out.println("I can swim");
}
public abstract void dispaly();
public void performFly() {
flyBehavior.fly();
}
public void performQuack() {
quackBehavior.quack();
}
}
public interface FlyBehavior {
public void fly();
}
public class FlyWithWings implements FlyBehavior{
public void fly() {
System.out.println("我是用翅膀飞行的");
}
}
public class FlyWithOther implements FlyBehavior{
public void fly() {
System.out.println("用其他方式飞行");
}
}
public class FlyNoWay implements FlyBehavior{
public void fly() {
System.out.println("死活不会飞");
}
}
public interface QuackBehavior {
public void quack();
}
public class Quack implements QuackBehavior{
public void quack() {
System.out.println("呱呱的叫!");
}
}
public class Squack implements QuackBehavior{
public void quack() {
System.out.println("吱吱的叫!");
}
}
public class MuteQuack implements QuackBehavior{
public void quack() {
System.out.println("死活不会叫!");
}
}
public class ReadHeadDuck extends Duck{
public ReadHeadDuck(){
flyBehavior = new FlyWithWings();
quackBehavior = new Quack();
}
@Override
public void dispaly() {
System.out.println("我是红额头的鸭子");
}
}
public class RedHeadDuckTest {
public static void main(String[] args) {
Duck redHeadDuck = new ReadHeadDuck();
redHeadDuck.performFly();
redHeadDuck.performQuack();
redHeadDuck.dispaly();
}
}
哈哈哈哈,是不是非常简单,看到这里,我相信任何有一点点Java基础的已经学会了策略模式,我们上面用到了面向对象的又一个特性,多态,我们会发现,其实设计模式中用到的很多东西都是我们最基础的东西,比如说,继承,封装,多态。我们上面的代码运用了构造方法,对于构造方法,大家都知道,构造方法在当前类被实例化的时候就执行了。这样似乎不是很完美的样子,似乎太死板了点。我们接下来对代码做一下小小的修改,我们不再父类中使用构造方法,可以在测试类中随时改变鸭子的类型,从而出现不同的表现,废话不说上代码。其他类没有任何变化.
public abstract class Duck {
FlyBehavior flyBehavior;
QuackBehavior quackBehavior;
public void swim() {
System.out.println("I can swim");
}
public abstract void dispaly();
public void performFly() {
flyBehavior.fly();
}
public void performQuack() {
quackBehavior.quack();
}
public void setFlyBehavior(FlyBehavior fb) {
flyBehavior = fb;
}
public void setQuackBehavior(QuackBehavior qb) {
quackBehavior = qb;
}
}
public class RedHeadDuckTest {
/**
* @param args
*/
public static void main(String[] args) {
Duck redHeadDuck = new ReadHeadDuck();
redHeadDuck.performFly();
redHeadDuck.performQuack();
redHeadDuck.setFlyBehavior(new FlyNoWay());
redHeadDuck.performFly();
redHeadDuck.dispaly();
}
}
看到了这里,设计模式之策略模式已经搞定,而且,我相信你也已经学会了策略模式。接下来总结一下我们用到的设计原则:
1. 针对接口编程,而不是真对实现编程;
2. 找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起;
3. 多用组合,少用耦合。
学完了这个策略模式,我的体会是:设计模式就是使系统局部的改变而不会影响到系统的其他部分。知道了OO基础:抽象,封装,继承,多态并不足以使我设计出良好的OO系统。良好的OO设计必须具备可维护,可复用,可扩充三个特性!我们学完了某种设计模式,但是,可能很多时候都用不到,不过不用着急,把你学的东西记在心里,然后自己经常反复的练习,努力在自己的程序中使用你学到的设计模式。学习设计模式是为了生搬硬套设计模式,而是为了写出可维护,可扩展,可复用的代码。小菜现炒现卖,不喜勿喷!其中很多个人观点,跪求斧正!