策略模式

news2024/11/29 8:38:16

文章目录

    • 策略模板模式
      • 1.策略模式的本质
      • 2.何时选用策略模式
      • 3.优缺点
      • 4.策略模式的结构
      • 5.实现
        • 支付案例
        • 支付案例参数不一致问题

策略模板模式

1.策略模式的本质

策略模式的本质:分离算法,选择实现。

纵观整个策略模式实现的功能和设计,它的本质还是“分离算法,选择实现”,因为分离并封装了算法,才能够很容易地修改和添加算法;也能很容易地动态切换使用不同的算法,也就是动态选择一个算法来实现需要的功能。

2.何时选用策略模式

建议在以下情况中选用策略模式。

  • 出现有许多相关的类,仅仅是行为有差别的情况下,可以使用策略模式来使用多个行为中的一个来配置一个类的方法,实现算法动态切换。
  • 出现同一个算法,有很多不同实现的情况下,可以使用策略模式来把这些“不同的实现”实现成为一个算法的类层次。
  • 需要封装算法中,有与算法相关数据的情况下,可以使用策略模式来避免暴露这些跟算法相关的数据结构。
  • 出现抽象一个定义了很多行为的类,并且是通过多个if-else语句来选择这些行为的情况下,可以使用策略模式来代替这些条件语句。

3.优缺点

策略模式有以下优点。

  • 定义一系列算法
    策略模式的功能就是定义一系列算法,实现让这些算法可以相互替换。所以会为这一系列算法定义公共的接口,以约束一系列算法要实现的功能。如果这一系列算法具有公共功能,可以把策略接口实现成为抽象类,把这些公共功能实现到父类中,对于这个问题,前面讲了三种处理方法,这里就不再啰嗦了。

  • 避免多重条件语句
    根据前面的示例会发现,策略模式的一系列策略算法是平等的,是可以互换的,写在一起就是通过if-else结构来组织,如果此时具体的算法实现中又有条件语句,就构成了多重条件语句,使用策略模式能避免这样的多重条件语句。

  • 更好的扩展性
    在策略模式中扩展新的策略实现非常容易,只要增加新的策略实现类,然后在使用策略的地方选择使用这个新的策略实现就可以了。

策略模式有以下缺点。

  • 客户必须了解每种策略的不同
    策略模式也有缺点,比如让客户端来选择具体使用哪一个策略,这就需要客户了解所有的策略,还要了解各种策略的功能和不同,这样才能做出正确的选择,而且这样也暴露了策略的具体实现。

  • 增加了对象数目
    由于策略模式把每个具体的策略实现都单独封装成为类,如果备选的策略很多的话,那么对象的数目就会很可观。

  • 只适合扁平的算法结构
    策略模式的一系列算法地位是平等的,是可以相互替换的,事实上构成了一个扁平的算法结构,也就是在一个策略接口下,有多个平等的策略算法,就相当于兄弟算法。而且在运行时刻只有一个算法被使用,这就限制了算法使用的层级,使用的时候不能嵌套使用。
    对于出现需要嵌套使用多个算法的情况,比如折上折、折后返卷等业务的实现,需要组合或者是嵌套使用多个算法的情况,可以考虑使用装饰模式,或是变形的职责链,或是AOP等方式来实现。

4.策略模式的结构

在这里插入图片描述

  • Strategy:策略接口,用来约束一系列具体的策略算法。Context使用这个接口来调用具体的策略实现定义的算法。
  • ConcreteStrategy:具体的策略实现,也就是具体的算法实现。
  • Context:上下文,负责和具体的策略类交互。通常上下文会持有一个真正的策略实现,上下文还可以让具体的策略类来获取上下文的数据,甚至让具体的策略类来回调上下文的方法。

5.实现

支付案例

策略模式在工作中使用,一下就能想到的就是支付
模拟支付宝、微信、银联支付在这里插入图片描述

支付策略及其实现类

/**
 * @description:支付策略接口
 */
public interface PayStrategy {

