设计模式--策略模式

策略模式

  • 定义

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

先不用着急理解定义,先看下面的例子

  • 栗子

假设我们有一个Car类(代表所有的汽车),我们知道汽车都可以发动、加速、刹车等,而现在的汽车种类有非常非常多,不同的品牌,使用不同的燃料等等,于是,为了提高代码的复用性,我们可以将所有汽车都具有的共性封装到Car类中,不同类型的车有各自的类,他们的个性就在各自的类中实现即可。代码如下:

Car类

public class Car{

    /**
     * 发动
     */
    public void start(){
        System.out.println("发动");
    }

    /**
     * 加速
     */
    public void accele(){
        System.out.println("加速");
    }

    /**
     * 刹车
     */
    public void brake(){
        System.out.println("刹车");
    }
}

宝马汽车类

/**
 * 奔驰 继承Car类
 */
public class BWMCar extends Car{
    
    /**
     * 查看汽车品牌
     */
    public void getBrand(){
        System.out.println("宝马");
    }

    /**
     * 补充燃料
     */
    public void refuel(){
        System.out.println("加油");
    }
}

特斯拉汽车类

/**
 * 特斯拉 继承Car
 */
public class TeslaCar extends Car{
    /**
     * 查看汽车品牌
     */
    public void getBrand(){
        System.out.println("特斯拉");
    }

    /**
     * 补充燃料
     */
    public void refuel(){
        System.out.println("充电");
    }
}

测试运行类

public class Test {
    public static void main(String[] args) {
        BWMCar bwm = new BWMCar();
        TeslaCar tesla = new TeslaCar();

        System.out.println("=======宝马=======");
        bwm.start();
        bwm.accele();
        bwm.brake();
        bwm.getBrand();
        bwm.refuel();

        System.out.println("========特斯拉========");
        tesla.start();
        tesla.accele();
        tesla.brake();
        tesla.getBrand();
        tesla.refuel();
        
    }
}

运行结果:

=======宝马=======
发动
加速
刹车
宝马
加油
========特斯拉========
发动
加速
刹车
特斯拉
充电

类图

image-20200922204013861

对于现在来讲这样做已经可以了,但是,开发过程中从来不缺新需求的提出。假设此时比亚迪的电动车也要添加上,如果我们继续按照上卖弄继承的方式,可以得到下面比亚迪电动车类。

public class BYDCar extends Car{
    /**
     * 查看汽车品牌
     */
    public void getBrand(){
        System.out.println("比亚迪");
    }

    /**
     * 补充燃料
     */
    public void refuel(){
        System.out.println("充电");
    }
}

我们可以看到,BYDCar 类和TeslaCar类除了getBrand() 的方法不一样外,refuel() 方法是一模一样的,那么我们是不是可以将这个共有的方法提取出来呢?

提取到Car中显然是不可以的,因为BWMCar 同时也继承的Car类,如果将refuel()【充电】方法提取到Car中,那么所有补充燃料方式不是充电的Car的子类都需要重写refuel()方法,当子类特别多的时候,将会是非常麻烦的事情,后期如果有变动,修改起来也会令人疯狂!

这时候你可能会有这样的想法,我们可以再设计出两个子类,一个作为汽油车的父类,一个作为电动车的父类,由这两个类继承Car,得到的类图如下所示

image-20200922204519584

看似这样是可以的,但是我们知道比亚迪也有传统的汽油车,那用继承该如何实现呢?每个品牌下都有很多型号的汽车,用继承全部实现可以吗?

不论是如何实现,我们从上面两次实现中都发现了继承的缺陷,显然,继承很难满足快速变化的需求,或者说继承不是最佳的实现方式!

分析上面类图可以发现,子类很多的方法都是相同的,只是具体的实现不同。是不是非常熟悉的感觉,没错,就是接口!我们接下来的解决方法就是使用接口来实现。

首先先抽离获取品牌的方法,我们知道,汽车的品牌有很多,同时每个品牌又有非常多的型号的汽车,所以品牌非常有必要抽离!

image-20200922205630936

我们抽离出一个接口Brand,所有的汽车品牌都需要实现这个接口,而具体的实现都是由具体的品牌自己确定。

同理,可以抽离出燃料补充接口:

image-20200922210408351

同时我们再Car类中添加这两种行为的属性,同时抽离这两种行为,并将其委托给具体的行为接口实现类去执行

image-20200922210910728

完整的类图如下:

image-20200922212707203

再这里使用的是组合来整合所有的行为,而不是继承,下面用代码来实现吧

Brand接口

/**
 * 品牌接口
 */
public interface Brand {

    void getBrand();
    
}

品牌实现类

/**
 * 宝马品牌实现类 继承品牌接口
 */
public class BWM implements Brand{
    
    public void getBrand(){
        System.out.println("宝马");
    }
}
public class Tesla implements Brand{
    
    public void getBrand(){
        System.out.println("特斯拉");
    }
}

public class BYD implements Brand{
    
