1.策略模式

1.什么是策略模式?

策略模式定义了算法族,分别封装起来,让他们之间可以相互替换,此模式让算法的变化独立于使用算法的客户。

2.结合案例理解此模式

注意:完整项目代码在文章末尾
借用设计模式head first书本中的例子,我们根据一次简单的项目设计来体现策略模式的优势。

2.1 首次需求

2.1.1需求描述

我们需要做一款模拟鸭子的游戏,游戏中会出现各种鸭子,一边游泳戏水,一般呱呱叫。请提供此游戏的后端支持,提供各种的鸭子api

2.1.2 程序设计

从满足需求的方向上去设计,我们可以得到如下设计:
在这里插入图片描述

设计一个鸭子抽象类,用来定义所有鸭子的共有属性行为

/**
 * 鸭子抽象类
 */
public abstract class Duck {
    /**
    *外观抽象方法,
    因为不同鸭子的外观可能不同,需要具体的鸭子实体类去实现
    */
    public abstract void disPlay();
    /**
    *呱呱叫行为
    */
    public void Quack(){
       System.out.println("Quack ! Quack!!");
    }
    /**
    *游泳行为
    */
    public void swim(){
        System.out.println("游啊游~~~");
    }
}

然后所有的鸭子实体类去继承它来进行编程,这样就达到了代码复用的效果:
目前游戏中只需要两类鸭子的API: MallardDuck , RedheadDuck

public class MallardDuck extends Duck {
    @Override
    public void disPlay() {
        System.out.println("我是绿头鸭!!");
    }
}
public class RedheadDuck extends Duck {
    @Override
    public void disPlay() {
        System.out.println("我是红头鸭!!");
    }
}

好了,这时我们就可以提供API了。
第一次需求完美解决了哈哈!! 又可以摸鱼了呀!!可是好景不长,没过几天,新的需求又来了。。。。。。

2.2 第二次需求

2.2.1 需求描述

产品决定给这个游戏里加上会飞的鸭子来增强游戏的可玩性。
这时,我们该怎么做呢?

2.2.2 程序设计

这时,你可能会说,这还不简单? 直接在抽象类Duck上加一个fly实现方法就好了呀,它的子类都拥有这个行为了呀,搞定!!
在这里插入图片描述

但是,产品说的是给游戏里加上会飞的鸭子,并不是说给所有鸭子加上飞行技能!! 于是产品让你返工。
在这里插入图片描述

这时,你又可能会说,这简单啊,直接修改那些本不具备飞行能力的鸭子,让他覆盖掉fly方法就好了,覆盖的方法啥都不做不就行了。
在这里插入图片描述

/**
 * 鸭子抽象类
 */
public abstract class Duck {
    /**
    *外观抽象方法,
    因为不同鸭子的外观可能不同,需要具体的鸭子实体类去实现
    */
    public abstract void disPlay();
    /**
    *呱呱叫行为
    */
    public void Quack(){
       System.out.println("Quack ! Quack!!");
    }
    /**
    *游泳行为
    */
    public void swim(){
        System.out.println("游啊游~~~");
    }
    public void fly(){
        System.out.println("飞呀飞~~~");
    }
}
public class MallardDuck extends Duck {
    @Override
    public void disPlay() {
        System.out.println("我是绿头鸭!!");
    }
}
public class RedheadDuck extends Duck {
    @Override
    public void disPlay() {
        System.out.println("我是红头鸭!!");
    }
    /**
    *红头鸭不会飞,覆盖掉父类的fly方法
    */
    @Override
    public void fly(){}
}

嗯嗯,这次产品那边没啥好说的了。
这次的需求勉强应付过去了吧,可是,在你修改的过程中,你会发现,你要如果要更改以前某些会飞的鸭子为不会飞,这样你需要一个一个鸭子的去覆盖,工作量也就上来了。

2.3 第三次需求

2.3.1 需求描述

现在需要加入一个诱饵鸭子,这个鸭子及不会飞,也不会叫。

2.3.2 程序设计

现在需要这样做:
在这里插入图片描述

public class DecoyDuck extends Duck {
    @Override
    public void disPlay() {
        System.out.println("我是诱饵鸭子!!");
    }
    /**
    *诱饵鸭不会飞,覆盖掉父类的fly方法
    */
    @Override
    public void fly(){}
    /**
    *诱饵鸭不会叫,覆盖掉父类的方法
    */
    @Override 
    public void quack(){}
}

这样做虽然可以,但是我们很快就意识到不对劲,按照这样的频率进行更新,每当有新的鸭子子类出现,我们就要被迫检查并可能需要覆盖父类的方法,这简直是工作量爆炸好吧。
意识到这点后,我们决定优化设计,让其能轻松应对频繁的更新而让我们的工作量很少。

2.4 设计优化

于是,技术主管让员工们想出一个较好的设计方案来进行优化,并且悬赏,解决这个问题将奖励10万元。

员工A的优化方案:
员工A: 我们可以把Duck类的fly方法提取出来,放进一个叫Flyable的接口里,这么一来,只有会飞的鸭子去实现这个接口,同样的方式,我们也可以设计一个Quackable接口。
在这里插入图片描述