    /**
     * 支付方法
     * @param orderId 订单id
     * @param amount 金额
     * @return
     */
    boolean pay(String orderId, Long amount);
}

/**
 * @description:微信支付策略
 */
public class WxPay implements PayStrategy{
    @Override
    public boolean pay(String orderId, Long amount) {
        System.out.println("调用微信支付策略-->订单id:"+orderId+" ,金额:"+amount);
        return true;
    }
}

/**
 * @description:支付宝支付策略
 */
public class ZfbPay implements PayStrategy{
    @Override
    public boolean pay(String orderId, Long amount) {
        System.out.println("调用支付宝支付策略-->订单id:"+orderId+" ,金额:"+amount);
        return true;
    }
}

/**
 * @description:银联支付策略
 */
public class YlPay implements PayStrategy{
    @Override
    public boolean pay(String orderId, Long amount) {
        System.out.println("调用银联支付策略-->订单id:"+orderId+" ,金额:"+amount);
        return true;
    }
}

支付上下文类

/**
 * @description:支付上下文类
 */
public class PayContext {

    /**
     * 订单id
     */
    private String orderId;
    /**
     * 金额
     */
    private Long amount;

    private PayStrategy payStrategy;

    public PayContext(String orderId, Long amount, PayStrategy payStrategy) {
        this.orderId = orderId;
        this.amount = amount;
        this.payStrategy = payStrategy;
    }

    public void payContext(){
        //调用具体策略的支付
        this.payStrategy.pay(orderId,amount);
    }
}

测试类

public class Client {

    public static void main(String[] args) {
        //微信支付
        new PayContext("10001",100L,new WxPay()).payContext();
        //支付宝支付
        new PayContext("10002",200L,new ZfbPay()).payContext();
        //银联支付
        new PayContext("10003",300L,new YlPay()).payContext();
    }
}

结果
在这里插入图片描述

看起来好像没有上下文什么事情,但是如果没有上下文,那么就需要客户端来直接与具体的策略交互,尤其是当需要提供一些公共功能,或者是相关状态存储的时候,会大大增加客户端使用的难度。因此,引入上下文还是很必要的,有了上下文,这些工作就由上下文来完成了,客户端只需要与上下文交互就可以了,这样会让整个设计模式更独立、更有整体性,也让客户端更简单。

支付案例参数不一致问题

实际使用各种支付时,各自的参数必然不一样,但是策略又是接口定义好的,那应该怎么办呢,这时介于客户端和策略方法中间的上下文类就发挥作用了,可以在这里面搞点小动作,把微信和支付宝用到的参数声明在这里,然后把自己传给策略类,不同的策略类需要什么参数就取什么参数

策略类及其实现类

/**
 * @description:支付策略接口
 */
public interface PayStrategy {

    /**
     * 支付方法
     * @return
     */
    boolean pay(PayContext payContext);
}

/**
 * @description:微信支付策略
 */
public class WxPay implements PayStrategy{
    @Override
    public boolean pay(PayContext payContext) {
        System.out.println("微信支付参数:"+payContext.getWxAppserect());
        System.out.println("调用微信支付策略-->订单id:"+payContext.getOrderId()+" ,金额:"+payContext.getAmount());
        return true;
    }
}

/**
 * @description:支付宝支付策略
 */
public class ZfbPay implements PayStrategy{
    @Override
    public boolean pay(PayContext payContext) {
        System.out.println("支付宝支付参数:"+payContext.getZfbId());
        System.out.println("调用支付宝支付策略-->订单id:"+payContext.getOrderId()+" ,金额:"+payContext.getAmount());
        return true;
    }
}

/**
 * @description:银联支付策略
 */
public class YlPay implements PayStrategy{
    @Override
    public boolean pay(PayContext payContext) {
        System.out.println("银联支付没有参数");
        System.out.println("调用银联支付策略-->订单id:"+payContext.getOrderId()+" ,金额:"+payContext.getAmount());
        return true;
    }
}

支付上下文类

/**
 * @description:支付上下文类
 */
@Getter
public class PayContext {

