策略模式

  • 概述
  • 案例
  • 应用分析
  • 题外话 Arrays.asList()方法不能add的小坑
  • 总结

概述

    策略模式(Strategy Pattern)基本介绍
    1、在策略模式中,定义算法族,分别封装起来。让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户端。是一种行为型模式。
    2、策略模式体现了几个设计原则:第一、把变化的代码从不变的代码中分离出来;第二、针对接口变成而不是具体的类(定义了策略接口);第三、多用组合/聚合,少用继承(客户端通过组合方式使用策略模式)。
    3、在策略模式中,定义一些独立的类来封装不同的算法,每一个类封装一种具体的算法,在这里,每一个封装算法的类就是一种策略,为了保证这些策略使用时具有一致性,一般情况下一提供一个抽象的策略类来做规则定义,每种算法对应一个具体的算法类。
    4、策略模式的主要目的是将算法的定义与使用分开,也就是即哪个算法的行为和环境分开,将算法定义在专门的策略类中,每一个策略类封装了一种实现算法,使用算法的环境类针对抽象策略类进行编程,符合依赖倒转原则,在出现新的算法类时,只需要增加一个新的具体策略类即可,
    类图描述
    
    角色分析
    1、Context:环境类,使用算法的角色,它在实现某个方法时,可能使用多种策略,在环境类中聚合一个抽象策略类(拥有一个抽象策略类的成员变量),用于定义采用的策略。
    2、Strategy,抽象策略类,它为所支持的算法声明了抽象方法,是所有具体策略类的父类,环境类通过抽象策略中声明的方法,在运行时调用具体策略类中实现的算法。
    3、ConcreteStrategy,具体策略类,它实现在抽象策略类中声明的算法,在运行时,使用具体策略类中的某个方法实现业务逻辑的处理。

案例

    景区门票折扣方案,不同的用户给予不同的优惠策略,比如年费会员免费入场、学生半价入场、儿童半价入场,成人95折入场。

package com.example.pattern.strategy;import lombok.Getter;
import lombok.Setter;/*** 策略模式** @author zjt* @date 2021-01-07*/interface Discount { // 抽象策略类double calculate(Double price);
}@Getter
@Setter
public class Ticket { // 环境类 门票private double price;private Discount discount;public Ticket(double price) {this.price = price;}public double getPrice() {return discount.calculate(price);}}class StudentDiscount implements Discount { // 具体策略类 学生票@Overridepublic double calculate(Double price) {return price * 0.5;}}class ChildDiscount implements Discount { // 具体策略类 儿童票@Overridepublic double calculate(Double price) {return price * 0.5;}}class MemberDiscount implements Discount { // 具体策略类 会员票@Overridepublic double calculate(Double price) {return 0;}}class AdultDiscount implements Discount { // 具体策略类 会员票@Overridepublic double calculate(Double price) {return price * 0.95;}}class Client {public static void main(String[] args) {double price = 100;Ticket ticket = new Ticket(price);System.out.println("原价为:" + price);ticket.setDiscount(new AdultDiscount());price = ticket.getPrice();System.out.println("原价为:" + price);}
}

应用分析

