Java 设计模式——策略模式

news2024/9/22 11:34:52

目录

  • 1.概述
  • 2.结构
  • 3.案例实现
  • 4.优缺点
  • 5.使用场景
  • 6.JDK 源码解析——Comparator

1.概述

(1)先看下面的图片,我们去旅游选择出行模式有很多种,可以骑自行车、可以坐汽车、可以坐火车、可以坐飞机。
在这里插入图片描述

(2)策略模式:该模式定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的变化不会影响使用算法的用户。策略模式属于对象行为模式,它通过对算法进行封装,把使用算法的责任和算法的实现分割开来,并委派给不同的对象对这些算法进行管理

2.结构

策略模式的主要角色如下:

  • 抽象策略 (Strategy):定义了策略类的公共接口或抽象类,用于封装具体的算法。
  • 具体策略 (Concrete Strategy):实现了抽象策略定义的接口或抽象类,定义了具体的算法实现。
  • 环境 (Context) 类:维护一个对策略对象的引用,提供一个方法供客户端调用,该方法根据需要调用相应的策略对象的算法。

3.案例实现

【例】促销活动
一家百货公司在定年度的促销活动。针对不同的节日(春节、中秋节等)推出不同的促销活动,由促销员将促销活动展示给客户。类图如下:

在这里插入图片描述

具体实现代码如下:
Strategy.java(抽象策略),定义百货公司所有促销活动的共同接口

package com.itheima.patterns.behaviorpattern.strategy;

public interface Strategy {
    void show();
}

每个节日具体的促销活动:
StrategyA.java(具体策略)

package com.itheima.patterns.behaviorpattern.strategy;

//具体策略类
public class StrategyA implements Strategy{
    @Override
    public void show() {
        System.out.println("买一送一");
    }
}

StrategyB.java(具体策略)

package com.itheima.patterns.behaviorpattern.strategy;

public class StrategyB implements Strategy{
    @Override
    public void show() {
        System.out.println("满200元减50元");
    }
}

StrategyC.java(具体策略)

package com.itheima.patterns.behaviorpattern.strategy;

public class StrategyC implements Strategy{
    @Override
    public void show() {
        System.out.println("满1000元加一元换购任意200元以下商品");
    }
}

SalesMan.java(环境类),定义环境角色,用于连接上下文,即把促销活动推销给客户,这里可以理解为销售员

package com.itheima.patterns.behaviorpattern.strategy;

public class SalesMan {
    //聚合策略类对象
    private Strategy strategy;
    
    public SalesMan(Strategy strategy){
        this.strategy = strategy;
    }
    
    //向客户展示促销活动
    public void salesManShow(){
        strategy.show();
    }
    
    public void setStrategy(Strategy strategy) {
        this.strategy = strategy;
    }
}

Client.java

package com.itheima.patterns.behaviorpattern.strategy;

public class Client {
    public static void main(String[] args) {
        //春节来了,使用春节促销活动
        SalesMan salesMan = new SalesMan(new StrategyA());
        //展示促销活动
        salesMan.salesManShow();
    
        System.out.println("==============");
        //中秋节到了,使用中秋节的促销活动
        salesMan.setStrategy(new StrategyB());
        //展示促销活动
        salesMan.salesManShow();
    
        System.out.println("==============");
        //圣诞节到了,使用圣诞节的促销活动
        salesMan.setStrategy(new StrategyC());
        //展示促销活动
        salesMan.salesManShow();
    }
}

4.优缺点

(1)优点

  • 策略类之间可以自由切换,由于策略类都实现同一个接口,所以使它们之间可以自由切换。
  • 易于扩展,增加一个新的策略只需要添加一个具体的策略类即可,基本不需要改变原有的代码,符合“开闭原则“。
  • 避免使用多重条件选择语句,充分体现面向对象设计思想。

