重识设计模式-策略模式(Strategy Pattern)

本文已同步发表到个人技术微信公众号,扫一扫文章底部的二维码或在微信搜索 “程序员驿站”便可关注,不按期更新优质技术文章。同时,也欢迎加入QQ技术群(群号:650306310)一块儿交流学习!java

策略模式在代码编写过程当中有常用到,好比,JAVA AWT 中的 LayoutManager,Android属性动画源码中的Interpolator(插值器)等,下面我将介绍策略模式相关知识点,但愿对你们可以有帮助。程序员

定义

策略模式定义了一系列的算法,并将每个算法封装起来,使每种算法之间能够相互替换。策略模式主要解决在有多种算法类似的状况下,使用if...else所带来的复杂和难以维护的问题。一般是在一个系统有许多许多类,而区分它们的只是他们直接的行为时使用。算法

角色

策略模式会涉及到三个角色,分别为环境(Context)角色、抽象策略(Strategy)角色、具体策略(ConcreteStrategy)角色。bash

环境(Context)角色: 持有一个Strategy的引用;
抽象策略(Strategy)角色: 这是一个抽象角色,一般由一个接口或抽象类实现。此角色给出全部的具体策略类所需的接口;
具体策略(ConcreteStrategy)角色: 包装了相关的算法或行为。微信

案例回放

某院线公司须要开发一套影院售票系统,在该系统中须要为不一样类型的用户提供不一样的电影票打折方式,具体打折方案以下:
(1) 学生凭学生证可享受票价8折优惠;
(2) 年龄在10周岁及如下的儿童可享受每张票减免10元的优惠( 原始票价需大于等于20元) ;
(3) 影院VIP用户除享受票价半价优惠外还可进行积分,积分累计到必定额度可换取电影院赠送的奖品。学习

Note:该系统在未来可能还要根据须要引入新的打折方式。测试

为了实现打折算法的复用,并可以灵活地向系统中增长新的打折方式,这里咱们使用策略模式做为电影院打折方案的基础结构再好不过了,使用策略模式以后基本结构以下:动画

以上结构图中MovieTicket充当环境类角色,Discount充当抽象策略角色,StudentDiscount、ChildrenDiscount 和VIPDiscount充当具体策略角色。下面是各个类的完整代码:ui

MovieTicket.javathis

/**
 * Created by chendx on 2019/3/15
 * @since 1.0
 * 电影票类:环境类
 */
public class MovieTicket {
    private double price;
    /** 维持一个对抽象折扣类的引用 */
    private Discount discount;

    public void setPrice(double price) {
        this.price = price;
    }

    /**
     * 注入一个折扣类对象
     */
    public void setDiscount(Discount discount) {
        this.discount = discount;
    }

    public double getPrice() {
        //调用折扣类的折扣价计算方法
        return discount.calculate(this.price);
    }
}
复制代码

Discount.java

/**
 * Created by chendx on 2019/3/15
 * @since 1.0
 * 折扣类:抽象策略类
 */
public interface Discount {
    double calculate(double price);
}
复制代码

StudentDiscount.java

/**
 * Created by chendx on 2019/3/15
 * @since 1.0
 * 学生票折扣类:具体策略类
 */
public class StudentDiscount implements Discount {
    public double calculate(double price) {
        System.out.println("学生票:");
        return price * 0.8;
    }
}
复制代码

ChildrenDiscount.java

/**
 * Created by chendx on 2019/3/15
 * @since 1.0
 * 儿童票折扣类:具体策略类
 */
public class ChildrenDiscount implements Discount {
    public double calculate(double price) {
        System.out.println("儿童票:");
        return price - 10;
    }
}
复制代码

VIPDiscount.java

/**
 * Created by chendx on 2019/3/15
 * @since 1.0
 * VIP会员票折扣类:具体策略类
 */
public class VIPDiscount implements Discount {
    public double calculate(double price) {
        System.out.println("VIP票:");
        System.out.println("增长积分!");
        return price * 0.5;
    }
}
复制代码

编写客户端测试代码:

Client.java

/**
 * Created by chendx on 2019/3/15
 * @since 1.0
 * 客户端测试代码
 */
