Java 设计模式中的策略模式详情
作者:我赢了算我输 发布时间:2023-08-06 03:45:11
策略模式的应用场景
策略模式是否要使用,取决于业务场景是否符合,有没有必要。
是否符合
如果业务是处于不同的场景时,采取不同的处理方式的话,就满足符合。 这里举几个业务栗子
如果今天我有1000块,那我就和朋友去游乐园玩、然后再去吃顿好吃的。如果有2000块的话,那我就先买一件好看的衣服,再和朋友去游乐园玩和吃好吃的商品收银业务,根据店中不同的活动分为正常收费、折扣收费、返利收费,每种收费的方式都不一样接收某安全设备的数据的时候,根据发过来不同的数据类型,执行设备报警、设备指定信息存储、设备本身状态信息修改等等的处理方式
这里上面三种业务场景都符合处于不同的场景时,采取不同的处理方式。
有没有必要
这里等描述完例子后,再回到这个问题
例子
这里采用上面第三个例子的业务分别进行编写不用策略模式和用策略模式的效果
不用策略模式
public class Main {
public static void main(String[] args) {
//设备报警
int deviceDataType=2;
DeviceData deviceData=new DeviceData();
deviceData.setName("安全设备A");
switch (deviceDataType){
case 1:
DeviceCallPolice(deviceData);
break;
case 2:
DeviceStatus(deviceData);
break;
default:
System.out.println("没有相关的策略");
break;
}
}
private static void DeviceStatus(DeviceData deviceData) {
System.out.println("正在修改"+deviceData.getName()+"状态为指定状态");
System.out.println("正在记录本次修改状态的时间信息");
System.out.println("根据修改后的状态判断是否要通知相关人");
}
private static void DeviceCallPolice(DeviceData deviceData) {
System.out.println("正在修改"+deviceData.getName()+"状态为报警状态");
System.out.println("正在记录本次报警时间信息");
System.out.println("设备相关人的短信通知");
}
}
效果:
使用策略模式
策略上下文
/**
*
* 安全设备处理上下文
*/
public class SafetyDeviceContext {
private DeviceHandlerStrategy deviceHandlerStrategy;
public SafetyDeviceContext(int dataType){
switch (dataType){
case 1:
deviceHandlerStrategy=new DeviceCallPoliceStrategy();
break;
case 2:
deviceHandlerStrategy=new DeviceStatusStrategy();
break;
default:
System.out.println("没有相关的策略");
break;
}
}
public void Handler(DeviceData deviceData){
deviceHandlerStrategy.handler(deviceData);
}
}
策略接口以及具体实现类
/**
* 设备处理策略
*/
public interface DeviceHandlerStrategy {
void handler(DeviceData deviceData);
}
/**
* 安全设备报警策略
*/
public class DeviceCallPoliceStrategy implements DeviceHandlerStrategy {
@Override
public void handler(DeviceData deviceData) {
System.out.println("正在修改设备状态为报警状态");
System.out.println("正在记录本次报警时间信息");
System.out.println("设备相关人的短信通知");
}
}
/**
* 设备状态修改策略
*/
public class DeviceStatusStrategy implements DeviceHandlerStrategy {
@Override
public void handler(DeviceData deviceData) {
System.out.println("正在修改设备状态为指定状态");
System.out.println("正在记录本次修改状态的时间信息");
System.out.println("根据修改后的状态判断是否要通知相关人");
}
}
Main类
public class Main {
public static void main(String[] args) {
//设备报警
int deviceDataType=2;
DeviceData deviceData=new DeviceData();
deviceData.setName("安全设备A");
SafetyDeviceContext safetyDeviceContext=new SafetyDeviceContext(deviceDataType);
safetyDeviceContext.handler(deviceData);
}
}
效果:
两种方式的不同
从简易和易懂程度来说,自然是不用策略模式好一些,用策略模式会导致类的增多,而且如果不懂策略模式的人去看代码时可读性不高。但使用策略模式胜于可扩展性和可维护性要强于不用策略模式的
使用策略模式在Main函数(使用方),消除了类型的swich判断,把这些判断放到了Context,使用方调用的时候,就不必过多关心设备处理策略,只需要把值传进Context就好了。
之前我很不理解策略模式为什么能避免使用多重条件判断,因为上面的写法只是把判断的代码移到了Context而已,要写的判断还是要写,直到我知道了采用字典和反射的方式时,就理解了,往下会说
如果后续我们要添加一种"设备定时报告信息"业务处理方式时,使用策略模式那种只需要在Context添加一层case(也可以不需要,可以用反射的方式)和添加一个策略的实现类就好了,而不用策略模式需要在原来使用方的代码添加一层case,可能会影响到原来的业务稳定性。
算法独立出来也方便我们使用单元测试进行测试
策略模式有没有必要使用?
策略模式的目的是减少具体的算法方式和使用方式之间的耦合,避免多重if判断,并让具体的算法方法尽可能独立。所以实现的时候,必然成本会高一些。因此我们要使用策略模式的时候,要确定该业务业务本身是否复杂,后续是不是会时不时添加其他的处理方式。
例如上面的第三个例子,设备对应的处理措施本身具备了复杂性,例如会涉及数据本身的处理、设备状态的更改、设备事件的触发等等处理措施,而且后续因业务的扩展,可能会添加不同的设备场景,不同的设备处理方式。像这种就可以考虑使用
如何避免Context类使用判断逻辑
可以采用字典+反射的方式进行避免
定义策略字典的全局变量
/**
* 设备处理策略方式字典
*/
public class DeviceStrategyDictionary {
public static HashMap<Integer,String> dictionary=new HashMap<>();
}
修改Context的代码
import java.util.HashMap;
/**
* 安全设备处理上下文
*/
public class SafetyDeviceContext {
private DeviceHandlerStrategy deviceHandlerStrategy;
public SafetyDeviceContext(int dataType) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
HashMap<Integer,String> dic= DeviceStrategyDictionary.dictionary;
if(!dic.containsKey(dataType)){
System.out.println("不包含该处理方式");
}
//使用反射的方式
Class<?> fz=Class.forName(dic.get(dataType));
deviceHandlerStrategy=(DeviceHandlerStrategy) fz.newInstance();
}
public void handler(DeviceData deviceData){
deviceHandlerStrategy.handler(deviceData);
}
}
Main方法
public class Main {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
//程序启动的时候
DeviceStrategyDictionary.dictionary.put(1,"DeviceCallPoliceStrategy");
DeviceStrategyDictionary.dictionary.put(2,"DeviceStatusStrategy");
//设备报警
int deviceDataType=2;
DeviceData deviceData=new DeviceData();
deviceData.setName("安全设备A");
SafetyDeviceContext safetyDeviceContext=new SafetyDeviceContext(deviceDataType);
safetyDeviceContext.handler(deviceData);
}
}
效果:
这样就可以避免使用if判断了,不过反射也有相关的性能消耗,这点也需要做权衡。
来源:https://blog.csdn.net/MDZZ666/article/details/122435141