(2)缺点

  • 客户端必须知道所有的策略类,并自行决定使用哪一个策略类。
  • 策略模式将造成产生很多策略类,可能会增加系统的复杂度,可以通过使用享元模式在一定程度上减少对象的数量。

5.使用场景

(1)当一个系统中存在多种算法或策略,并且需要在运行时动态地选择其中一种算法来完成任务时,策略模式可以很好地解决这个问题。例如,一个网站的登录方式有多种(账号密码登录、微信扫码登录等),此时可以根据用户的登录方式来进行认证
(2)一个类定义了多种行为,并且这些行为在这个类的操作中以多个条件语句的形式出现,可将每个条件分支移入它们各自的策略类中以代替这些条件语句。
(3)系统中各算法彼此完全独立,且要求对客户隐藏具体算法的实现细节时。
(4)系统要求使用算法的客户不应该知道其操作的数据时,可使用策略模式来隐藏与算法相关的数据结构。
(5)多个类只区别在表现行为不同,可以使用策略模式,在运行时动态选择具体要执行的行为。

6.JDK 源码解析——Comparator

(1)Comparator 中就使用到了策略模式。在 Arrays 类中有一个 sort() 方法,其代码如下:

public class Arrays{
	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);
		}
	}
	
	//...
}

(2)Arrays 就是一个环境角色类,这个 sort 方法可以传一个新策略让 Arrays 根据这个策略来进行排序。就比如下面的测试类。

public class Demo {
	public static void main(String[] args) {
		Integer[] data = {12, 2, 3, 2, 4, 5, 1};
		//实现降序排序
		Arrays.sort(data, new Comparator<Integer>() {
			public int compare(Integer o1, Integer o2) {
				return o2 - o1;
			}
		});
		System.out.println(Arrays.toString(data)); // [12, 5, 4, 3, 2, 2, 1]
	}
}

(3)这里我们在调用 Arrays 的 sort 方法时,第二个参数传递的是 Comparator 接口的子实现类对象。所以 Comparator 充当的是抽象策略角色,而具体的子实现类充当的是具体策略角色。环境角色类 (Arrays) 应该持有抽象策略的引用来调用。那么,Arrays 类的 sort 方法到底有没有使用 Comparator 子实现类中的 compare() 方法吗?让我们继续查看 TimSort 类的 sort() 方法,代码如下:

class TimSort<T> {
	static <T> void sort(T[] a, int lo, int hi, Comparator<? super T> c,T[] work, int workBase, int workLen) {
		assert c != null && a != null && lo >= 0 && lo <= hi && hi <= a.length;
		int nRemaining = hi - lo;
		if (nRemaining < 2)
			return; // Arrays of size 0 and 1 are always sorted
		// If array is small, do a "mini-TimSort" with no merges
		if (nRemaining < MIN_MERGE) {
			int initRunLen = countRunAndMakeAscending(a, lo, hi, c);
			binarySort(a, lo, hi, lo + initRunLen, c);
			return;
		}
		//...
	}
	
	private static <T> int countRunAndMakeAscending(T[] a, int lo, int hi,Comparator<? super T> c) {
		assert lo < hi;
		int runHi = lo + 1;
		if (runHi == hi)
			return 1;
		// Find end of run, and reverse range if descending
		if (c.compare(a[runHi++], a[lo]) < 0) { // Descending
			while (runHi < hi && c.compare(a[runHi], a[runHi - 1]) < 0)
				runHi++;
			reverseRange(a, lo, runHi);
		} else { // Ascending
			while (runHi < hi && c.compare(a[runHi], a[runHi - 1]) >= 0)
				runHi++;
		}
		return runHi - lo;
	}
}

上面的代码中最终会进入到 countRunAndMakeAscending() 这个方法中。我们可以看见,只用了 compare 方法,所以在调用 Arrays.sort() 方法只传具体 compare 重写方法的类对象就行,这也是 Comparator 接口中必须要子类实现的一个方法。

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

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

相关文章

【计算机网络】网络编程套接字(二)