主管思考一会儿后回答:这真是一个超笨的主意好吧,你没发现如果这样做重复的代码会变多吗? 如果你认为覆盖几个方法是差劲的,那么对于你这种做法,对于50个鸭子类都要修改以下飞行的行为,你又怎么说?

员工A: 好吧,我承认这确实不是个好主意。

员工B的方案:
员工B经过思考后,总结出了使用继承并不能很好的解决问题,因为鸭子的行为在子类里是不断改变的,并且让所有的子类都有这些行为是不恰当的。 后面员工A的方法虽然解决了一些问题,但是java接口不具有实现代码的特性,所以继承接口无法达到代码的复用。 这意味着: 无论何时你需要修改某个行为,你必须得往下追踪并在每一个定义此行为的类中修改它,一不小心就会出现新的错误。
这时,他记得一个设计原则:找出应用中可能需要变化之处,把他们独立出来,不要和那些不需要变化的代码混在一起。
这个原则可能会有帮助,那么从何开始呢?
首先分析出变化和非变化的部分: 我们知道Duck类的fly() 和quack() 会随着鸭子的不同而改变。所以它是变化的部分,我们需要将这两个行为从Duck类中分离出来,我们可以建立一组新类来代表每个行为。
这时,员工B又想到一个原则:针对接口编程,而不是针对实现编程。 所以他决定把这组新类用接口描述,然后又具体的实体类去实现它。

public interface FlyBehavior {
    void fly();
}
public interface QuackBehavior {
    void quack();
}

这样,具体的fly和quack的实现实体的动作实体类中就可以了:

public class FlyWithWings implements FlyBehavior {
    @Override
    public void fly() {
        System.out.println("我飞起来了!!");
    }
}
public class Quack implements QuackBehavior {
    @Override
    public void quack() {
        System.out.println("呱呱呱!");
    }
}

好啦,现在把这些可变的部分从Duck中分离出来了,现在Duck是这样的:

/**
 * 鸭子抽象类
 */
public abstract class Duck {
    /**
    *外观抽象方法,
    因为不同鸭子的外观可能不同,需要具体的鸭子实体类去实现
    */
    public abstract void disPlay();
    /**
    *游泳行为
    */
    public void swim(){
        System.out.println("游啊游~~~");
    }
}

经过上面的提取,现在设计的UML是下面的:
在这里插入图片描述

员工B卡住了,他想,虽然这些动作抽离出来了,但是要怎样用在具体的鸭子类里呢?
这时他想到了一个原则:多用组合,少用继承
于是他有了下面的做法:
在这里插入图片描述

public class MallardDuck extends Duck {
    FlyBehavior fly;
    QuackBehavior quack;
    @Override
    public void disPlay() {
        System.out.println("我是绿头鸭!!");
    }
    public void fly(){
        fly.fly();    
    }
    public void quack(){
        quack.quack();    
    }
    
    public void setFlyBehavior(FlyBehavior fly){
        this.fly = fly;    
    }
    public void setQuackBehavior(QuackBehavior qb){
        this.quack = qb;
    }
}
public class RedheadDuck extends Duck {
    FlyBehavior fly;
    QuackBehavior quack;
    @Override
    public void disPlay() {
        System.out.println("我是红头鸭!!");
    }
     public void fly(){
        fly.fly();    
    }
    public void quack(){
        quack.quack();    
    }
     public void setFlyBehavior(FlyBehavior fly){
        this.fly = fly;    
    }
    public void setQuackBehavior(QuackBehavior qb){
        this.quack = qb;
    }
}

员工B仔细一看,不对啊,还是又一些重复的代码,能不能不要彻底丢弃继承呢? 适当的时候还是用一下,于是他进行了优化,得到下面的做法:
将组合的动作在父类做了,子类直接使用即可。

/**
 * 鸭子抽象类,组合了飞行接口和呱呱接口,
 * 实现了面对抽象编程
 */
public abstract class Duck {
    //接口,实现可变
    FlyBehavior flyBehavior;
    //接口,实现可变
    QuackBehavior quackBehavior;
    public Duck(){
    }

    public abstract void disPlay();

    public void performFly(){
        flyBehavior.fly();
    }
    public void performQuack(){
        quackBehavior.quack();
    }

    public void swim(){
        System.out.println("All ducks float , even decoys!!");
    }

    public void setFlyBehavior(FlyBehavior fb){
        this.flyBehavior = fb;
    }

    public void setQuackBehavior(QuackBehavior qb){
        this.quackBehavior = qb;
    }
}

这时,子类的编写既可以简化成下面的:

public class MallardDuck extends Duck {
    @Override
    public void disPlay() {
        System.out.println("我是绿头鸭!!");
    }
    public MallardDuck(){
    }
    public MallardDuck(QuackBehavior quackBehavior, FlyBehavior flyBehavior){
        setQuackBehavior(quackBehavior);
        setFlyBehavior(flyBehavior);
    }
}

至此,一个模式就此诞生!! 策略模式!! 员工B的这个模式得到了老板的认同,他拿到了奖金!!

策略模式定义了算法族,分别封装起来,让他们之间可以相互替换,此模式让算法的变化独立于使用算法的客户。

https://gitee.com/yan-jiadou/design-mode/blob/master/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/src/main/java/StrategyPattern/MiniDuckSimulator.java

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员小牧之

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值