    public void getBrand(){
        System.out.println("比亚迪");
    }
}

补充燃料接口

/**
 * 补充燃料接口
 */
public interface RefuelWay {
    
    void refuel();
}

补充燃料实现接口

/**
 * 加油补充燃料实现类
 */
public class Oil implements RefuelWay{
    
    public void refuel(){
        System.out.println("加油");
    }
}

public class Electricity implements RefuelWay{
    
    public void refuel(){
        System.out.println("充电");
    }
}

Car实现类

public class Car{

    /**
     * 展示品牌行为
     */
    private Brand brand;

    /**
     * 补充燃料方式行为
     */
    private RefuelWay refuelWay;

    /**
     * 构造方法中设置两种行为
     */
    public Car(Brand brand,RefuelWay refuelWay){
        this.brand = brand;
        this.refuelWay = refuelWay;
    }

    /**
     * 展示品牌 委托给品牌具体实现类
     */
    public void getBrand(){
        brand.getBrand();
    }

    /**
     * 补充燃料 委托给补充燃料的具体实现类
     */
    public void refuel(){
        refuelWay.refuel();
    }

    /**
     * 发动
     */
    public void start(){
        System.out.println("发动");
    }

    /**
     * 加速
     */
    public void accele(){
        System.out.println("加速");
    }

    /**
     * 刹车
     */
    public void brake(){
        System.out.println("刹车");
    }

    /**
     * 展示方法 方便我们测试
     */
    public void display(){
        this.start();
        this.accele();
        this.brake();
        this.getBrand();
        this.refuel();
    }
}

测试类

public class Test {
    public static void main(String[] args) {
        // 创建不同的品牌
        Brand bwm = new BWM();
        Brand tesla = new Tesla();
        Brand byd = new BYD();

        // 创建不同的燃料补充方式
        RefuelWay oil = new Oil();
        RefuelWay electricity = new Electricity();

        // 构造 宝马的汽油车
        System.out.println("------------构造 宝马的汽油车--------------");
        Car car1 = new Car(bwm,oil);
        car1.display();

        // 构造 宝马电动车
        System.out.println("------------构造 宝马电动车--------------");
        Car car2 = new Car(bwm, electricity);
        car2.display();

        // 构造 特斯拉电动车
        System.out.println("------------构造 特斯拉电动车--------------");
        Car car3 = new Car(tesla,electricity);
        car3.display();

        // 构造 比亚迪电动车
        System.out.println("------------构造 比亚迪电动车--------------");
        Car car4 = new Car(byd,electricity);
        car4.display();

        // 构造 比亚迪汽油车
        System.out.println("------------构造 比亚迪汽油车--------------");
        Car car5 = new Car(byd,oil);
        car5.display();
    }
}

输出:

------------构造 宝马的汽油车--------------
发动
加速
刹车
宝马
加油
------------构造 宝马电动车--------------
发动
加速
刹车
宝马
充电
------------构造 特斯拉电动车--------------
发动
加速
刹车
特斯拉
充电
------------构造 比亚迪电动车--------------
发动
加速
刹车
比亚迪
充电
------------构造 比亚迪汽油车--------------
发动
加速
刹车
比亚

对于一开始使用的继承,如果父类后期发生变化,那么对于子类的影响是非常大的,同时继承也非常的不灵活。而使用组合时,如果后期行为发生了变化,我们只需要切换这个行为的实现类即可,而Car中使用的时实现这个行为的接口,对其子类不会产生影响,如果需要添加新的行为,只需要添加一个新的行为接口,并将其委托给具体的实现类即可。

由此我们可以得出:实际开发中,我们应当多用组合,少用继承。

这里对展示品牌和补充燃料方式行为的封装就是使用的策略模式,再次阅读策略模式的定义,应当容易理解了。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,530评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,403评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,120评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,770评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,758评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,649评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,021评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,675评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,931评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,751评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,410评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,004评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,969评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,042评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,493评论 2 343

推荐阅读更多精彩内容

  • [设计模式] 策略模式 @TOC 手机用户请横屏获取最佳阅读体验,REFERENCES中是本文参考的链接,如需要链...
    离兮丶斜阳阅读 294评论 0 0
  • 一. 概述 考虑如下情景,超市搞打折活动,对于消费额度不同提供不同的折扣,比如:满200打9折,满400打8折之类...
    BrightLoong阅读 502评论 0 4
  • 二十三种设计模式 - 策略模式 策略模式简介 模式动机 完成一项任务,往往可以有多种不同的方式,每一种方式称为一个...
    JustTheSame阅读 1,826评论 2 16
  • 1、定义: 定义一系列算法,将每个算法封装到具有公共接口的一系列策略类中,从而使它们可以相互替换,并让算法可以在不...
    程序员will阅读 252评论 0 0
  • 久违的晴天,家长会。 家长大会开好到教室时,离放学已经没多少时间了。班主任说已经安排了三个家长分享经验。 放学铃声...
    飘雪儿5阅读 7,480评论 16 22