一、定义
策略模式比较容易理解且很常见,主要思想就是将同一类型的算法封装为一个算法组,使得他们之间可以相互替换,此模式让算法变化独立于使用算法的客户。可能这样直接说比较抽象,下面的实现通过一个经典的鸭子类的例子来实现策略模式。
二、实现
面临的问题
首先有一个鸭子基类Duck
,不同类型的鸭子都集成于这个基类,基类中有一些基本的方法:叫、游泳、飞,子类都会继承这些方法。
public abstract class Duck {public void swim(){System.out.println("游泳");}public void fly(){System.out.println("飞");}public void quack(){System.out.println("嘎嘎叫");}//每种鸭子外观不同,所以是抽象的public abstract void display();
}public class GaDuck extends Duck{@Overridepublic void display() {System.out.println("嘎嘎鸭的外观:黄头");}
}public class MallardDuck extends Duck {@Overridepublic void display() {System.out.println("绿头鸭的外观:绿头");}
}
这样没什么问题,但是如果新加了一个橡皮鸭类型呢,橡皮鸭既不会飞也不会叫,那应该怎么办?
这样也比较容易想到,重写父类的方法就好了嘛。
public class RubberDuck extends Duck{@Overridepublic void fly() {System.out.println("不会飞");}@Overridepublic void quack() {System.out.println("不会叫");}@Overridepublic void display() {System.out.println("这是一个橡皮鸭");}
}//测试:
public class DuckTest {public static void main(String[] args) {Duck rubberDuck = new RubberDuck();testDuck(rubberDuck);}public static void testDuck(Duck duck){duck.fly();duck.quack();duck.display();}
}//输出:
不会飞
不会叫
这是一个橡皮鸭
来了一个不会飞不会叫的橡皮鸭,我们可以重写父类的方法改进,但是如果后面又来了一种同样不会飞不会叫的木头鸭子呢?可能也是一样,重写方法,来多少个就重写多少个,但是这样是不是有悖于我们的设计原则?
- 首先,会出现大量的重复代码,都重写父类的方法改为不会飞不会叫;
- 然后这些行为都是重复的写死在类中,属于是直接面向实现编程而不是面向抽象编程;
- 最后,如果我们想修改某一类鸭子的行为,只能通过修改代码来实现,这是最不能接受的。
改进
既然飞和叫是每个鸭子都会有的行为,只不过不同类型的鸭子实现方法不同,且这些行为也不像鸭子的外观那样几乎每种鸭子都是独一无二的,比如飞的行为,只有两种,会飞和不会飞。
那我们就可以把这两种行为抽象为接口,具体的鸭子类可以自主选择这类行为下的实现。
首先,定义好行为的算法组,同时为了方便,我们直接把所有的行为都提前创建好实例放到工厂中。
飞行的行为:
public interface FlyBehavior {void fly();
}
public class FlyWithNoWay implements FlyBehavior{@Overridepublic void fly() {System.out.println("不能飞行");}
}
public class FlyWithWings implements FlyBehavior{@Overridepublic void fly() {System.out.println("使用翅膀飞行");}
}
public enum FlyEnum {NO_WAY("noWay","无法飞"),WING("wing","使用翅膀飞");//键名private final String key;//描述private final String desc;//...
}
public class FlyFactory {private static final Map<String,FlyBehavior> FLY_MAP = new HashMap<>();static{FLY_MAP.put(FlyEnum.NO_WAY.getKey(), new FlyWithNoWay());FLY_MAP.put(FlyEnum.WING.getKey(), new FlyWithWings());}public static FlyBehavior get(String flyKey){FlyBehavior flyBehavior = FLY_MAP.get(flyKey);if(flyBehavior == null){throw new RuntimeException("指定的飞行方式不存在");}return flyBehavior;}
}
叫的行为:
public interface QuackBehavior {void quack();
}
public class QuackWithGa implements QuackBehavior{@Overridepublic void quack() {System.out.println("嘎嘎叫");}
}
public class QuackWithGua implements QuackBehavior{@Overridepublic void quack() {System.out.println("Gua Gua 叫");}
}
public class QuackWithNoWay implements QuackBehavior{@Overridepublic void quack() {System.out.println("不会叫");}
}
public enum QuackEnum {GUAGUA("guagua", "呱呱叫"),GAGA("gaga", "嘎嘎叫"),NO_WAY("noWay", "不会叫");//键值private final String key;//描述private final String desc;//...
}
public class QuackFactory {private static final Map<String, QuackBehavior> QUACK_MAP = new HashMap<>();static{QUACK_MAP.put(QuackEnum.GUAGUA.getKey(), new QuackWithGua());QUACK_MAP.put(QuackEnum.GAGA.getKey(), new QuackWithGa());QUACK_MAP.put(QuackEnum.NO_WAY.getKey(), new QuackWithNoWay());}public static QuackBehavior get(String quackKey){QuackBehavior quackBehavior = QUACK_MAP.get(quackKey);if(quackBehavior == null){throw new RuntimeException("指定的叫声方式不存在");}return quackBehavior;}
}
然后修改鸭子的基类,把飞和叫的行为抽象为接口,交由子类从对应的算法组中选择
public abstract class Duck {//飞和叫会因为鸭子的不同而不同,所以使用策略模式,将他们抽离出来private FlyBehavior flyBehavior;private QuackBehavior quackBehavior;//抽象方法,说明public abstract void display();public void swim(){System.out.println("所有鸭子都会游泳");}public void performFly(){flyBehavior.fly();}public void performQuack(){quackBehavior.quack();}public void setFlyBehavior(FlyBehavior flyBehavior) {this.flyBehavior = flyBehavior;}public void setQuackBehavior(QuackBehavior quackBehavior) {this.quackBehavior = quackBehavior;}
}
//子类,嘎嘎鸭
public class GaDuck extends Duck {public GaDuck() {setFlyBehavior(FlyFactory.get(FlyEnum.WING.getKey()));setQuackBehavior(QuackFactory.get(QuackEnum.GAGA.getKey()));}@Overridepublic void display() {System.out.println("这是一个嘎鸭");}
}
//子类,橡皮鸭
public class RubberDuck extends Duck {public RubberDuck() {setFlyBehavior(FlyFactory.get(FlyEnum.NO_WAY.getKey()));setQuackBehavior(QuackFactory.get(QuackEnum.NO_WAY.getKey()));}@Overridepublic void display() {System.out.println("这是一只橡皮鸭");}
}
下面就可以测试了
public class DuckTest {public static void main(String[] args) {System.out.println("======================");testRubberDuck();System.out.println("======================");testGaDuck();}public static void testRubberDuck(){System.out.println("创造一个橡皮鸭,不会飞,不会叫");Duck rubberDuck = new RubberDuck();rubberDuck.display();rubberDuck.swim();rubberDuck.performFly();rubberDuck.performQuack();}public static void testGaDuck(){System.out.println("创造一个嘎嘎鸭,会飞,嘎嘎叫");Duck gagaDuck = new GaDuck();gagaDuck.display();gagaDuck.swim();gagaDuck.performFly();gagaDuck.performQuack();System.out.println("修改嘎嘎鸭,让他不会飞");gagaDuck.setFlyBehavior(FlyFactory.get(FlyEnum.NO_WAY.getKey()));gagaDuck.performFly();}
}//测试结果:
======================
创造一个橡皮鸭,不会飞,不会叫
这是一只橡皮鸭
所有鸭子都会游泳
不能飞行
不会叫
======================
创造一个嘎嘎鸭,会飞,嘎嘎叫
这是一个嘎鸭
所有鸭子都会游泳
使用翅膀飞行
嘎嘎叫
修改嘎嘎鸭,让他不会飞
不能飞行
至此,我们看到通过策略模式,实现很灵活的设置鸭子的飞和叫的行为,且支持改变这些行为而不用修改代码。