    /**
     * 订单id
     */
    private String orderId;
    /**
     * 金额
     */
    private Long amount;

    /**
     * 微信支付需要的参数
     */
    private String wxAppserect;
    /**
     * 支付宝支付需要的参数
     */
    private String zfbId;

    private PayStrategy payStrategy;

    public PayContext(String orderId, Long amount, String wxAppserect, String zfbId, PayStrategy payStrategy) {
        this.orderId = orderId;
        this.amount = amount;
        this.wxAppserect = wxAppserect;
        this.zfbId = zfbId;
        this.payStrategy = payStrategy;
    }

    public void payContext(){
        //调用具体策略的支付
        this.payStrategy.pay(this);
    }
}

测试类

public class Client {

    public static void main(String[] args) {
        //微信支付
        PayContext wxPay = new PayContext("10001", 100L, "wx123", "", new WxPay());
        wxPay.payContext();
        //支付宝支付
        PayContext zfbPay = new PayContext("10001", 100L, "", "zfb123", new ZfbPay());
        zfbPay.payContext();
        //银联支付
        PayContext ylPay = new PayContext("10001", 100L, "", "", new YlPay());
        ylPay.payContext();
    }
}

结果
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/85216.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

会议学习笔记

目录1.标注高效的核心思路:1.部分监督PSL2.自监督SSL魔方复原3.单一标注无标注2.深度通用学习DUL1.通用特征点检测2.通用MRI重建3.学习与知识融合如何把大数据小任务转换成小数据大任务 1.标注高效的核心思路: 以一变二:数据增广无中生有&am…

[附源码]Node.js计算机毕业设计电影院订票管理系统Express

项目运行 环境配置: Node.js最新版 Vscode Mysql5.7 HBuilderXNavicat11Vue。 项目技术: Express框架 Node.js Vue 等等组成,B/S模式 Vscode管理前后端分离等等。 环境需要 1.运行环境:最好是Nodejs最新版,我…

腾讯云年终:云服务器CVM、轻量应用服务器、GPU服务器价格表出炉!

腾讯云年终:云服务器CVM、轻量应用服务器、GPU服务器价格表出炉!小编来详细说下腾讯云年末限时回馈优惠活动云服务器配置及精准报价。 一、轻量应用服务器配置及优惠价格表 先说轻量应用服务器吧,因为轻量服务器太便宜了,轻量应用…

2022年山东最新交安安全员考试模拟题及答案

百分百题库提供交安安全员考试试题、交安安全员考试真题、交安安全员证考试题库等,提供在线做题刷题,在线模拟考试,助你考试轻松过关。 1.根据《建筑工程安全生产管理条例》,意外伤害保险费由施工单位支付。实行施工总承包的&…

C++四种强制类型转换

四种强制类型转换🏞️1. C语言中的类型转换🍁2. 为什么C需要四种类型转换🌠3. C强制类型转换📖3.1 static_cast📖3.2 reinterpret_cast📖3.3 const_cast📖3.4 dynamic_cast🌁4. RTTI…

JAVA毕业设计——基于Springboot+vue的疫情物资管理系统(源代码+数据库)

github代码地址 https://github.com/ynwynw/yiqingMaterial-public 毕业设计所有选题地址 https://github.com/ynwynw/allProject 基于Springbootvue的疫情物资管理系统(源代码数据库)047 一、系统介绍 本项目分为管理员与普通用户两种角色 管理员角色包含以下功能&#xf…

安全智能分析 技术背景

安全数据资产 统一管理DataOps,即 Data 和 Operations 的集成,于 2014 年首次提出。Gartner 将 DataOps 定义为“一种协作性的数据管理 实践,专注于改进组织内数据管道的通信、集成和自动化”[7]。DataOps 是一种面向流程的自动化方法&#x…

Linux安装多版本jdk(jdk8和jdk11)并可随意选择版本

