【设计模式之美】策略模式实践:不同大小(采用不同的策略)文件进行排序

文章目录

  • 一. 问题与解决思路
  • 二. 代码实现与分析
    • 1. 业务代码逻辑的架子
    • 2. 代码重构:使用策略模式来解耦代码逻辑
  • 三. 进一步:满足开闭原则:使用注解或配置文件

设计原则和思想其实比设计模式更加普适和重要,掌握了代码的设计原则和思想,我们甚至可以自己创造出来新的设计模式。—《设计模式之美》王争

一. 问题与解决思路

假设有这样一个需求,希望写一个小程序,实现对一个文件进行排序的功能。如果文件涉及到的文件有不同规模的,如下

  • 10GB 大小,因为内存有限(比如只有 8GB 大小),我们没办法一次性加载文件中的所有数据到内存中,这个时候,我们就要利用外部排序算法(ing)了。
  • 100GB 大小,我们为了利用 CPU 多核的优势,可以在外部排序的基础之上进行优化,加入多线程并发排序的功能,这就有点类似“单机版”的 MapReduce。
  • 1TB 大小,即便是单机多线程排序,这也算很慢了。这个时候,我们可以使用真正的 MapReduce 框架,利用多机的处理能力,提高排序的效率。

很明显不同大小的文件需要使用不同的算法逻辑去实现,而不同的算法逻辑就可以使用策略模式将算法逻辑解耦,具体算法实现不是本文的重点,我们来看下策略模式是如何实现这个代码架构的。

 

二. 代码实现与分析

1. 业务代码逻辑的架子

如下逻辑架子,简单实现了算法的一些流程步骤,具体算法暂时不实现。

public class Sorter {private static final long GB = 1000 * 1000 * 1000;public void sortFile(String filePath) {// 省略校验逻辑File file = new File(filePath);long fileSize = file.length();if (fileSize < 6 * GB) { // [0, 6GB)quickSort(filePath);} else if (fileSize < 10 * GB) { // [6GB, 10GB)externalSort(filePath);} else if (fileSize < 100 * GB) { // [10GB, 100GB)concurrentExternalSort(filePath);} else { // [100GB, ~)mapreduceSort(filePath);}}private void quickSort(String filePath) {// 快速排序}private void externalSort(String filePath) {// 外部排序}private void concurrentExternalSort(String filePath) {// 多线程外部排序}private void mapreduceSort(String filePath) {// 利用MapReduce多机排序}
}public class SortingTool {public static void main(String[] args) {Sorter sorter = new Sorter();sorter.sortFile(args[0]);}
}

 

2. 代码重构:使用策略模式来解耦代码逻辑

分析下需求和代码结构:

  • 策略定义与创建: 每种排序类都是无状态的,我们没必要在每次使用的时候,都重新创建一个新的对象。所以,我们可以使用工厂模式对对象的创建进行封装。
  • 策略的使用:这里的 if-else 逻辑分支不多、也不复杂,这样写完全没问题。当然你也可以使用查表法

接下来我们分别实现这几个方面

策略定义:

public interface ISortAlg {void sort(String filePath);
}public class QuickSort implements ISortAlg {@Overridepublic void sort(String filePath) {//...}
}...
ExternalSort implements ISortAlg
ConcurrentExternalSort implements ISortAlg
MapReduceSort implements ISortAlg 
...

 

策略实现:

public class SortAlgFactory {private static final Map<String, ISortAlg> algs = new HashMap<>();static {algs.put("QuickSort", new QuickSort());algs.put("ExternalSort", new ExternalSort());algs.put("ConcurrentExternalSort", new ConcurrentExternalSort());algs.put("MapReduceSort", new MapReduceSort());}public static ISortAlg getSortAlg(String type) {if (type == null || type.isEmpty()) {throw new IllegalArgumentException("type should not be empty.");}return algs.get(type);}
}
。。。
}

策略使用:

public class Sorter {private static final long GB = 1000 * 1000 * 1000;private static final List<AlgRange> algs = new ArrayList<>();static {algs.add(new AlgRange(0, 6*GB, SortAlgFactory.getSortAlg("QuickSort")));algs.add(new AlgRange(6*GB, 10*GB, SortAlgFactory.getSortAlg("ExternalSort")));algs.add(new AlgRange(10*GB, 100*GB, SortAlgFactory.getSortAlg("ConcurrentExternalSort")));algs.add(new AlgRange(100*GB, Long.MAX_VALUE, SortAlgFactory.getSortAlg("MapReduceSort")));}public void sortFile(String filePath) {// 省略校验逻辑File file = new File(filePath);long fileSize = file.length();ISortAlg sortAlg = null;for (AlgRange algRange : algs) {//如果处于某一个范围就选定这个,并结束循环if (algRange.inRange(fileSize)) {sortAlg = algRange.getAlg();break;}}sortAlg.sort(filePath);}private static class AlgRange {private long start;private long end;//AlgRange封装策略类与策略类型,这样就能去除if elseprivate ISortAlg alg;public AlgRange(long start, long end, ISortAlg alg) {this.start = start;this.end = end;this.alg = alg;}public ISortAlg getAlg() {return alg;}public boolean inRange(long size) {return size >= start && size < end;}}
}

 

三. 进一步:满足开闭原则:使用注解或配置文件

即便这样,当我们添加新的排序算法的时候,还是需要修改策略实现与选择的代码,并不完全符合开闭原则。

有什么办法让我们完全满足开闭原则呢?

对于 Java 语言来说,我们可以通过反射来避免对策略工厂类的修改。具体是这么做的:

