设计模式六:策略模式

1、策略模式

策略模式定义了一系列的算法,并将每一个算法封装起来,使每个算法可以相互替代,使算法本身和使用算法的客户端分割开来,相互独立。

策略模式的角色:

  • 策略接口角色IStrategy:用来约束一系列具体的策略算法,策略上下文角色ConcreteStrategy使用此策略接口来调用具体的策略所实现的算法。

  • 具体策略实现角色ConcreteStrategy:具体的策略实现,即具体的算法实现。

  • 策略上下文角色StrategyContext:策略上下文,负责和具体的策略实现交互,通常策略上下文对象会持有一个真正的策略实现对象,策略上下文还可以让具体的策略实现从其中获取相关数据,回调策略上下文对象的方法。
    在这里插入图片描述

//策略接口
public interface IStrategy {//定义的抽象算法方法 来约束具体的算法实现方法public void algorithmMethod();
}// 具体的策略实现1
public class ConcreteStrategy1 implements IStrategy {//具体的算法实现@Overridepublic void algorithmMethod() {System.out.println("this is ConcreteStrategy1 method...");}
}// 具体的策略实现2
public class ConcreteStrategy2 implements IStrategy {//具体的算法实现@Overridepublic void algorithmMethod() {System.out.println("this is ConcreteStrategy2 method...");}
}/*** 策略上下文*/
public class StrategyContext {//持有一个策略实现的引用private IStrategy strategy;//使用构造器注入具体的策略类public StrategyContext(IStrategy strategy) {this.strategy = strategy;}public void contextMethod(){//调用策略实现的方法strategy.algorithmMethod();}
}//外部客户端
public class Client {public static void main(String[] args) {//1.创建具体测策略实现IStrategy strategy = new ConcreteStrategy2();//2.在创建策略上下文的同时,将具体的策略实现对象注入到策略上下文当中StrategyContext ctx = new StrategyContext(strategy);//3.调用上下文对象的方法来完成对具体策略实现的回调ctx.contextMethod();}
}

示例

现实生活中我们到商场买东西的时候,卖场往往根据不同的客户制定不同的报价策略,比如针对新客户不打折扣,针对老客户打9折,针对VIP客户打8折…

//一般做法
//商城管理
import java.math.BigDecimal;public class QuoteManager {public BigDecimal quote(BigDecimal originalPrice, String customType){if ("新客户".equals(customType)) {return this.quoteNewCustomer(originalPrice);}else if ("老客户".equals(customType)) {return this.quoteOldCustomer(originalPrice);}else if("VIP客户".equals(customType)){return this.quoteVIPCustomer(originalPrice);}//其他人员都是原价return originalPrice;}/*** 对VIP客户的报价算法* @param originalPrice 原价* @return 折后价*/private BigDecimal quoteVIPCustomer(BigDecimal originalPrice) {System.out.println("恭喜!VIP客户打8折");originalPrice = originalPrice.multiply(new BigDecimal(0.8)).setScale(2,BigDecimal.ROUND_HALF_UP);return originalPrice;}/*** 对老客户的报价算法* @param originalPrice 原价* @return 折后价*/private BigDecimal quoteOldCustomer(BigDecimal originalPrice) {System.out.println("恭喜!老客户打9折");originalPrice = originalPrice.multiply(new BigDecimal(0.9)).setScale(2,BigDecimal.ROUND_HALF_UP);return originalPrice;}/*** 对新客户的报价算法* @param originalPrice 原价* @return 折后价*/private BigDecimal quoteNewCustomer(BigDecimal originalPrice) {System.out.println("抱歉!新客户没有折扣!");return originalPrice;}}//客户
public class Client {public static void main(String[] args) {QuoteManager quoteManager = new QuoteManager();BigDecimal price = quoteManager.quote(BigDecimal.valueOf(780.09), "VIP客户");System.out.println("支付: " + price + "元");}
}

这种方式下:

1、当我们新增一个客户类型的时候,首先要添加一个该种客户类型的报价算法方法,然后再quote方法中再加一个else if的分支,是不是感觉很是麻烦呢?而且这也违反了设计原则之一的开闭原则(open-closed-principle)

2.我们经常会面临这样的情况,不同的时期使用不同的报价规则,比如在各个节假日举行的各种促销活动时、商场店庆时往往都有普遍的折扣,但是促销时间一旦过去,报价就要回到正常价格上来。按照上面的代码我们就得修改if else里面的代码很是麻烦


策略模式:
在这里插入图片描述

//报价策略接口
public interface IQuoteStrategy {//获取折后价的价格BigDecimal getPrice(BigDecimal originalPrice);
}//新客户的报价策略实现类
public class NewCustomerQuoteStrategy implements IQuoteStrategy {@Overridepublic BigDecimal getPrice(BigDecimal originalPrice) {System.out.println("抱歉!新客户没有折扣!");return originalPrice;}
}//老客户的报价策略实现
public class OldCustomerQuoteStrategy implements IQuoteStrategy {@Overridepublic BigDecimal getPrice(BigDecimal originalPrice) {System.out.println("恭喜!老客户享有9折优惠!");originalPrice = originalPrice.multiply(new BigDecimal(0.9)).setScale(2,BigDecimal.ROUND_HALF_UP);return originalPrice;}
}//VIP客户的报价策略实现
public class VIPCustomerQuoteStrategy implements IQuoteStrategy {@Overridepublic BigDecimal getPrice(BigDecimal originalPrice) {System.out.println("恭喜!VIP客户享有8折优惠!");originalPrice = originalPrice.multiply(new BigDecimal(0.8)).setScale(2,BigDecimal.ROUND_HALF_UP);return originalPrice;}
}
//报价上下文角色
public class QuoteContext {//持有一个具体的报价策略private IQuoteStrategy quoteStrategy;//注入报价策略public QuoteContext(IQuoteStrategy quoteStrategy){this.quoteStrategy = quoteStrategy;}//回调具体报价策略的方法public BigDecimal getPrice(BigDecimal originalPrice){return quoteStrategy.getPrice(originalPrice);}
}
//外部客户端
public class Client {public static void main(String[] args) {//1.创建老客户的报价策略IQuoteStrategy oldQuoteStrategy = new OldCustomerQuoteStrategy();//2.创建报价上下文对象,并设置具体的报价策略QuoteContext quoteContext = new QuoteContext(oldQuoteStrategy);//3.调用报价上下文的方法BigDecimal price = quoteContext.getPrice(new BigDecimal(100));System.out.println("折扣价为:" +price);}
}

策略模式下,新增客户类型或者修改原有客户类型,只需要新增一个实现类或者修改对应类即可

2、策略模式和工厂模式区别

策略模式和工厂模式在模式结构上,两者很相似:

在这里插入图片描述

区别:

用途和关注点

  • 策略模式属于行为类设计模式,关注对行为的封装,client注重的是结果

  • 工厂模式属于创建类设计模式,关注对象的创建,client注重的是获取类对象

解决问题:

  • 策略模式是为了解决的是策略的切换与扩展,更简洁的说是定义策略族,分别封装起来,让他们之间可以相互替换,策略模式让策略的变化独立于使用策略的客户。

  • 工厂模式是创建型的设计模式,它接受指令,创建出符合要求的实例;它主要解决的是资源的统一分发,将对象的创建完全独立出来,让对象的创建和具体的使用客户无关。主要应用在多数据库选择,类库文件加载等。

  • 工厂相当于黑盒子,策略相当于白盒子;

3、策略模式和模板模式区别

策略模式(Strategy Pattern)和模板方法模式(Template Method Pattern)是两种不同的行为类设计模式,它们在实现上有一些明显的区别

在这里插入图片描述
在这里插入图片描述

  1. 目的和应用场景:
    • 策略模式: 主要用于定义一系列的算法,将每个算法封装起来,并使它们可以互相替换。客户端可以选择不同的策略对象,以达到不同的行为。
    • 模板方法模式: 主要用于定义一个算法的骨架,将一些步骤的实现延迟到子类。父类中定义了模板方法,该方法中的某些步骤的具体实现由子类决定。
  2. 关注点:
    • 策略模式: 关注的是算法的替换和客户端的选择。
    • 模板方法模式: 关注的是算法的骨架和具体步骤的延迟实现。
  3. 组成结构:
    • 策略模式: 主要包含环境类(Context)、策略接口(Strategy)和具体策略实现类(ConcreteStrategy)。
    • 模板方法模式: 主要包含抽象类(AbstractClass)、模板方法(Template Method)和具体实现步骤的方法。
  4. 灵活性和扩展性:
    • 策略模式: 策略可以相对独立地变化,客户端可以灵活地选择和切换不同的策略。
    • 模板方法模式: 算法的骨架是固定的,但某些步骤的具体实现可以在子类中进行扩展。
  5. 调用方式:
    • 策略模式: 客户端通常主动选择并设置具体的策略对象。
    • 模板方法模式: 算法的执行是由父类的模板方法触发的,子类可以通过扩展来影响某些步骤的具体实现。

策略模式关注的是定义一系列算法并使它们可以互相替换,而模板方法模式关注的是定义一个算法的骨架,将某些步骤的实现交给子类决定。它们分别适用于不同的设计需求和场景。

4、spring中的策略模式

4.1 ThreadPoolExecutor

在多线程编程中,我们经常使用线程池来管理线程,以减缓线程频繁的创建和销毁带来的资源的浪费,在创建线程池的时候,经常使用一个工厂类来创建线程池Executors,实际上Executors的内部使用的是类ThreadPoolExecutor.它有一个最终的构造函数如下:

public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler) {if (corePoolSize < 0 ||maximumPoolSize <= 0 ||maximumPoolSize < corePoolSize ||keepAliveTime < 0)throw new IllegalArgumentException();if (workQueue == null || threadFactory == null || handler == null)throw new NullPointerException();this.corePoolSize = corePoolSize;this.maximumPoolSize = maximumPoolSize;this.workQueue = workQueue;this.keepAliveTime = unit.toNanos(keepAliveTime);this.threadFactory = threadFactory;this.handler = handler;
}

这里面是线程池的重要参数:

  • corePoolSize:线程池中的核心线程数量,即使这些线程没有任务干,也不会将其销毁。

  • maximumPoolSize:线程池中的最多能够创建的线程数量。

  • keepAliveTime:当线程池中的线程数量大于corePoolSize时,多余的线程等待新任务的最长时间。

  • unit:keepAliveTime的时间单位。

  • workQueue:在线程池中的线程还没有还得及执行任务之前,保存任务的队列(当线程池中的线程都有任务在执行的时候,仍然有任务不断的提交过来,这些任务保存在workQueue队列中)。

  • threadFactory:创建线程池中线程的工厂。

  • handler:当线程池中没有多余的线程来执行任务,并且保存任务的多列也满了(指的是有界队列),对仍在提交给线程池的任务的处理策略。

在这里插入图片描述

RejectedExecutionHandler 是一个策略接口,用在当线程池中没有多余的线程来执行任务,并且保存任务的多列也满了(指的是有界队列),对仍在提交给线程池的任务的处理策略。

public interface RejectedExecutionHandler {/***当ThreadPoolExecutor的execut方法调用时,并且ThreadPoolExecutor不能接受一个任务Task时,该方法就有可能被调用。* 不能接受一个任务Task的原因:有可能是没有多余的线程来处理,有可能是workqueue队列中没有多余的位置来存放该任务,该方法有可能抛出一个未受检的异常RejectedExecutionException* @param r the runnable task requested to be executed* @param executor the executor attempting to execute this task* @throws RejectedExecutionException if there is no remedy*/void rejectedExecution(Runnable r, ThreadPoolExecutor executor);
}

该策略接口有四个实现类

AbortPolicy:该策略是直接将提交的任务抛弃掉,并抛出RejectedExecutionException异常。

java复制代码/*** A handler for rejected tasks that throws a* <tt>RejectedExecutionException</tt>.*/public static class AbortPolicy implements RejectedExecutionHandler {/*** Creates an <tt>AbortPolicy</tt>.*/public AbortPolicy() { }/*** Always throws RejectedExecutionException.* @param r the runnable task requested to be executed* @param e the executor attempting to execute this task* @throws RejectedExecutionException always.*/public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {throw new RejectedExecutionException();}}

DiscardPolicy:该策略也是将任务抛弃掉(对于提交的任务不管不问,什么也不做),不过并不抛出异常。

java复制代码/*** A handler for rejected tasks that silently discards the* rejected task.*/public static class DiscardPolicy implements RejectedExecutionHandler {/*** Creates a <tt>DiscardPolicy</tt>.*/public DiscardPolicy() { }/*** Does nothing, which has the effect of discarding task r.* @param r the runnable task requested to be executed* @param e the executor attempting to execute this task*/public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {}}

DiscardOldestPolicy:该策略是当执行器未关闭时,从任务队列workQueue中取出第一个任务,并抛弃这第一个任务,进而有空间存储刚刚提交的任务。使用该策略要特别小心,因为它会直接抛弃之前的任务。

java复制代码/*** A handler for rejected tasks that discards the oldest unhandled* request and then retries <tt>execute</tt>, unless the executor* is shut down, in which case the task is discarded.*/public static class DiscardOldestPolicy implements RejectedExecutionHandler {/*** Creates a <tt>DiscardOldestPolicy</tt> for the given executor.*/public DiscardOldestPolicy() { }/*** Obtains and ignores the next task that the executor* would otherwise execute, if one is immediately available,* and then retries execution of task r, unless the executor* is shut down, in which case task r is instead discarded.* @param r the runnable task requested to be executed* @param e the executor attempting to execute this task*/public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {if (!e.isShutdown()) {e.getQueue().poll();e.execute(r);}}}

CallerRunsPolicy:该策略并没有抛弃任何的任务,由于线程池中已经没有了多余的线程来分配该任务,该策略是在当前线程(调用者线程)中直接执行该任务。

java复制代码/*** A handler for rejected tasks that runs the rejected task* directly in the calling thread of the {@code execute} method,* unless the executor has been shut down, in which case the task* is discarded.*/public static class CallerRunsPolicy implements RejectedExecutionHandler {/*** Creates a {@code CallerRunsPolicy}.*/public CallerRunsPolicy() { }/*** Executes task r in the caller's thread, unless the executor* has been shut down, in which case the task is discarded.** @param r the runnable task requested to be executed* @param e the executor attempting to execute this task*/public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {if (!e.isShutdown()) {r.run();}}}

类ThreadPoolExecutor中持有一个RejectedExecutionHandler接口的引用,以便在构造函数中可以由外部客户端自己制定具体的策略并注入。下面看一下其类图:
在这里插入图片描述

创建线程池的时候,根据具体场景,选择不同的拒绝策略

4.2 InstantiationStrategy

spring中bean的实例化过程如下:

InstantiationStrategy提供了实例化bean的不同策略:

public interface InstantiationStrategy {/*** 默认的构造方法* @param bd* @param beanName* @param owner* @return* @throws BeansException*/
Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner)throws BeansException;
/*** 指定构造方法* @param bd* @param beanName* @param owner* @param ctor* @param args* @return* @throws BeansException*/Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner,Constructor<?> ctor, Object... args) throws BeansException;
/*** 通过指定的工厂方法* @param bd* @param beanName* @param owner* @param factoryBean* @param factoryMethod* @param args* @return* @throws BeansException*/
Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner,@Nullable Object factoryBean, Method factoryMethod, Object... args)throws BeansException;

看具体的方法有子类实现并执行,类图如下:

使用策略模式,实例化bean的时候,根据传参的不同,选择不同的实例化策略

具体InstantiationStrategy实现及执行流程,搜索[相关文章](

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

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

相关文章

第一次开机开机动画结束后闪白屏

开机动画结束会闪下白屏&#xff0c;再进入launcher 思路 : 分析下从开机动画结束到launcher起来之间的流程步骤 从ZygoteInit.java开始分析 &#xff1a; SystemServer起来后会启动一些核心服务 attachApplication方法中主要创建了Application和Activity 接下里RootActivityC…

快速搭建网站原型!8款网站原型软件推荐

现在&#xff0c;基于云的软件已经逐渐成为主流&#xff0c;网站原型设计工具也不例外。与桌面版本相比&#xff0c;在线原型工具具有独特的优势&#xff0c;无论您使用Linux&#xff0c;Mac 或者Windows&#xff0c;都不需要安装就可以使用这些工具。下面小编就为大家推荐8款非…

c++入门学习⑧——模板

目录 前言 基本介绍 什么是模板&#xff1f; 作用 特点 分类 函数模板 语法 使用方式 注意事项 函数模板和普通函数区别 普通函数和函数模板的调用规则 局限性 类模板 语法 类模板的成员函数创建时机 类模板实例化对象 类模板实例化对象做函数参数 类模板成…

普中51单片机学习(LCD1602)

LCD1602 1602液晶也叫1602字符型液晶&#xff0c;它是一种专门用来显示字母、数字、符号的点阵型液晶模块。它是由若干个5x7或者5x10的点阵字符位组成&#xff0c;每个点阵字符位都可以用显示一个字符&#xff0c;每位之间有一个点距的间隔&#xff0c;每行之间也有间隔&#…

代码随想录算法训练营29期|day60 任务以及具体安排

第九章 动态规划part17 647. 回文子串 class Solution {public int countSubstrings(String s) {char[] chars s.toCharArray();int len chars.length;boolean[][] dp new boolean[len][len];int result 0;for (int i len - 1; i > 0; i--) {for (int j i; j < le…

C++ 高频考点

1. C/C内存有哪几种类型&#xff1f; C中&#xff0c;内存分为5个区&#xff1a;堆(malloc)、栈(如局部变量、函数参数)、程序代码区&#xff08;存放二进制代码&#xff09;、全局/静态存储区&#xff08;全局变量、static变量&#xff09;和常量存储区&#xff08;常量&…

YOLOv9来了!实时目标检测新SOTA

先上一把网上的测试效果对比: YOLOv9架构图 速度论文 代码&#xff1a;GitHub - WongKinYiu/yolov9: Implementation of paper - YOLOv9: Learning What You Want to Learn Using Programmable Gradient Information 论文&#xff1a;[2402.13616] YOLOv9: Learning What You…

HTTP/HTTPS协议

什么是HTTP协议 HTTP被称为超文本传输协议(里面不仅仅可以是字符串,还可以是图片,特殊字符等),这是一种应用非常广泛的应用层协议. HTTP协议诞生于1991年,现在是最主流使用的一种应用层协议.它从诞生到现在为止迭代了多个版本. 但目前最主流使用的还是HTTP1.1和HTTP2.0. HTTP协…

Java数据结构---初识集合框架

目录 一、什么是集合框架 二、集合框架的重要性 三、背后涉及的数据结构及算法 1.什么是数据结构 2.容器背后对应的数据结构 3.相关的Java知识 4.什么是算法 一、什么是集合框架 Java 集合框架 Java Collection Framework &#xff0c;又被称为容器 container &#xff0…

Unity编辑器内工程文件重命名|Project视图文件名修改

Unity编辑器内文件重命名 前言大项内容一使用方法代码展示 总结 前言 本文代码可以一键更改Project视图的文件名字 在当前文件名的状态下增加一段字符区分文件。 大项内容一 功能是因为在给其他人导入项目资源时有重复的资源的时候&#xff0c;资源会产生覆盖的问题。所以直…

家用办公主机需要多少钱?推荐主机选购攻略!!

1.头部部分 本文将长期保持更新&#xff0c;您可以保存并随时查看。 过去推荐的 主持人推荐 以下家庭办公主机均采用性能强劲的12/13代i5配置&#xff0c;可以完美应对日常办公、平面设计、编辑等使用场景。 主机价格在4K左右&#xff0c;自带显示器&#xff0c;并附有三到…

xss靶场实战(xss-labs-master靶场)

xss-labs-master靶场链接&#xff1a;https://pan.baidu.com/s/1X_uZLF3CWw2Cmt3UnZ1bTw?pwdgk9c 提取码&#xff1a;gk9c xss-labs level 1 修改 url 地址中的name<script>alert(1)</script>&#xff0c;便可以通关 level 2 在搜索框中输入的 JS 代码无法执行 …

用户体验设计师如何在 2024抢占先机?

01. 严峻的经济形势和就业市场 我们生活在一个通货膨胀的时代。就从超市抓几个苹果、卷心菜、鸡蛋&#xff0c;看看价格吧&#xff01;我不得不多次检查收据&#xff0c;因为我简直不敢相信。外出就餐费用上涨了 10-20%&#xff0c;现在 Spotify 和 YouTube 要求收取更高的订阅…

STM32通用定时器输入捕获

通用定时器输入捕获部分框图介绍 通用定时器输入捕获脉宽测量原理 要测量脉宽的高电平的时间&#xff1a;t2-t1&#xff08;脉宽下降沿时间点-脉宽上升沿时间点&#xff09; 假设&#xff1a;递增计数模式 ARR&#xff1a;自动重装载寄存器的值 CCRx1&#xff1a;t1时间点CCRx…

Google炸场,推出开“放”可商用的大语言模型Gemma!超级轻量,个人电脑即可运行

与OpenAI的封闭式大型模型不同&#xff0c;谷歌、Meta等科技巨头正致力于开发开源模型&#xff0c;以期实现技术上的快速追赶。 介绍 2月21日&#xff0c;谷歌发布了其最新一代的开源AI模型——Gemma&#xff08;https://ai.google.dev/gemma&#xff09;&#xff0c;这是一个…

2024.2.29 模拟实现 RabbitMQ —— 项目展示

目录 项目介绍 核心功能 核心技术 演示直接交换机 演示扇出交换机 演示主题交换机 项目介绍 此处我们模拟 RabbitMQ 实现了一个消息队列服务器 核心功能 提供了 虚拟主机、交换机、队列、绑定、消息 概念的管理九大核心 API 创建队列、销毁队列、创建交换机、销毁交换机、…

基于JSP的毕业设计选题系统的设计与实现

基于JSP的毕业设计选题系统的设计与实现 (源代码论文) A. 项目简介 毕业设计选题系统就是能够使学生通过互联网完成毕业设计课题的选定&#xff0c;它采用Web方式&#xff0c;同时适用于局域网和Internet&#xff0c;它要实现审核&#xff0c;权限管理&#xff0c;邮件通知…

Python中的atexit模块:优雅地处理程序退出

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站AI学习网站。 目录 前言 atexit模块概述 atexit模块的基本用法 示例代码&#xff1a;文件操作时的应用场景 典型应用场景 1 资源释放…

云里物里轻薄系列电子价签,如何革新零售?

云里物里的DS轻薄系列电子价签&#xff0c;凭借轻巧外观和强劲性能&#xff0c;为零售行业提供了更便捷的商品改价方案。这不仅是对纸质价标的替代&#xff0c;更以其安全性和可持续发展性&#xff0c;实现对零售行业的效率升级&#xff0c;让商家们轻松迎接数字化时代的挑战&a…

【Vue3】学习watch监视:深入了解Vue3响应式系统的核心功能(下)

&#x1f497;&#x1f497;&#x1f497;欢迎来到我的博客&#xff0c;你将找到有关如何使用技术解决问题的文章&#xff0c;也会找到某个技术的学习路线。无论你是何种职业&#xff0c;我都希望我的博客对你有所帮助。最后不要忘记订阅我的博客以获取最新文章&#xff0c;也欢…