精华内容
下载资源
问答
  • 本文的主要内容:介绍策略模式示例商场购物打折策略的实现策略模式总结源码分析策略模式的典型应用Java Comparator 中的策略模式Spring Resource 中的策略模式Spring Bean 实例化中的策略模式策略模式在软件开发中,...

    本文的主要内容:

    • 介绍策略模式

    • 示例

      • 商场购物打折策略的实现
    • 策略模式总结

    • 源码分析策略模式的典型应用

      • Java Comparator 中的策略模式
      • Spring Resource 中的策略模式
      • Spring Bean 实例化中的策略模式

    策略模式

    在软件开发中,我们也常常会遇到类似的情况,实现某一个功能有多条途径,每一条途径对应一种算法,此时我们可以使用一种设计模式来实现灵活地选择解决途径,也能够方便地增加新的解决途径。

    譬如商场购物场景中,有些商品按原价卖,商场可能为了促销而推出优惠活动,有些商品打九折,有些打八折,有些则是返现10元等。

    而优惠活动并不影响结算之外的其他过程,只是在结算的时候需要根据优惠方案结算

    499a19cabd46b0febebaad5784e2d29e.png
    img

    商场促销场景

    角色

    Context(环境类):环境类是使用算法的角色,它在解决某个问题(即实现某个方法)时可以采用多种策略。在环境类中维持一个对抽象策略类的引用实例,用于定义所采用的策略。

    Strategy(抽象策略类):它为所支持的算法声明了抽象方法,是所有策略类的父类,它可以是抽象类或具体类,也可以是接口。环境类通过抽象策略类中声明的方法在运行时调用具体策略类中实现的算法。

    ConcreteStrategy(具体策略类):它实现了在抽象策略类中声明的算法,在运行时,具体策略类将覆盖在环境类中定义的抽象策略类对象,使用一种具体的算法实现某个业务处理。

    示例

    如果要写出一个商场优惠场景的Demo可以很快的写出来,譬如

    import java.text.MessageFormat;

    public class Shopping {
        private String goods;
        private double price;
        private double finalPrice;
        private String desc;

        public Shopping(String goods, double price) {
            this.goods = goods;
            this.price = price;
        }

        public double calculate(String discountType) {
            if ("dis9".equals(discountType)) {
                finalPrice = price * 0.9;
                desc = "打九折";
            } else if ("dis8".equals(discountType)) {
                finalPrice = price * 0.8;
                desc = "打八折";
            } else if ("cash10".equals(discountType)) {
                finalPrice = price >= 10 ? price - 10 : 0;
                desc = "返现10元";
            } else {
                finalPrice = price;
                desc = "不参与优惠活动";
            }
            System.out.println(MessageFormat.format("购买的物品:{0},原始价格:{1},{2},最终价格为:{3}", goods, price, desc, finalPrice));
            return finalPrice;
        }
    }

    测试

    public class Test {
        public static void main(String[] args) {
            Shopping shopping1 = new Shopping("书籍-深入理解Java虚拟机", 54.00);
            shopping1.calculate("dis9"); // 九折

            Shopping shopping2 = new Shopping("Apple 妙控鼠标", 588.00 );
            shopping2.calculate("dis8");

            Shopping shopping3 = new Shopping("戴尔U2417H显示器", 1479.00);
            shopping3.calculate("cash10");

            Shopping shopping4 = new Shopping("索尼ILCE-6000L相机", 3599.00);
            shopping4.calculate(null);
        }
    }

    以上代码当然完成了我们的需求,但是存在以下问题:

    • Shopping 类的 calculate() 方法非常庞大,它包含各种优惠算法的实现代码,在代码中出现了较长的 if…else…语句,不利于测试和维护。
    • 增加新的优惠算法或者对原有打折算法进行修改时必须修改 Shopping 类的源代码,违反了 “开闭原则”,系统的灵活性和可扩展性较差。
    • 算法的复用性差,如果在另一个系统中需要重用某些优惠算法,只能通过对源代码进行复制粘贴来重用,无法单独重用其中的某个或某些算法。

    所以我们需要使用策略模式对 Shopping 类进行重构,将原本庞大的 Shopping 类的职责进行分解,将算法的定义和使用分离。

    抽象策略类 Discount,它是所有具体优惠算法的父类,定义了一个 discount 抽象方法

    import lombok.Data;

    @Data
    public abstract class Discount {
        protected double finalPrice;
        protected String desc;

        public Discount(String desc) {
            this.desc = desc;
        }

        abstract double discount(double price);
    }

    四种具体策略类,继承自抽象策略类 Discount,并在 discount 方法中实现具体的优惠算法

    public class Dis9Discount extends Discount {
        public Dis9Discount() {
            super("打九折");
        }

        @Override
        double discount(double price) {
            finalPrice = price * 0.9;
            return finalPrice;
        }
    }

    public class Dis8Discount extends Discount{
        public Dis8Discount() {
            super("打八折");
        }

        @Override
        double discount(double price) {
            finalPrice = price * 0.8;
            return finalPrice;
        }
    }

    public class Cash10Discount extends Discount {
        public Cash10Discount() {
            super("返现10元");
        }

        @Override
        public double discount(double price) {
            this.finalPrice = price >= 10 ? price - 10 : 0;
            return finalPrice;
        }
    }

    public class NoneDiscount extends Discount {
        public NoneDiscount() {
            super("不参与优惠活动");
        }

        @Override
        double discount(double price) {
            finalPrice = price;
            return finalPrice;
        }
    }

    环境类 Shopping,维护了一个 Discount 引用

    public class Shopping {
        private String goods;
        private double price;
        private Discount discount;

        public Shopping(String goods, double price, Discount discount) {
            this.goods = goods;
            this.price = price;
            this.discount = discount;
        }

        public double calculate() {
            double finalPrice = discount.discount(this.price);
            String desc = discount.getDesc();
            System.out.println(MessageFormat.format("购买的物品:{0},原始价格:{1},{2},最终价格为:{3}", goods, price, desc, finalPrice));
            return finalPrice;
        }
    }

    测试

    public class Test {
        public static void main(String[] args) {
            Shopping shopping1 = new Shopping("书籍-深入理解Java虚拟机", 54.00, new Dis9Discount());
            shopping1.calculate();

            Shopping shopping2 = new Shopping("Apple 妙控鼠标", 588.00, new Dis8Discount());
            shopping2.calculate();

            Shopping shopping3 = new Shopping("戴尔U2417H显示器", 1479.00, new Cash10Discount());
            shopping3.calculate();

            Shopping shopping4 = new Shopping("索尼ILCE-6000L相机", 3599.00, new NoneDiscount());
            shopping4.calculate();
        }
    }

    结果

    购买的物品:书籍-深入理解Java虚拟机,原始价格:54,打九折,最终价格为:48.6
    购买的物品:Apple 妙控鼠标,原始价格:588,打八折,最终价格为:470.4
    购买的物品:戴尔U2417H显示器,原始价格:1,479,返现10元,最终价格为:1,469
    购买的物品:索尼ILCE-6000L相机,原始价格:3,599,不参与优惠活动,最终价格为:3,599

    可以看到,使用策略模式重构后,Shopping 类的 calculate 方法简洁了很多,当需要更改优惠算法的时候不需要再修改 Shopping 类的源代码;要扩展出新的优惠算法很方便,只需要继承抽象策略类 Discount 并实现 calculate 方法即可;优惠算法很容易重用。

    画出类图如下

    9c2c71b71f45cca70f083792b1625888.png
    img

    示例.策略模式类图

    策略模式总结

    策略模式的主要优点如下:

    • 策略模式提供了对 “开闭原则” 的完美支持,用户可以在不修改原有系统的基础上选择算法或行为,也可以灵活地增加新的算法或行为。
    • 策略模式提供了管理相关的算法族的办法。策略类的等级结构定义了一个算法或行为族,恰当使用继承可以把公共的代码移到抽象策略类中,从而避免重复的代码。
    • 策略模式提供了一种可以替换继承关系的办法。如果不使用策略模式而是通过继承,这样算法的使用就 和算法本身混在一起,不符合 “单一职责原则”,而且使用继承无法实现算法或行为在程序运行时的动态切 换。
    • 使用策略模式可以避免多重条件选择语句。多重条件选择语句是硬编码,不易维护。
    • 策略模式提供了一种算法的复用机制,由于将算法单独提取出来封装在策略类中,因此不同的环境类可以方便地复用这些策略类。

    策略模式的主要缺点如下:

    • 客户端必须知道所有的策略类,并自行决定使用哪一个策略类。这就意味着客户端必须理解这些算法的区别,以便适时选择恰当的算法。换言之,策略模式只适用于客户端知道所有的算法或行为的情况。
    • 策略模式将造成系统产生很多具体策略类,任何细小的变化都将导致系统要增加一个新的具体策略类。
    • 无法同时在客户端使用多个策略类,也就是说,在使用策略模式时,客户端每次只能使用一个策略类,不支持使用一个策略类完成部分功能后再使用另一个策略类来完成剩余功能的情况。

    适用场景

    • 一个系统需要动态地在几种算法中选择一种,那么可以将这些算法封装到一个个的具体算法类中,而这些具体算法类都是一个抽象算法类的子类。换言之,这些具体算法类均有统一的接口,根据 “里氏代换原则” 和面向对象的多态性,客户端可以选择使用任何一个具体算法类,并只需要维持一个数据类型是抽象算法类的对象。
    • 一个对象有很多的行为,如果不用恰当的模式,这些行为就只好使用多重条件选择语句来实现。此时,使用策略模式,把这些行为转移到相应的具体策略类里面,就可以避免使用难以维护的多重条件选择语句。
    • 不希望客户端知道复杂的、与算法相关的数据结构,在具体策略类中封装算法与相关的数据结构,可以提高算法的保密性与安全性。

    源码分析策略模式的典型应用

    Java Comparator 中的策略模式

    java.util.Comparator 接口是比较器接口,可以通过 Collections.sort(List,Comparator) 和 Arrays.sort(Object[],Comparator) 对集合和数据进行排序,下面为示例程序

    一个学生类,有两个属性 id 和 name

    @Data
    @AllArgsConstructor
    public class Student {
        private Integer id;
        private String name;

        @Override
        public String toString() {
            return "{id=" + id + ", name='" + name + "'}";
        }
    }

    实现两个比较器,比较器实现了 Comparator 接口,一个升序,一个降序

    // 降序
    public class DescSortor implements Comparator {
        @Override
        public int compare(Student o1, Student o2) {return o2.getId() - o1.getId();
        }
    }
    // 升序
    public class AscSortor implements Comparator {
        @Override
        public int compare(Student o1, Student o2) {return o1.getId() - o2.getId();
        }
    }

    通过 Arrays.sort() 对数组进行排序

    public class Test1 {
        public static void main(String[] args) {
            Student[] students = {
                    new Student(3, "张三"),
                    new Student(1, "李四"),
                    new Student(4, "王五"),
                    new Student(2, "赵六")
            };
            toString(students, "排序前");

            Arrays.sort(students, new AscSortor());
            toString(students, "升序后");

            Arrays.sort(students, new DescSortor());
            toString(students, "降序后");
        }

        public static void toString(Student[] students, String desc){
            for (int i = 0; i             System.out.print(desc + ": " +students[i].toString() + ", ");
            }
            System.out.println();
        }
    }

    输出

    排序前: {id=3, name='张三'}, 排序前: {id=1, name='李四'}, 排序前: {id=4, name='王五'}, 排序前: {id=2, name='赵六'},
    升序后: {id=1, name='李四'}, 升序后: {id=2, name='赵六'}, 升序后: {id=3, name='张三'}, 升序后: {id=4, name='王五'},
    降序后: {id=4, name='王五'}, 降序后: {id=3, name='张三'}, 降序后: {id=2, name='赵六'}, 降序后: {id=1, name='李四'},

    通过 Collections.sort() 对集合List进行排序

    public class Test2 {
        public static void main(String[] args) {
            List students = Arrays.asList(
                    new Student(3, "张三"),
                    new Student(1, "李四"),
                    new Student(4, "王五"),
                    new Student(2, "赵六")
            );
            toString(students, "排序前");
            Collections.sort(students, new AscSortor());
            toString(students, "升序后");
            Collections.sort(students, new DescSortor());
            toString(students, "降序后");
        }
        public static void toString(List students, String desc) {for (Student student : students) {
                System.out.print(desc + ": " + student.toString() + ", ");
            }
            System.out.println();
        }
    }

    输出

    排序前: {id=3, name='张三'}, 排序前: {id=1, name='李四'}, 排序前: {id=4, name='王五'}, 排序前: {id=2, name='赵六'},
    升序后: {id=1, name='李四'}, 升序后: {id=2, name='赵六'}, 升序后: {id=3, name='张三'}, 升序后: {id=4, name='王五'},
    降序后: {id=4, name='王五'}, 降序后: {id=3, name='张三'}, 降序后: {id=2, name='赵六'}, 降序后: {id=1, name='李四'},

    我们向 Collections.sort() 和 Arrays.sort() 分别传入不同的比较器即可实现不同的排序效果(升序或降序)

    这里 Comparator 接口充当了抽象策略角色,两个比较器 DescSortor 和 AscSortor 则充当了具体策略角色,Collections 和 Arrays 则是环境角色

    Spring Resource 中的策略模式

    Spring 把所有能记录信息的载体,如各种类型的文件、二进制流等都称为资源,譬如最常用的Spring配置文件。

    在 Sun 所提供的标准 API 里,资源访问通常由 java.NET.URL 和文件 IO 来完成,尤其是当我们需要访问来自网络的资源时,通常会选择 URL 类。

    URL 类可以处理一些常规的资源访问问题,但依然不能很好地满足所有底层资源访问的需要,比如,暂时还无法从类加载路径、或相对于 ServletContext 的路径来访问资源,虽然 Java 允许使用特定的 URL 前缀注册新的处理类(例如已有的 http: 前缀的处理类),但是这样做通常比较复杂,而且 URL 接口还缺少一些有用的功能,比如检查所指向的资源是否存在等。

    Spring 改进了 Java 资源访问的策略,Spring 为资源访问提供了一个 Resource 接口,该接口提供了更强的资源访问能力,Spring 框架本身大量使用了 Resource 接口来访问底层资源。

    public interface Resource extends InputStreamSource {
        boolean exists();    // 返回 Resource 所指向的资源是否存在
        boolean isReadable();   // 资源内容是否可读
        boolean isOpen();   // 返回资源文件是否打开
        URL getURL() throws IOException;
        URI getURI() throws IOException;
        File getFile() throws IOException;  // 返回资源对应的 File 对象
        long contentLength() throws IOException;
        long lastModified() throws IOException;
        Resource createRelative(String var1) throws IOException;
        String getFilename();
        String getDescription();    // 返回资源的描述信息
    }

    Resource 接口是 Spring 资源访问策略的抽象,它本身并不提供任何资源访问实现,具体的资源访问由该接口的实现类完成——每个实现类代表一种资源访问策略

    9f163635702b00ed6a8e8d51e720ac70.png
    img

    Spring资源访问接口Resource的实现类

    Spring 为 Resource 接口提供的部分实现类如下:

    • UrlResource:访问网络资源的实现类。
    • ClassPathResource:访问类加载路径里资源的实现类。
    • FileSystemResource:访问文件系统里资源的实现类。
    • ServletContextResource:访问相对于 ServletContext 路径里的资源的实现类:
    • InputStreamResource:访问输入流资源的实现类。
    • ByteArrayResource:访问字节数组资源的实现类。
    • WritableResource:写资源文件

    这些 Resource 实现类,针对不同的的底层资源,提供了相应的资源访问逻辑,并提供便捷的包装,以利于客户端程序的资源访问。

    它们之间的类关系如下所示:

    717293c9c3e38372ee10d459ab43ab5a.png
    img

    Spring Resource 类图

    可以看到 AbstractResource 资源抽象类实现了 Resource 接口,为子类通用的操作提供了具体实现,非通用的操作留给子类实现,所以这里也应用了模板方法模式。(只不过缺少了模板方法)

    Resource 不仅可在 Spring 的项目中使用,也可直接作为资源访问的工具类使用。意思是说:即使不使用 Spring 框架,也可以使用 Resource 作为工具类,用来代替 URL

    譬如我们可以使用 UrlResource 访问网络资源。

    也可以通过其它协议访问资源,file: 用于访问文件系统;http: 用于通过 HTTP 协议访问资源;ftp: 用于通过 FTP 协议访问资源等

    public class Test {
        public static void main(String[] args) throws IOException {
            UrlResource ur = new UrlResource("http://image.laijianfeng.org/hello.txt");

            System.out.println("文件名:" + ur.getFilename());
            System.out.println("网络文件URL:" + ur.getURL());
            System.out.println("是否存在:" + ur.exists());
            System.out.println("是否可读:" + ur.isReadable());
            System.out.println("文件长度:" + ur.contentLength());

            System.out.println("\n--------文件内容----------\n");
            byte[] bytes = new byte[47];
            ur.getInputStream().read(bytes);
            System.out.println(new String(bytes));
        }
    }

    输出的内容如下,符合预期

    文件名:hello.txt
    网络文件URL:http://image.laijianfeng.org/hello.txt
    是否存在:true
    是否可读:true
    文件长度:47

    --------文件内容----------

    hello world!
    welcome to http://laijianfeng.org

    Spring Bean 实例化中的策略模式

    Spring实例化Bean有三种方式:构造器实例化、静态工厂实例化、实例工厂实例化

    譬如通过构造器实例化bean的XML示例如下:

    <?xml  version="1.0" encoding="UTF-8"?>
    "http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">"person" class="com.demo.Person">"personWithParam" class="com.demo.Person">"name" value="小旋锋"/>"personWirhParams" class="com.demo.Person">"name" value="小旋锋"/>"age" value="22"/>

    具体实例化Bean的过程中,Spring中角色分工很明确,创建对象的时候先通过 ConstructorResolver 找到对应的实例化方法和参数,再通过实例化策略 InstantiationStrategy 进行实例化,根据创建对象的三个分支( 工厂方法、有参构造方法、无参构造方法 ), InstantiationStrategy 提供了三个接口方法:

    public interface InstantiationStrategy {
        // 默认构造方法
        Object instantiate(RootBeanDefinition beanDefinition, String beanName, BeanFactory owner) throws BeansException;

        // 指定构造方法
        Object instantiate(RootBeanDefinition beanDefinition, String beanName, BeanFactory owner, Constructor> ctor,
                Object[] args) throws BeansException;

        // 指定工厂方法
        Object instantiate(RootBeanDefinition beanDefinition, String beanName, BeanFactory owner, Object factoryBean,
                Method factoryMethod, Object[] args) throws BeansException;
    }
    InstantiationStrategy` 为实例化策略接口,扮演抽象策略角色,有两种具体策略类,分别为 `SimpleInstantiationStrategy` 和 `CglibSubclassingInstantiationStrategy
    3fe376b462152b030c606f1fd285228b.png
    img

    Spring 实例化策略类图

    在 SimpleInstantiationStrategy 中对这三个方法做了简单实现,如果工厂方法实例化直接用反射创建对象,如果是构造方法实例化的则判断是否有 MethodOverrides,如果有无 MethodOverrides 也是直接用反射,如果有 MethodOverrides 就需要用 cglib 实例化对象,SimpleInstantiationStrategy 把通过 cglib 实例化的任务交给了它的子类 CglibSubclassingInstantiationStrategy

    文章来源: http://laijianfeng.org

    展开全文
  • 容错恢复机制容错恢复机制是应用程序开发中非常常见的功能。那么什么是容错恢复呢?简单点说就是:程序运行的时候,正常情况下应该按照某种方式来做,如果按照某种方式来做发生错误的话,系统并不会崩溃,也不会就此...

    容错恢复机制

    容错恢复机制是应用程序开发中非常常见的功能。那么什么是容错恢复呢?简单点说就是:程序运行的时候,正常情况下应该按照某种方式来做,如果按照某种方式来做发生错误的话,系统并不会崩溃,也不会就此不能继续向下运行了,而是有容忍出错的能力,不但能容忍程序运行出现错误,还提供出现错误后的备用方案,也就是恢复机制,来代替正常执行的功能,使程序继续向下运行。

    举个实际点的例子吧,比如在一个系统中,所有对系统的操作都要有日志记录,而且这个日志还需要有管理界面,这种情况下通常会把日志记录在数据库里面,方便后续的管理,但是在记录日志到数据库的时候,可能会发生错误,比如暂时连不上数据库了,那就先记录在文件里面,然后在合适的时候把文件中的记录再转录到数据库中。

    对于这样的功能的设计,就可以采用策略模式,把日志记录到数据库和日志记录到文件当作两种记录日志的策略,然后在运行期间根据需要进行动态的切换。

    在这个例子的实现中,要示范由上下文来选择具体的策略算法,前面的例子都是由客户端选择好具体的算法,然后设置到上下文中。

    下面还是通过代码来示例一下。

    (1)先定义日志策略接口,很简单,就是一个记录日志的方法,示例代码如下:

    /**

    * 日志记录策略的接口

    */

    public interface LogStrategy {

    /**

    * 记录日志

    * @param msg 需记录的日志信息

    */

    public void log(String msg);

    }

    (2)实现日志策略接口,先实现默认的数据库实现,假设如果日志的长度超过长度就出错,制造错误的是一个最常见的运行期错误,示例代码如下:

    /**

    * 把日志记录到数据库

    */

    public class DbLog implements LogStrategy{

    public void log(String msg) {

    //制造错误

    if(msg!=null && msg.trim().length()>5){

    int a = 5/0;

    }

    System.out.println("现在把 '"+msg+"' 记录到数据库中");

    }

    }

    接下来实现记录日志到文件中去,示例代码如下:

    /**

    * 把日志记录到文件

    */

    public class FileLog implements LogStrategy{

    public void log(String msg) {

    System.out.println("现在把 '"+msg+"' 记录到文件中");

    }

    }

    (3)接下来定义使用这些策略的上下文,注意这次是在上下文里面实现具体策略算法的选择,所以不需要客户端来指定具体的策略算法了,示例代码如下:

    ad7c78b40be9a23c5c6fa09f9226e5ef.gif

    (4)看看现在的客户端,没有了选择具体实现策略算法的工作,变得非常简单,故意多调用一次,可以看出不同的效果,示例代码如下:

    f1aabbac7e74ac9ee93cf32041ffbb91.gif

    (5)小结一下,通过上面的示例,会看到策略模式的一种简单应用,也顺便了解一下基本的容错恢复机制的设计和实现。在实际的应用中,需要设计容错恢复的系统一般要求都比较高,应用也会比较复杂,但是基本的思路是差不多的。

    2e2d60cbe683a84535c0deec11dfcc55.gif

    作者演示了策略的一种变体。

    这个例子同时说明了设计中的另外一个重要的概念,单一职责原理。

    单一职责原则(SRP),就一个类而言应该仅有一个引起它变化的原因。

    如果一个类承担的职责过多,就等于把这些职责耦合在一起,一个职责的变化可能会削弱或者抑制这个类完成其他职责的能力,这种耦合会导致脆弱的设计

    当变化发生时,设计会遭受到意想不到的破坏。

    展开全文
  • 策略模式策略模式作为一种软件设计模式,指对象有某个行为,但是在不同的场景中,该行为有不同的实现算法。比如每个人都要“交个人所得税”,但是“在美国交个人所得税”和“在中国交个人所得税”就有不同的算税方法...
    bf6b96f2e7de1293798d7d601d3d9ab8.png

    策略模式

    策略模式作为一种软件设计模式,指对象有某个行为,但是在不同的场景中,该行为有不同的实现算法。比如每个人都要“交个人所得税”,但是“在美国交个人所得税”和“在中国交个人所得税”就有不同的算税方法。

    1. 定义了一组算法(业务规则);
    2. 封装了每个算法;
    3. 这组的算法可互换代替(interchangeable)。

    优点

    1. 算法可以自由切换
    2. 避免使用多重条件判断
    3. 扩展性良好

    缺点:

    1. 策略类会增多
    2. 所有策略类都需要对外暴露

    应用场景

    1、 多个类只区别在表现行为不同,可以使用Strategy模式,在运行时动态选择具体要执行的行为。

    2、 需要在不同情况下使用不同的策略(算法),或者策略还可能在未来用其它方式来实现。

    3、 对客户隐藏具体策略(算法)的实现细节,彼此完全独立

    策略模式在支付系统的使用

    77a014a042bc5179867089971d177f08.png

    PayStrategy

    public interface PayStrategy {   public void pay(Object obj);}

    微信支付

    public class WechatPayStrategy implements PayStrategy{   @Override   public void pay(Object obj) {      System.out.println("do wechat pay");   }}

    支付宝支付

    public class AliPayStrategy implements PayStrategy{   @Override   public void pay(Object obj) {      System.out.println("do alipay");   }}

    context

    public class Context {   private PayStrategy payStrategy;    public Context(PayStrategy payStrategy){      this.payStrategy = payStrategy;   }    public void executeStrategy(Object obj){      return strategy.pay(obj);   }}

    StrategyPatternDemo

    public class StrategyPatternDemo {   public static void main(String[] args) {      Object wechatPayObj = new Object();      Context context = new Context(new WechatPayStrategy());          context.executeStrategy(wechatPayObj);       Object aliPayObj = new Object();      context = new Context(new AliPayStrategy());            context.executeStrategy(aliPayObj);    }}
    展开全文
  • 一、策略模式应用场景策略模式(Strategy Pattern)是指定义了算法家族、分别封装起来,让它们之间可以相互替换,此模式让算法的变化不会影响到使用算法的用户。1.1 应用场景假如系统中有很多类,而他们的区别仅仅...

    一、策略模式的应用场景

    策略模式(Strategy Pattern)是指定义了算法家族、分别封装起来,让它们之间可以相互替换,此模式让算法的变化不会影响到使用算法的用户。

    1.1 应用场景

    • 假如系统中有很多类,而他们的区别仅仅在于他们的行为不同。
    • 一个系统需要动态地在几种算法中选择一种。

    1.2 实现餐饮行业选择支付方式的业务场景

    我们在外面去吃饭的时候,不同的饭店经常会有优惠活动,优惠策略也有很多很多,比如优惠券折扣、返现促销、拼团下单等等。我们来用程序模拟这样的业务场景,首先创建一个促销策略的接口:

    public interface IPromotionStrategy {    void doPromotion();}

    然后分别创建优惠券抵扣策略 CouponStrategy 类:

    public class CouponStrategy implements IPromotionStrategy {    @Override    public void doPromotion() {        System.out.println("领取的优惠券在指定时间到店消费,订单的价格直接减优惠券面额抵扣!");    }}

    返现促销策略 CashBackStrategy 类:

    public class CashBackStrategy implements IPromotionStrategy {    @Override    public void doPromotion() {        System.out.println("返现促销,返回的金额转到支付账号!");    }}

    拼团优惠策略 GroupBuyStrategy 类:

    public class GroupBuyStrategy implements IPromotionStrategy {    @Override    public void doPromotion() {        System.out.println("拼团,满5人成团,全团享受团购价!");    }}

    无优惠策略 EmptyStrategy 类:

    public class EmptyStrategy implements IPromotionStrategy {    @Override    public void doPromotion() {        System.out.println("无促销活动!");    }}

    然后创建促销活动方案 PromotionActivity 类:

    public class PromotionActivity {    private IPromotionStrategy promotionStrategy;    public PromotionActivity(IPromotionStrategy promotionStrategy) {        this.promotionStrategy = promotionStrategy;    }    public void execute() {        promotionStrategy.doPromotion();    }}

    编写测试类:

    public static void main(String[] args) {    PromotionActivity activity_618 = new PromotionActivity(new CouponStrategy());    PromotionActivity activity_1212 = new PromotionActivity(new CashBackStrategy());    activity_618.execute();    activity_1212.execute();}

    此时,上面的这段测试代码放到实际的业务场景并不实用,因为餐饮门店做活动的时候是要根据不同需求对促销策略进行动态选择的,并不会一次性执行多种优惠。所以代码会这样写:

     public static void main(String[] args) {    PromotionActivity promotionActivity = null;    String promotionKey = "COUPON";    if(promotionKey.equals("COUPON")){        promotionActivity = new PromotionActivity(new CouponStrategy());    }else if(promotionKey.equals("CASHBACK")){        promotionActivity = new PromotionActivity(new CashBackStrategy());    }//......    promotionActivity.execute();}

    这样改造之后,满足了业务需求,客户可根据自己的需求选择不同的优惠策略了。但是,经过一段时间的业务积累,我们的促销活动会越来越多。于是,我们的程序猿小哥哥就忙不赢了,每次上活动之前都要通宵改代码,而且要做重复测试,判断逻辑可能也变得越来越复杂。这时候,我们是不需要思考代码是不是应该重构了?

    其实我们可以结合工厂模式和单例模式来进行优化改造。创建PromotionActivityFactory:

    public class PromotionActivityFactory {    public interface PromotionKey {        String COUPON = "COUPON";        String CASHBACK = "CASHBACK";        String GROUPBUY = "GROUPBUY";    }    private static Map PROMOTION_STRATEGY_MAP =            new HashMap<>();    static {        PROMOTION_STRATEGY_MAP.put(PromotionKey.COUPON, new CouponStrategy());        PROMOTION_STRATEGY_MAP.put(PromotionKey.CASHBACK, new CashBackStrategy());        PROMOTION_STRATEGY_MAP.put(PromotionKey.GROUPBUY, new GroupBuyStrategy());    }    private static final IPromotionStrategy NO_PROMOTION = new EmptyStrategy();    private PromotionActivityFactory() {}    public static IPromotionStrategy getPromotionStrategy(String promotionKey) {        IPromotionStrategy promotionStrategy = PROMOTION_STRATEGY_MAP.get(promotionKey);        return promotionStrategy == null ? NO_PROMOTION : promotionStrategy;    }}

    测试代码如下:

    public static void main(String[] args) {    String promotionKey = "COUPON";    PromotionActivity promotionActivity =  new PromotionActivity(PromotionActivityFactory.getPromotionStrategy(promotionKey));    promotionActivity.execute();}

    修改代码之后维护工作应该轻松了很多,每次上新活动,不会影响原来的代码逻辑。来看一下完整的类图:

    e323d8f1dbca179cf62aa739cd0564ea.png

    为了加深大家对策略模式的理解,结合实际生活场景再举一个例子。大家都用过移动支付进行付款,比较流行的支付方式有支付宝、微信、银联等。一个场景的支付场景就是在支付的时候提示会选择支付方式,如果用户没有进行选择,那么系统会使用默认的支付方式进行结算。

    创建抽象类Payment:

    public abstract class Payment {    public abstract String getType();    public PayState pay(String id, double amount) {        if(queryBalance(id) < amount) {            return new PayState(500,"支付失败","余额不足");        }        return new PayState(200,"支付成功","支付金额:" + amount);    }    public abstract double queryBalance(String id);}

    分别创建具体的支付方式,支付宝 AliPay 类:

    public class AliPay extends Payment {    @Override    public String getType() {        return "支付宝";    }    @Override    public double queryBalance(String id) {        return 1000;    }}

    微信支付 WechatPay 类:

    public class WechatPay extends Payment {    @Override    public String getType() {        return "微信支付";    }    @Override    public double queryBalance(String id) {        return 512;    }}

    银联云闪付支付 UnionPay 类:

    public class UnionPay extends Payment {    @Override    public String getType() {        return "云闪付";    }    @Override    public double queryBalance(String id) {        return 380;    }}

    创建支付状态的包装类 PayState:

    public class PayState {    private int code;    private Object data;    private String msg;    public PayState(int code, Object data, String msg) {        this.code = code;        this.data = data;        this.msg = msg;    }    @Override    public String toString() {        return "PayState{" +                "code=" + code +                ", data=" + data +                ", msg='" + msg + ''' +                '}';    }}

    创建支付策略管理工厂类:

    public class PayStrategyFactory {    public interface PayKey {        String DEFAULTPAY = "ALIPAY";        String ALIPAY = "ALIPAY";        String WECHATPAY = "WECHATPAY";        String UNIONPAY = "UNIONPAY";    }    public static final Map PAYMENT_MAP = new HashMap<>();    static  {        PAYMENT_MAP.put(PayKey.ALIPAY, new AliPay());        PAYMENT_MAP.put(PayKey.WECHATPAY, new WechatPay());        PAYMENT_MAP.put(PayKey.UNIONPAY, new UnionPay());    }    private static Payment getPayment(String payKey) {        if(!PAYMENT_MAP.containsKey(payKey)){            return PAYMENT_MAP.get(PayKey.DEFAULTPAY);        }        return PAYMENT_MAP.get(payKey);    }}

    创建订单 Order 类:

    public class Order {    private String id;    private String orderId;    private double amount;    public Order(String id, String orderId, double amount) {        this.id = id;        this.orderId = orderId;        this.amount = amount;    }    public PayState pay(){        return pay(PayStrategyFactory.PayKey.DEFAULTPAY);    }    public PayState pay(String payKey){        Payment payment = PayStrategyFactory.PAYMENT_MAP.get(payKey);        System.out.println("欢迎使用" + payment.getType());        System.out.println("本次交易金额为:" + amount + ",开始扣款...");        return payment.pay(id,amount);    }}

    测试代码如下:

    public static void main(String[] args) {    Order order = new Order("1", "20200225000001", 120.98);    System.out.println(order.pay(PayStrategyFactory.PayKey.ALIPAY));}

    运行结果:

    8d9dc14ff1146b8f46462a481636ad36.png

    最后来看一下类图结构:

    3bdc9cc9c2483bf83eb558d7471dd5f4.png

    二、源码中的策略模式

    2.1 Compartor接口

    Compartor接口中的compare()方法就是一个策略模式的抽象实现。

    int compare(T o1, T o2);

    Comparator 接口下面有非常多的实现类,我们经常会把 Comparator 作为参数传入作为排序策略,例如 Arrays 类的 parallelSort 方法等:

    25406973380207896877f8848ff8478e.png

    还有 TreeMap 的构造方法:

    b02204c8780ed88e153219bb15ccb2aa.png

    2.2 Spring中的策略模式

    2.2.1 Resouce类

    我们来看Resource类的源码:

    package org.springframework.core.io;import java.io.File;import java.io.IOException;import java.net.URI;import java.net.URL;import java.nio.channels.Channels;import java.nio.channels.ReadableByteChannel;import org.springframework.lang.Nullable;public interface Resource extends InputStreamSource {    boolean exists();    default boolean isReadable() {        return this.exists();    }    default boolean isOpen() {        return false;    }    default boolean isFile() {        return false;    }    URL getURL() throws IOException;    URI getURI() throws IOException;    File getFile() throws IOException;    default ReadableByteChannel readableChannel() throws IOException {        return Channels.newChannel(this.getInputStream());    }    long contentLength() throws IOException;    long lastModified() throws IOException;    Resource createRelative(String var1) throws IOException;    @Nullable    String getFilename();    String getDescription();}

    我们虽然没有直接使用 Resource 类,但是我们经常使用它的子类,例如:

    4011e69f1b0fab9a2c1d260ae25264b5.png

    Spring 的初始化也采用了策略模式,不同的类型的类采用不 同的初始化策略。首先有一个 InstantiationStrategy 接口,我们来看一下源码:

    public interface InstantiationStrategy {    Object instantiate(RootBeanDefinition var1, @Nullable String var2, BeanFactory var3) throws BeansException;    Object instantiate(RootBeanDefinition var1, @Nullable String var2, BeanFactory var3, Constructor> var4, Object... var5) throws BeansException;    Object instantiate(RootBeanDefinition var1, @Nullable String var2, BeanFactory var3, @Nullable Object var4, Method var5, Object... var6) throws BeansException;}

    顶层的策略抽象非常简单,但是它下面有两种策略 SimpleInstantiationStrategyCglibSubclassingInstantiationStrategy,我们看一下类图:

    e8f93aab08bd3054adbf84f038aeb6cf.png

    打开类图我们还发现 CglibSubclassingInstantiationStrategy 策略类还继承了 SimpleInstantiationStrategy类,说明在实际应用中多种策略之间还可以继承使用。

    三、策略模式的优缺点

    优点:

    • 策略模式符合开闭原则;
    • 避免使用多重条件转移语句,如 if...else...语句、switch 语句;
    • 使用策略模式可以提高算法的保密性和安全性。

    缺点:

    • 客户端必须知道所有的策略,并且自行决定使用哪一个策略类;
    • 代码中会产生非常多策略类,增加维护难度。
    展开全文
  • 记得先点web前端学习圈关注我哦~浅谈 JavaScript 中策略模式的使用:什么是设计模式什么是策略模式策略模式在 JavaScript 中的应用(使用策略模式封装百度AI识别调用)策略模式在 Vue 组件封装中的应用(使用策略模式...
  • 一、组合模式组合模式的定义与特点1 组合(Composite)模式的定义:有时又叫作部分-整体模式,它是一种将对象组合成树状的层次结构的模式,用来表示“部分-整体”的关系,使用户对单个对象和组合对象具有一致的访问...
  • 浅谈 JavaScript 中策略模式的使用:什么是设计模式什么是策略模式策略模式在 JavaScript 中的应用(使用策略模式封装百度AI识别调用)策略模式在 Vue 组件封装中的应用(使用策略模式封装Select组件)什么是设计模式...
  • 这是轻松追踪应用程序错误系列的第三篇博客。在这个系列中,我写了一个轻量级但是具有工业生产力的应用来定期扫描日志文件,查找错误。如果发现任何错误,则会生成并发出一个报告.如果你已经阅读了这个系列的第一篇...
  • 本文以实际项目应用“自己动手写工具--XSmartNote”为切入点,来讲述策略模式应用。很多初学者都有一种感觉,就是在看设计模式相关文章的时候,都看得懂,而且小Demo也是手到擒来,但是就是不知道该怎么用在实际的...
  • 例如上图是电商系统中经典的业务场景,订单-仓储-物流的服务模式,不同服务提供不同的应用场景,服务间存在通信机制,以此实现服务的高可用。2、隔离思想分布式的架构体系中,涉及一个根本思想逻辑:隔离;服务...
  • 朱迪,中国社会科学院社会学...著有《品味与物质欲望:当代中产阶层的消费模式》(2013)《消费社会学研究的一个理论框架》(2012)《北上广还是二线?——大学毕业生就业区域流向分析》(2015)等。[图源:sociology.c...
  • 撰稿 | 流苏编辑 | 图图2020年9月23日,京东安全与安在联合举办的“京麒沙龙第三期之智能风控”专场直播活动圆满落幕。...图的风控场景业务应用”。以下为直播实录。网易严选风控业务负责人苏跃:风控...
  • 策略模式应用场景

    2021-03-03 16:57:25
    go设计模式 - 策略模式策略模式应用场景策略模式的用处总结 策略模式应用场景 策略模式的主要目的是将行为与具体的算法或者实现进行分开。如:条条大路通罗马。采取不同的路线或者出行方式虽然会出现不同的算法...
  • 策略模式使用场景

    千次阅读 2016-04-04 10:24:15
    今天看了Defonds专栏里面对于策略模式应用场景的描述,感觉比较合适,节选了有关的内容 策略模式的定义  策略模式是应用最普遍的设计模式之一。Gof 把策略模式归类到对象行为型模式,《设计模式:可复用面向对象...
  • 策略模式应用场景 1、假如系统中有很多类,而他们的区别仅仅在于他们的行为不同。 2、一个系统需要动态地在几种算法中选择一种。 用策略模式实现选择支付方式的业务场景 大家都知道经常会有优惠活动,优惠策略...
  • Java设计模式-策略模式实际应用场景 容错恢复机制 容错恢复机制是应用程序开发中非常常见的功能。那么什么是容错恢复呢?简单点说就是:程序运行的时候,正常情况下应该按照某种方式来做,如果按照某种方式来做发生...
  • 有个大神写的很好: 参考:设计模式学习笔记(四:策略模式) 参考:设计模式学习笔记(二:观察者模式) 参考:设计模式学习笔记-代理模式 参考:设计模式--装饰者模式与代理...一、策略模式: (1)解决场景:...
  • 掌握设计模式的理论知识不难,难就难在怎么在实际项目中具体应用,或者说哪个应用场景适合应用哪个设计模式。 本文将通过一个实际的营销策略实际来说明策略模式的具体适用场景 二、策略模式 策略模式(Strategy ...
  • 策略模式是行为型模式,作用是在许多行为中选择一种行为,关注的是行为的多样性。 但是如果对于一个实际的应用场景来说,怎么才能分得清什么创造与行为的区别啊……看网上的例子也是经常模糊不清,总感觉两者可以...
  • 容错恢复机制容错恢复机制是应用程序开发中非常常见的功能。那么什么是容错恢复呢?简单点说就是:程序运行的时候,正常情况下应该按照某种方式来做,如果按照某种方式来做发生错误的话,系统并不会崩溃,也不会就此...
  • 模板方法模式和策略模式应用场景浅析 一、定义 二、举个例子 1.杀鱼 2.做鱼 三、伪代码实现 1.杀鱼 1.1分析 1.2实现 2.做鱼 2.1分析 2.2实现 四、总结 最近闲下来整理一下模板方法模式和策略模式的区别和应用场景 ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,391
精华内容 556
关键字:

策略模式应用场景