  1. 我们通过一个配置文件或者自定义的 annotation 来标注都有哪些策略类;策略工厂类读取配置文件或者搜索被 annotation 标注的策略类,然后通过反射动态地加载这些策略类、创建策略对象;
  2. 当我们新添加一个策略的时候,只需要将这个新添加的策略类添加到配置文件或者用 annotation 标注即可。

当添加新的排序算法时,我们只需要改动配置文件即可,不需要改动代码。

 
 
 
参考:《设计模式之美》–王争

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

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

相关文章

如何注册微信公众号

如何注册微信公众号 如何注册一个微信公众号 &#x1f60a;&#x1f4f1;摘要引言正文内容1. 准备工作内容定位和受众群体公众号名称和头像 2. 网页注册流程第一步&#xff1a;访问微信公众平台第二步&#xff1a;选择账户注册类型第三步&#xff1a;填写基本信息第四步&#x…

ChatGLM2-6B 运行代码解读(二)

目录 一、cli_demo.py 解读 3.1 transformers 3.2 AutoTokenizer.from_pretrained 函数 3.3 AutoModel.from_pretrained 函数 3.4 model.eval() 函数

树型结构数据存储实践

很多业务场景会遇到树形结构的数据&#xff0c;如公司的人员职级树、行政区划树等。 使用类似MySQL的数据库进行存储&#xff0c;需要将树形结构&#xff08;二维&#xff09;存储到行格式&#xff08;一维&#xff09;的db中。 本文介绍了树型结构数据存储的三种方式&#xf…

华为HCIP Datacom H12-821 卷27

1.单选题 IS-IS中地址的总长度最少为多少Byte? A、20 B、2 C、16 D、8 正确答案:D 解析: 地址总长度最长为13+6+1=20Byte,最短为1+6+1=8Byte。 2.多选题 下面哪些路由协议支持通过命令配置发布

Java视频点播网站

作者介绍&#xff1a;计算机专业研究生&#xff0c;现企业打工人&#xff0c;从事Java全栈开发 主要内容&#xff1a;技术学习笔记、Java实战项目、项目问题解决记录、AI、简历模板、简历指导、技术交流、论文交流&#xff08;SCI论文两篇&#xff09; 上点关注下点赞 生活越过…

[FreeRTOS 基础知识] 事件组 概念

文章目录 事件组 定义事件组 基本原理 事件组 定义 在实时操作系统&#xff08;RTOS&#xff09;中&#xff0c;事件组是一种用于任务间通信和同步的机制。事件组允许多个任务等待一个或多个事件的组合&#xff0c;当这些事件的组合满足特定条件时&#xff0c;任务可以被唤醒。…

vb.netcad二开自学笔记2:认识vs编辑器

认识一下宇宙第一编辑器的界面图标含义还是很重要的&#xff0c;否则都不知道面对的是什么还怎么继续&#xff1f; 一、VS编辑器中常见的图标的含义 变量 长方体&#xff1a;变量 局部变量 两个矩形块&#xff1a;枚举 预定义的枚举 紫色立方体&#xff1a;方法 橙色树状结构…

优惠券管理系统设计

优惠券管理系统的设计旨在高效地创建、分发、追踪和分析优惠券的使用情况&#xff0c;以促进销售、增强客户忠诚度并提升营销活动的效果。以下是设计一个高效优惠券管理系统的关键要素和步骤&#xff1a; 1. 需求分析与目标设定 明确业务目标&#xff1a;确定优惠券使用的具体…

shell中不常见的命令

** iconv 功能&#xff1a;将文件内容字符集转成其他字符集 常用选项&#xff1a; -l 列出所有已知的字符集 -f 原始文本编码 -t 输出编码 -o 输出到文件 -s 关闭警告 将文件内容转换 UTF8&#xff1a; # iconv -f gbk -t utf8 old.txt -o new.txt 将 csv 文件转换 GBK&…

直播电商如何实现分账?

随着直播电商的兴起&#xff0c;越来越多的企业开始选择通过直播平台来推广自己的产品。而在直播电商中&#xff0c;主播是非常重要的一环&#xff0c;他们的表现直接影响到产品的销售情况。因此&#xff0c;如何与主播合作&#xff0c;让他们更加积极地推广产品&#xff0c;成…

UE4_材质_材质节点_Fresnel

学习笔记&#xff0c;不喜勿喷&#xff0c;侵权立删&#xff0c;祝愿生活越来越好&#xff01; 一、问题导入 在创建电影或过场动画时&#xff0c;你常常需要想办法更好地突显角色或场景的轮廓。这时你需要用到一种光照技术&#xff0c;称为边沿光照或边缘光照&#xff0c;它的…

Android动态设置系统音量最大值

产品需求 通过设定最大音量限制大屏声音输出&#xff0c;设置完后需要立即生效且需要记忆&#xff1b;以10%为递增状态&#xff0c;设置最大音量后&#xff0c;无论最大音量调节至多少百分比&#xff0c;音量条始终显示为100%&#xff08;比如最大音量设置为80%&#xff0c;即…

Threejs环境、透视相机、坐标系、光源

文章目录 如何引入threejsnpm方式script方式script module方式 基本流程与坐标摄像机Geometry(几何体)和Material(材质)光源 如何引入threejs 对于很多刚刚上手threejs的朋友&#xff0c;可能第一步引入threejs就出问题了&#xff0c; 明明已经导入了&#xff0c;就是这样问题…

【搭建Nacos服务】centos7 docker从0搭建Nacos服务

前言 本次搭建基于阿里云服务器系统为&#xff08;CentOS7 Linux&#xff09;、Nacos&#xff08;2.0.3&#xff09;、Docker version 26.1.4 本次搭建基于一个新的云服务器 安装java yum install -y java-1.8.0-openjdk.x86_64安装驱动以及gcc等前置需要的命令 yum install …

【nvm管理nodejs版本,切换node指定版本】

nvm管理nodejs版本 nvm管理nodejs版本主要功能使用 nvm nvm管理nodejs版本 nvm&#xff08;Node Version Manager&#xff09;顾名思义node版本管理器&#xff0c;无须去node管网下载很多node安装程序;用于管理多个 Node.js 版本的工具。它允许你在同一台机器上同时安装和管理…

Appium启动APP时报错Security exception: Permission Denial

报错内容Security exception: Permission Denial: starting Intent 直接通过am命令尝试也是同样的报错 查阅资料了解到&#xff1a;android:exported | App quality | Android Developers exported属性默认false&#xff0c;所以android:exported"false"修改为t…

Eureka服务降级策略配置指南:确保微服务架构的弹性

在微服务架构中&#xff0c;服务之间的依赖关系非常普遍。当一个服务不可用时&#xff0c;可能会导致整个系统的故障。服务降级是一种提高系统可用性的策略&#xff0c;它允许服务在某些条件不满足时提供降级的响应。Eureka作为Netflix开源的服务发现框架&#xff0c;可以与Hys…

c与c++ 常用的字符与字符串处理的接口介绍:

在 C 和 C 中&#xff0c;字符与字符串处理是非常基础和常见的操作。以下是一些常用的接口和函数&#xff1a; C 字符串处理接口 字符处理&#xff1a; char 类型&#xff1a;C 中用于表示单个字符。 字符串是以 null 结尾的字符数组&#xff0c;通常使用 char[] 或 char* 表…

基于java+springboot+vue实现的图书商城管理系统(文末源码+Lw)283

摘 要 现代经济快节奏发展以及不断完善升级的信息化技术&#xff0c;让传统数据信息的管理升级为软件存储&#xff0c;归纳&#xff0c;集中处理数据信息的管理方式。本图书商城管理系统就是在这样的大环境下诞生&#xff0c;其可以帮助管理者在短时间内处理完毕庞大的数据信…

rpm包下载

内网无法下载、选择外网的一台机器下载rpm包 下载后上传rpm包 1、创建下载目录 mkdir /data/asap/test 2、下载能留存包的工具 sudo yum install yum-utils -y 报错就是环境问题没下载成功&#xff0c;我换了个环境正常的机器就可以了 3、下载rpm包到指定目录/data/asa…