一、定义
策略模式比较容易理解且很常见,主要思想就是将同一类型的算法封装为一个算法组,使得他们之间可以相互替换,此模式让算法变化独立于使用算法的客户。可能这样直接说比较抽象,下面的实现通过一个经典的鸭子类的例子来实现策略模式。
二、实现
面临的问题
首先有一个鸭子基类Duck
,不同类型的鸭子都集成于这个基类,基类中有一些基本的方法:叫、游泳、飞,子类都会继承这些方法。
java
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{
@Override
public void display() {
System.out.println("嘎嘎鸭的外观:黄头");
}
}
public class MallardDuck extends Duck {
@Override
public void display() {
System.out.println("绿头鸭的外观:绿头");
}
}
这样没什么问题,但是如果新加了一个橡皮鸭类型呢,橡皮鸭既不会飞也不会叫,那应该怎么办?
这样也比较容易想到,重写父类的方法就好了嘛。
java
public class RubberDuck extends Duck{
@Override
public void fly() {
System.out.println("不会飞");
}
@Override
public void quack() {
System.out.println("不会叫");
}
@Override
public 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();
}
}
//输出:
不会飞
不会叫
这是一个橡皮鸭
来了一个不会飞不会叫的橡皮鸭,我们可以重写父类的方法改进,但是如果后面又来了一种同样不会飞不会叫的木头鸭子呢?可能也是一样,重写方法,来多少个就重写多少个,但是这样是不是有悖于我们的设计原则?
- 首先,会出现大量的重复代码,都重写父类的方法改为不会飞不会叫;
- 然后这些行为都是重复的写死在类中,属于是直接面向实现编程而不是面向抽象编程;
- 最后,如果我们想修改某一类鸭子的行为,只能通过修改代码来实现,这是最不能接受的。
改进
既然飞和叫是每个鸭子都会有的行为,只不过不同类型的鸭子实现方法不同,且这些行为也不像鸭子的外观那样几乎每种鸭子都是独一无二的,比如飞的行为,只有两种,会飞和不会飞。
那我们就可以把这两种行为抽象为接口,具体的鸭子类可以自主选择这类行为下的实现。
首先,定义好行为的算法组,同时为了方便,我们直接把所有的行为都提前创建好实例放到工厂中。
飞行的行为:
java
public interface FlyBehavior {
void fly();
}
public class FlyWithNoWay implements FlyBehavior{
@Override
public void fly() {
System.out.println("不能飞行");
}
}
public class FlyWithWings implements FlyBehavior{
@Override
public 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;
}
}
叫的行为:
java
public interface QuackBehavior {
void quack();
}
public class QuackWithGa implements QuackBehavior{
@Override
public void quack() {
System.out.println("嘎嘎叫");
}
}
public class QuackWithGua implements QuackBehavior{
@Override
public void quack() {
System.out.println("Gua Gua 叫");
}
}
public class QuackWithNoWay implements QuackBehavior{
@Override
public 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;
}
}
然后修改鸭子的基类,把飞和叫的行为抽象为接口,交由子类从对应的算法组中选择
java
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()));
}
@Override
public 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()));
}
@Override
public void display() {
System.out.println("这是一只橡皮鸭");
}
}
下面就可以测试了
java
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();
}
}
//测试结果:
======================
创造一个橡皮鸭,不会飞,不会叫
这是一只橡皮鸭
所有鸭子都会游泳
不能飞行
不会叫
======================
创造一个嘎嘎鸭,会飞,嘎嘎叫
这是一个嘎鸭
所有鸭子都会游泳
使用翅膀飞行
嘎嘎叫
修改嘎嘎鸭,让他不会飞
不能飞行
至此,我们看到通过策略模式,实现很灵活的设置鸭子的飞和叫的行为,且支持改变这些行为而不用修改代码。