public class Client {
    public static void main(String args[]) {
        double originalPrice = 60.0;
        double currentPrice;

        MovieTicket mt = new MovieTicket();
        mt.setPrice(originalPrice);

        System.out.println("原始价为:" + originalPrice);
        System.out.println("---------------------------------");

        Discount discount = new StudentDiscount();
//        Discount discount = new ChildrenDiscount();
//        Discount discount = new VIPDiscount();
        
        mt.setDiscount(discount); //注入折扣对象
        currentPrice = mt.getPrice();
        System.out.println("折后价为:" + currentPrice);
    }
}
复制代码

编译并运行程序,输出结果以下:

原始价为:60.0
---------------------------------
学生票:
折后价为:48.0
复制代码

若是须要更换具体策略类,例如将学生票改成儿童票,只需将具体策略类StudentDiscount改成ChildrenDiscount便可:

Client.java

/**
 * Created by chendx on 2019/3/15
 * @since 1.0
 * 客户端测试代码
 */
public class Client {
    public static void main(String args[]) {
        double originalPrice = 60.0;
        double currentPrice;

        MovieTicket mt = new MovieTicket();
        mt.setPrice(originalPrice);

        System.out.println("原始价为:" + originalPrice);
        System.out.println("---------------------------------");

//        Discount discount = new StudentDiscount();
        Discount discount = new ChildrenDiscount();
//        Discount discount = new VIPDiscount();
        
        mt.setDiscount(discount); //注入折扣对象
        currentPrice = mt.getPrice();
        System.out.println("折后价为:" + currentPrice);
    }
}
复制代码

从新运行客户端程序,输出结果以下:

原始价为:60.0
---------------------------------
儿童票:
折后价为:50.0
复制代码

若是须要增长新的打折方式,原有代码均无须修改,只要增长一个新的折扣类做为抽象折扣类的子类,实如今抽象折扣类中声明的打折方法,将原有具体折扣类类名改成新增折扣类类名便可,彻底符合“开闭原则”,

典型应用

咱们常用到属性动画插值器就是策略模式的典型应用之一,这里为了节省篇幅,就不在分析属性动画插值器在源码中的整个调用流程,属性动画插值器类图以下:

优势

1.策略模式提供了管理相关的算法族的办法。策略类的等级结构定义了一个算法或行为族。恰当使用继承能够把公共的代码移到父类里面,从而避免代码重复;

2.使用策略模式能够避免使用多重条件(if-else)语句,多重条件语句不易维护,它把采起哪种算法或采起哪种行为的逻辑与算法或行为的逻辑混合在一块儿,通通列在一个多重条件语句里面,比使用继承的办法还要原始和落后。

缺点

1.客户端必须知道全部的策略类,并自行决定使用哪个策略类。这就意味着客户端必须理解这些算法的区别,以便适时选择恰当的算法类。换言之,策略模式只适用于客户端知道算法或行为的状况。

2.因为策略模式把每一个具体的策略实现都单独封装成为类,若是备选的策略不少的话,那么对象的数目就会很可观。

重点

策略模式的重心不是如何实现算法,而是如何组织、调用这些算法,从而让程序结构更灵活,具备更好的维护性和扩展性。

特色

运行时策略的惟一性: 运行期间,策略模式在每个时刻只能使用一个具体的策略实现对象,虽然能够动态地在不一样的策略实现中切换,可是同时只能使用一个。

平等性: 策略模式一个很大的特色就是各个策略算法的平等性。对于一系列具体的策略算法,你们的地位是彻底同样的,正由于这个平等性,才能实现算法之间能够相互替换。全部的策略算法在实现上也是相互独立的,相互之间是没有依赖的。

使用场景

一、若是在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式能够动态地让一个对象在许多行为中选择一种行为。

二、一个系统须要动态地在几种算法中选择一种。

三、若是一个对象有不少的行为,若是不用恰当的模式,这些行为就只好使用多重的条件选择语句来实现。

关注个人技术公众号"程序员驿站",天天都有优质技术文章推送,微信扫一扫下方二维码便可关注:

image
相关文章
相关标签/搜索