文章目录 网络编程套接字&#xff08;二&#xff09;简单TCP服务器实现创建套接字服务器绑定服务器监听服务器接收连接服务器处理请求 简单TCP客户端实现创建套接字客户端发起连接客户端发起请求 服务器简单测试服务器简单测评多进程版TCP服务器捕捉SIGCHLD信号孙子进程提供服务…

如何使用ai绘画生成器创造出惊人的作品

你们了解如何文字生图片吗&#xff0c;简单的来说就是用文字描述出图片的样子&#xff0c;然后通过ai技术生成出来。 这种技术现在应用的很广泛&#xff0c;在各行各业都有在使用&#xff0c;平常我无聊的时候&#xff0c;就靠它为我打磨时间了。 不过现在的ai绘画工具有很多…

51 # 二叉搜索树的实现

实现二叉搜索树 比如我们有数组&#xff1a; [10, 8, 19, 6, 15, 22, 20]需要把数组转为二叉搜索树&#xff0c;效果如下&#xff1a; // 节点 class Node {constructor(element, parent) {this.element element; // 存的数据this.parent parent; // 父节点this.left null…

订座排队小程序/H5/app【附源码】

订座排队小程序/H5/app 可看 应用效果 首页 答题活动规则 选择门店暂无门店 订座订座 订座成功 用户反馈订座 关于后端

抖音seo源码/源码开发部署环境配置及流程分享

抖音seo源码开发部署环境配置&#xff1a; 要配置抖音seo源码开发环境&#xff0c;您需要完成以下步骤&#xff1a; 安装 Python&#xff1a;请在官网下载 Python 并进行安装。 安装 Django&#xff1a;请在命令行中输入以下命令来安装 Django&#xff1a; pip install django…

双周连载 | 暗网分析报告Part 1:他们无法保护你

双周连载 | 暗网分析报告Part 1&#xff1a;他们无法保护你 暗网&#xff0c;一个人们讳莫如深的世界&#xff0c;一个充斥着悄无声息的威胁&#xff0c;似乎永久地悬在我们每个人的头顶。这是一个罪恶的不法之地&#xff0c;时刻散播着未知的可怕力量&#xff0c;这便是那些隐…

使用 Elastic 修剪传入日志体量

作者&#xff1a;Carly Richmond 记录日志还是不记录日志&#xff1f; 一直是软件工程师仍在努力解决的一个难题&#xff0c;这对站点可靠性工程&#xff08;SRE&#xff09;同事来说是不利的。 开发人员并不总能正确了解他们在应用程序中捕获的警告和错误的级别或上下文&#…

01-webpack的理解,解决了什么问题

一、背景 Webpack 最初的目标是实现前端项目的模块化&#xff0c;旨在更高效地管理和维护项目中的每一个资源 模块化 最早的时候&#xff0c;我们会通过文件划分的形式实现模块化&#xff0c;也就是将每个功能及其相关状态数据各自单独放到不同的JS 文件中 约定每个文件是一…

dubbo监控中心dubbo-admin老版本(dubbo-ops)使用

1、dubbo分组 在dubbo中&#xff0c;可以指定group&#xff0c;如下&#xff1a; <dubbo:registry protocol"zookeeper" address"${dubbo.registry.address}" client"curator" group"${dubbo.registry.group}" /> 使用proper…

差值结构的排斥能

( A, B )---3*30*2---( 1, 0 )( 0, 1 ) 让网络的输入只有3个节点&#xff0c;AB训练集各由5张二值化的图片组成&#xff0c;让差值结构中有7个1, 行分布是0&#xff0c;1&#xff0c;2&#xff0c;2&#xff0c;2列分布是1&#xff0c;2&#xff0c;4.统计迭代次数并排序。 得…

今年面试真的很难...