public static void main(String[] args) {Integer[] arr = {1, 4, 2, 3};// 实现升序排序 反回-1放左边,返回1放右边// 说明// 1、实现了Comparator接口(类似与策略对象) 匿名类对象// 2、函数式接口实现 new Comparator<Integer>() {} 就是实现了策略接口的对象// 3、public int compare(Integer o1, Integer o2) {} 指定具体的处理方式Comparator<Integer> comparator = new Comparator<Integer>() {@Overridepublic int compare(Integer o1, Integer o2) {if (o1 > o2) {return 1;} else {return -1;}}};Arrays.sort(arr, comparator);// 调用方法的源码//public static <T> void sort(T[] a, Comparator<? super T> c) {//        if (c == null) {//            sort(a); // 默认方法//        } else {//            if (LegacyMergeSort.userRequested)//                legacyMergeSort(a, c); // 使用策略对象//            else//                TimSort.sort(a, 0, a.length, c, null, 0, 0);//        }//    }System.out.println(Arrays.toString(arr));// 实现方式2 lambda 表达式 实现策略模式Arrays.sort(arr, (o1, o2) -> {if (o1 < o2) {return 1;} else {return -1;}});System.out.println(Arrays.toString(arr));}

题外话 Arrays.asList()方法不能add的小坑

    提到了Arrays工具类,前几天我在单元测试的时候发现了个问题,Arrays.asList()增加元素报错,顺便多提一点关于Arrays.asList()方法使用小坑,先看一段代码

    public static void main(String[] args) {Integer[] irr = {1, 2, 3, 4};List<Integer> list = Arrays.asList(irr);list.add(5);}// 运行报错// Exception in thread "main" java.lang.UnsupportedOperationException// at java.util.AbstractList.add(AbstractList.java:148)// at java.util.AbstractList.add(AbstractList.java:108)// 报错所在代码 是在AbstractList中抛出的错误,//  public boolean add(E e) {//    add(size(), e);//    return true;// }// public void add(int index, E element) {//    throw new UnsupportedOperationException();// }

    因为我自己没有仔细看源码,第一眼只看到了下面的代码,下意识的认为是返回的java.util.ArrayList,还困扰了我一会儿,java.util.ArrayList源码中找了一下,发现确实实现了新增的各种方法,最后才发现问题原因,在代码中备注一下。

     @SafeVarargs@SuppressWarnings("varargs")public static <T> List<T> asList(T... a) {// 这个ArrayList是Arrays工具类的内部静态类,它所实现的方法有限,// 并没有实现 add() remove() 方法。需要额外操作还需要进一步转换// 比如 List<Integer> list = new ArrayList<>(Arrays.asList(arr)) ;return new ArrayList<>(a); }

    内部静态类代码就在asList() 方法的下面一行。记录一下,以后看代码还是要认真的多看看。

private static class ArrayList<E> extends AbstractList<E>implements RandomAccess, java.io.Serializable{...... // 内容省略 实现的大致方法有 size(),toArray(),get(int index),// set(int index, E element),indexOf(Object o),contains(Object o)// sort(Comparator<? super E> c),replaceAll(UnaryOperator<E> operator)// forEach(Consumer<? super E> action)}

    好了,回归策略模式。

总结

    优点
    1、策略模式的关键是:分析项目中变化部分与不变的部分。
    2、策略模式提体现了,多用组合/聚合,少用继承,用行为的组合,而不是行为的继承。
    3、策略模式体现了“开闭原则”的支持,用户可以在不修改原有系统的基础上选择算法或者行为,也可以灵活的增加新的算法或行为。
    4、策略模式提供了管理算法族的方案,定义一个算法或者行为族,恰当的使用避免重复代码。提供了算法复用机制,不同的环境类可以方便的复用这些算法。同时也避免了大量的条件判断语句。
    5、策略模式提供了替换继承关系的办法:将算法封装在独立抽象策略类中,也就是可以独立与环境类去改变它,使得易于切换、理解和扩展。
    缺点:
    1、每增加一个策略,就要增加一个类,过多的策略会导致类的数目庞大。同时策略模式将造成系统产生很多具体策略类,任何细小的变化都将导致系统要增加一个新的具体策略类。
    2、客户端必须知道所有的策略类,并自行决定使用哪一个策略类。这就意味着客户端必须理解这些算法的区别,以便适时选择恰当的算法。换言之,策略模式只适用于客户端知道所有的算法或行为的情况。

设计模式学习第二十二节 策略模式相关推荐

  1. C++设计模式学习笔记:策略模式

    C++设计模式学习笔记:策略模式 策略模式设计商场促销 1.策略模式介绍 2.商场收银系统策略模式实现 3.策略模式与工厂模式结合 3.策略模式与工厂模式对比 策略模式设计商场促销 1.策略模式介绍 ...

  2. 设计模式学习笔记--Strategy 策略模式

    所谓策略模式(Strategy Pattern),就是将策略 (算法) 封装为一个对象,易于相互替换,如同 USB 设备一样可即插即用:如果将策略.具体的算法和行为,编码在某个类或客户程序内部,将导至 ...

  3. 设计模式学习笔记(5) - 策略模式

    我一直觉得策略模式是一个很好玩的模式,让我们用游戏来了解一下. 举一个武侠的例子: 小说中大侠一般都有两样功夫: 第一:武功 第二:轻功 说到武功,让我们看看天龙八部里的三位高手的绝招: 萧峰:降龙十 ...

  4. Head First 设计模式学习笔记 一 策略模式

    策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换.策略模式让算法独立于使用它的客户而独立变化. 这是一个比较简单的模式,核心的思想就是将应用中的变化之处独立出来,这样就可以 ...

  5. 设计模式初学者系列-策略模式 -------为什么总是继承

    设计模式初学者系列-策略模式                                                 -------为什么总是继承 模板方法的延续 这篇稿子是基于我的前一篇模板 ...

  6. 设计模式学习笔记——解释器(Interpreter)模式

    设计模式学习笔记--解释器(Interpreter)模式 @(设计模式)[设计模式, 解释器模式, Interpreter] 设计模式学习笔记解释器Interpreter模式 基本介绍 解释器案例 类 ...

  7. 设计模式学习笔记——命令(Command)模式

    设计模式学习笔记--命令(Command)模式 @(设计模式)[设计模式, 命令模式, command] 设计模式学习笔记命令Command模式 基本介绍 命令案例 类图 实现代码 Command接口 ...

  8. 设计模式学习笔记——代理(Proxy)模式

    设计模式学习笔记--代理(Proxy)模式 @(设计模式)[设计模式, 代理模式, proxy] 设计模式学习笔记代理Proxy模式 基本介绍 代理案例 类图 实现代码 Printable接口 Pri ...

  9. 设计模式学习笔记——状态(State)模式框架

    设计模式学习笔记--状态(State)模式框架 @(设计模式)[设计模式, 状态模式, State] 设计模式学习笔记状态State模式框架 基本介绍 状态案例 类图 实现代码 State接口 Day ...

最新文章

  1. 【JNI】javah使用(初步)
  2. 在mac上搭建octopress+github pages博客
  3. boost::math模块通过 Gauss 和 Gauss-Kronrod 正交的数值积分
  4. gnutls_handshake() failed: Illegal parameter
  5. finallshell使用_FinalShell使用---Xshell的良心国产软件
  6. 程序员心髓:移动应用API设计10大技巧
  7. python flask接收图像
  8. c语言程序设计二维数组ppt,C语言程序设计教程二维数组的应用优秀讲义.ppt
  9. String常用方法汇总
  10. 采用蒙特卡罗方法求解π值
  11. 手写数字识别及python实现
  12. 宝宝出生前需要准备的用品
  13. 基于arduino 开发 esp32 点亮ili9341屏幕
  14. local variable ‘x‘ referenced before assignment错误
  15. 膨胀腐蚀-OpenCL加速及kernel变成二进制文件
  16. 入选数据库顶会 VLDB:如何有效降低产品级内存数据库快照尾延迟?
  17. php写动物的属性,状物的记叙文类型——描述动物
  18. 什么是ALM?应用程序生命周期管理体系有哪些?
  19. java基础入门-04-【集合学生管理系统】
  20. wps表格序号怎么拉下去123456自动排列

热门文章

  1. 三菱伺服电机编码器ID修改器 支持三菱伺服电机J2/J2S/J3/J4系列所有电机
  2. java中的构造方法必须和类名相同,在Java中,关于构造方法,下列说法错误的是()A、构造方法的名称必须与类名相同B、构造方法可以...
  3. 超高频UHF RFID读写模块R200开发测试
  4. matlab摩托车刹车问题,安全骑行篇,摩托车刹车的基本知识与技巧!
  5. 环信客服SDK接入-----(一)
  6. Composer 基础使用
  7. 【26天高效学完Java编程】Day03:Java中的运算符与流程控制语句的基本概念与实操
  8. 优化工具 Neos Server
  9. word中利用题注实现公式图表自动编号及引用
  10. 双屏显示html vga,官方数据:一台计算机连接到两台显示器,双屏显示(VGA,HDMI)指南...