zoukankan      html  css  js  c++  java
  • 设计模式--策略模式

    策略模式

    • 定义

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

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

    • 栗子

    假设我们有一个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中使用的时实现这个行为的接口,对其子类不会产生影响,如果需要添加新的行为,只需要添加一个新的行为接口,并将其委托给具体的实现类即可。

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

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

  • 相关阅读:
    hdu 3265 Posters(线段树+扫描线+面积并)
    hdu 3974 Assign the task(线段树)
    hdu 1540 Tunnel Warfare(线段树)
    poj 2777 Count Color(线段树(有点意思))
    用Flask+Redis维护Cookies池
    用代理抓取微信文章
    Idea-常用快捷键列表
    用Flask+Redis维护代理池
    Selsnium-Chrome-PhantomJS-爬取淘宝美食
    分析Ajax请求抓取今日头条街拍图片
  • 原文地址:https://www.cnblogs.com/erkye/p/13715137.html
Copyright © 2011-2022 走看看