大家好&#xff0c;最近有不少小伙伴在后台留言&#xff0c;今年面试实在是太卷了&#xff0c;不知道从何下手&#xff01; 不论是跳槽涨薪&#xff0c;还是学习提升&#xff01;先给自己定一个小目标&#xff0c;然后再朝着目标去努力就完事儿了&#xff01; 为了帮大家节约…

应用层:文件传输协议FTP

1.应用层&#xff1a;文件传输协议FTP 笔记来源&#xff1a; 湖科大教书匠&#xff1a;应用层概述 湖科大教书匠&#xff1a;文件传输协议FTP 声明&#xff1a;该学习笔记来自湖科大教书匠&#xff0c;笔记仅做学习参考 将某台计算机中的文件通过网络传送到可能相距很远的另一…

猿人学web刷题1

1.第一题 js混淆源码乱码 - 猿人学 : url 时间戳加密 右键遇到反调试&#xff0c;参考前面的文章 过反调试 2.ast解混淆 首页1.js 拿到 function oo0O0, 在<script>标签里面, 无法调试&#xff0c;分析自己提取出来&#xff0c;或则hook替换 加密逻辑存在于window.a中&a…

Linux分布式应用 Zabbix监控软件实例:监控NGINX的性能

实例&#xff1a;监控NGINX的性能 1.安装&#xff08;确认是否有状态统计模块&#xff09; yum -y install nginx 2.更改NGINX配置文件添加状态统计站点 vim /etc/nginx/nginx.conf #主配置文件 vim /etc/nginx/conf.d/default.conf #子配置文件location /nginx_status {stub_…

Docker 常用指令集合,更换镜像(Ubantu)

1.更换镜像 先进入root用户 cat /etc/docker/daemon.json 查看有没有镜像创建目录,创建并编辑damon,json文件 mkdir -p /etc/docker vim /etc/docker/daemon.json# 填写内容 {"registry-mirrors": ["https://h5rurp1p.mirror.aliyuncs.com"] } 重新启…

在工作中保持情绪稳定:策略与实践

一、引言 近期发生的新闻热点再度引发公众对稳定情绪和心理健康的关注。在快节奏的现代生活中&#xff0c;我们常常面临各种压力和挑战&#xff0c;这可能会导致情绪波动&#xff0c;甚至影响我们的工作和生活质量。有时候我们遇到的最大的敌人&#xff0c;不是运气也不是能力…

Kubernetes的介绍(组件、Pod)和 安装使用

目录 Kubernetes是什么&#xff1f; 跟Kubernetes相似的软件&#xff1a; k8s里有哪些组件&#xff1f; 官方网站&#xff1a;Kubernetes 组件 | Kubernetes master上的Control Plane组件 什么是组件&#xff1f; Pod是什么呢&#xff1f; 1、kube-apiserver …

Oracle的CentOS安装

1.CentOS环境 阿里云轻量型服务器 2核-4g内存-80G系统盘 2.Oracle下载 Oracle下载 Oracle 数据库免费 CPU 限制 Oracle 数据库免费版自动将自身限制为两个内核进行处理。例如&#xff0c;在具有 2 个双核 CPU&#xff08;四个核&#xff09;的计算机上&#xff0c;如果大量…

【在线文件管理】响应式文件管理AngularJS

目录 1.功能展示截图 2.实现代码 2.1HTML页面代码 2.2后台代码 2.2.1项目结构 2.2.2项目代码 其他问题 1.功能展示截图 项目中需要用到文件管理来自由控制文件的上传、下载、删除等&#xff0c;就想到做一个简单的在线文件管理功能。 支持在线编辑&#xff1a; 2.实现代…

Java基础---SPI

目录 典型回答 从面向接口编程说起 接口位于调用方所在的包中 接口位于实现方所在的包中 注意 如何定义一个SPI SPI的实现原理 SPI的应用场景 典型回答 Java中区分 API 和 SPI&#xff0c;通俗的讲&#xff1a;API 和 SPI 都是相对的概念&#xff0c;他们的差别只在语义…