猜你喜欢
- 带logo的二维码生成分为两步骤:首先根据输入的内容生成二维码图片,然后读取本地的logo图片,通过图片处理生成带logo的二维码。生成的二
- 本文实例为大家分享了Java NIO实现聊天功能的具体代码,供大家参考,具体内容如下server code : package c
- Spring2.5.6开发环境搭建的过程,供大家参考,具体内容如下1、jar 包准备:spring 2.5.6 的 jar 包(链接: ht
- 本文实例讲述了DevExpress中ChartControl实现柱状图演示的方法。分享给大家供大家参考。具体实现方法如下:关键代码如下:us
- 简单介绍下功能1.每隔一段时间(比如1分钟)在京东手机每日一秒杀页面提取产品(手机)链接。 http://sale.360buy.com/a
- struts2可以非常简单地使用FreeMarker模板作为视图技术,对于传统的jsp页面而言,FreeMarker是一个绝佳的替代方案。除
- XListview是一个非常受欢迎的下拉刷新控件,但是已经停止维护了。之前写过一篇XListview的使用介绍,用起来非常简单,这两天放假无
- 前言: 上图的报错信息相信大部分程序员都遇到过,奇怪的是虽然代码报错,但丝毫不影响程序的正常执行,也就是虽然编译器 IDEA 报错
- 本文实例讲述了Android使用ContentResolver搜索手机通讯录的方法。分享给大家供大家参考,具体如下:在这个程序中使用Cont
- 一、创建项目1.File->new->project;2.选择“Spring Initializr”,点击next;(jdk1.
- 之前学完了Java SE的知识,掌握了面向对象的编程思想,但对集合、多线程、反射、流的使用等内容理解的还不是很深入,打算再学习数据结构与算法
- 在面向对象设计原则中,要求"要依赖于抽象,不要依赖于具体", 这句话有很多人搞不懂。在这里谈谈我自己的理解。首先看看以下
- 在实际项目开发过程中,我们经常需要对某个对象或者某个集合中的元素进行排序,常用的两种方式是实现某个接口。常见的可以实现比较功能的接口有Com
- 场景Android中点击按钮启动另一个Activity以及Activity之间传值:https://www.jb51.net/article
- java中的final关键字详解final的作用随着所修饰的类型而不同  
- 概述RocketMQ 支持发送延迟消息,但不支持任意时间的延迟消息的设置,仅支持内置预设值的延迟时间间隔的延迟消息;预设值的延迟时间间隔为:
- java内存分析类加载的过程类的加载与ClassLoader的理解类的初始化package Collections;public class
- 一、题目描述题目:模拟一个简单的银行系统,使用两个不同的线程向同一个账户存钱。实现:使用特殊域变量volatile实现同步。二、解题思路创建
- /// <summary> /// 读写INI文件的类。
- 介绍和使用场景1)什么是消息一个事件,需要广播或者单独传递给某个接口2)为什么使用这个配置更新了,但是其他系统不知道是否更新SpringCl