一、官网下载不同版本的压缩包 官网下载地址 我这里下载的是jdk8和jdk11 二、将压缩包上传至服务器自定义的文件夹中 三、安装jdk8 1、解压压缩包 tar zxvf /install/java/jdk-8u202-linux-x64.tar.gz2、修改配置 vi /etc/profile在配置文件中添加以下配置(需修改成自己的…

购物车系统设计

1 主要功能 在用户选购商品时,下单前,暂存用户想购买的商品。 购物车对数据可靠性要求不高,性能也无特别要求,在整个电商系统是相对容易设计和实现的一个子系统。 购物车系统的主要功能: 把商品加入购物车&#xf…

3个月前被裁员了,心情跌落谷底,直到学姐给了我这份面试文档…

3个月前的某一天,正在愉快的打工,突然被喊去谈话,然后就被辞退了。。 加入了找工作的大军 然而,因为疫情,因为大专学历的我,找工作比以往都艰难了许多 很多,纯粹就是因为学历,都不…

如何在SpringBoot中异步请求和异步调用

一、SpringBoot中异步请求的使用 1、异步请求与同步请求 特点: 可以先释放容器分配给请求的线程与相关资源,减轻系统负担,释放了容器所分配线程的请求,其响应将被延后,可以在耗时处理完成(例如长时间的运…

Python运维之远程登录paramiko(VsCode)

Python运维之远程登录----paramiko paramiko是一个用于做远程控制的模块,使用该模块可以对远程服务器进行命令或文件操作,paramiko是用python语言写的一个模块,遵循SSH2协议,支持以加密和认证的方式,进行远程服务器的连接。 par…

CentOS7中搭建docker

一、配置要求 系统:Centos7 Linux 内核:官方建议 3.10 以上 1.查看当前的内核版本 uname -r 2.卸载旧版本(如果之前安装过的话) yum remove docker docker-common docker-selinux docker-engine 二、安装Docker 1.安装依赖包 …

【C进阶】第十篇——数据在内存中的存储

数据类型的介绍 类型的基本归类 整型在内存中的存储 原码,反码,补码 大小端介绍 什么是大小端 为什么有大端和小端? 判断当前机器的字节序 浮点型在内存中的存储 例题引入 浮点数的存 浮点数的取 浮点数的比较 数据类型的介绍 char //字符数据类型 short…

现代制造技术产品设计与柔性制造系统的预测和分析

产品设计要求的市场竞争是面向市场,以用户为中心。精益设计的工业设计方法,以及一系列新的设计概念,如制造、装配过程、检查和测量、环境中的绿色设计等,使设计与柔性制造系统的整个过程紧密结合,包括从产品概念设计到…

Flask全栈开发教程

Flask全栈开发教程 成为使用 Flask、Python、HTML、CSS 和 MongoDB 的全栈 Web 开发人员! 课程英文名:Web Developer Bootcamp with Flask and Python 此视频教程共5.0小时,中英双语字幕,画质清晰无水印,源码附件全…

[前端]白屏性能优化

[前端]白屏性能优化 业务面会问的东西 从打开一个页面,到页面的画面展示经历了怎样的过程? 简单来说,有以下几个主要步骤。 1、URL解析:判断浏览器输入的是搜索内容还是URL; 2、查找缓存:如果能找到缓存…

笔试强训(四十四)

目录一、选择题二、编程题2.1 驼峰命名法2.1.1 题目2.1.2 题解2.2 单词倒排2.2.2 题解一、选择题 (1)IPv4版本的因特网总共有多少有效A类地址网络(D) A.255 B.128 C.256 D.126 A类地址的网络号从0~127共128个,其中有两…

大数据 常用命令

常用shell命令 管道命令 查看/etc目录信息前5行信息 执行命令:ll /etc | head -5 查看/etc/profile文件后5行信息 执行命令:cat /etc/profile | tail -5 grep命令 抓取/etc目录下的python信息 执行命令:ll /etc | grep python 抓…

解决visual studio对不安全函数的警告

解决visual studio 对scanf ,strcpy,strcmp等函数的不安全警告报错 可以看到,编译器对scanf进行了报错,原因是说它不安全 编译器自己给了一种解决方案: 使用vs自带的 scanf_s,但是用这个函数,仅…