设计模式09-行为型模式2(状态模式/策略模式/Java)

5.4 状态模式

5.4.1 状态模式的定义

1.模式动机:有些对象具有多种状态,这些状态在某些情况下能够相互转换,对象在不同的状态下将具有不同的行为,将拥有状态的对象中和状态的行为分离。

2.模式定义:允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类

5.4.2 状态模式的结构与分析

image-20241103155911551

  • 环境类是指拥有状态的对象,可以对状态进行切换。
  • 抽象状态类是具体状态类的父类,各状态的行为封装在具体的状态类中,对于客户端而言,无须关心对象状态的转换以及对象所处的当前状态,无论对于何种状态的对象,客户端都可以一致处理。
public abstract class State {
    public abstract void handle();
}

public class ConcreteState extends State{
    @Override
    public void handle() {

    }
}

public class Context {
    private State state;
    public void setState(State state) {
        this.state = state;
    }
    public void request() {
        state.handle();
    }
}
5.4.3 状态模式的案例

在某论坛系统中,用户可以发表留言,发表留言将增加积分;用户也可以回复留言,回复留言也将增加积分;用户还可以下载文件,下载文件将扣除积分。该系统用户分为三个等级,分别是新手、高手和专家,这三个等级对应三种不同的状态,这三种状态分别定义如下:

(1) 如果积分小于100分,则为新手状态,用户可以发表留言、回复留言,但是不能下载文件。如果积分大于等于1000分,则转换为专家状态;如果积分大于等于100分,则转换为高手状态。

(2) 如果积分大于等于100分但小于1000分,则为高手状态,用户可以发表留言、回复留言,还可以下载文件,而且用户在发表留言时可以获取双倍积分。积分小于100分,转换为新手状态;如果积分大于等于1000分,则转换为专家状态;如果下载文件后积分小于0,则不能下载该文件。

(3) 如果积分大于等于1000分,则为专家状态,用户可以发表留言、回复留言和下载文件,用户除了在发表留言时可以获取双倍积分外,下载文件只扣除所需积分的一半。积分小于100分,则转换为新手状态;积分小于1000分,但大于等于100,则转换为高手状态;如果下载文件后积分小于0,则不能下载该文件。

image-20241103165818020

由于上述类图将point放到了状态方面,不好理解,下面代码将point放到ForumAccount中

image-20241103170254317

public class ForumAccount {
    private String username;
    private State state;
    private int point;

    public ForumAccount(String username) {
        this.username = username;
        this.point = 0;
        this.state = new PrimaryState(this);
    }

    public void writeNote(int point) {
        state.writeNote(point);
    }
    public void replyNote(int point) {
        state.replyNote(point);
    }
    public void downloadNote(int point) {
        state.downloadNote(point);
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public State getState() {
        System.out.println(state.getStateName());
        return state;
    }

    public void setState(State state) {
        this.state = state;
    }

    public int getPoint() {
        return point;
    }

    public void setPoint(int point) {
        this.point = point;
    }

}
public abstract class State {
    protected ForumAccount forumAccount;
    protected String stateName;
    public State(ForumAccount forumAccount) {
        this.forumAccount = forumAccount;
    }
    public abstract void writeNote(int point);
    public abstract void replyNote(int point);
    public abstract void downloadNote(int point);
    public void updateState(int point) {
        if (point >= 1000) {
            forumAccount.setState(new HighState(forumAccount));
        } else if (point >= 100) {
            forumAccount.setState(new MiddleState(forumAccount));
        } else {
            forumAccount.setState(new PrimaryState(forumAccount));
        }
    }
    public String getStateName() {
        return stateName;
    }
}
public class PrimaryState extends State{
    public PrimaryState(ForumAccount forumAccount) {
        super(forumAccount);
        this.stateName = "新手级别";
    }

    @Override
    public void writeNote(int point) {
        System.out.println("新手版-写评论");
        forumAccount.setPoint(forumAccount.getPoint() + point);
        updateState(forumAccount.getPoint());
    }

    @Override
    public void replyNote(int point) {
        System.out.println("新手版-回复评论");
        forumAccount.setPoint(forumAccount.getPoint() + point);
        updateState(forumAccount.getPoint());
    }

    @Override
    public void downloadNote(int point) {
        System.out.println("新手状态-无法下载文件");
    }
}
public class MiddleState extends State{
    public MiddleState(ForumAccount forumAccount) {
        super(forumAccount);
        this.stateName = "高手级别";
    }

    @Override
    public void writeNote(int point) {
        System.out.println("高手版-写评论");
        forumAccount.setPoint(forumAccount.getPoint() + point);
        updateState(forumAccount.getPoint());
    }

    @Override
    public void replyNote(int point) {
        System.out.println("高手版-回复评论");
        forumAccount.setPoint(forumAccount.getPoint() + point * 2);
        updateState(forumAccount.getPoint());
    }

    @Override
    public void downloadNote(int point) {
        if (point > forumAccount.getPoint()) {
            System.out.println("积分不足,不能下载");
        } else {
            System.out.println("高手版-下载文件");
            forumAccount.setPoint(forumAccount.getPoint() - point);
            updateState(forumAccount.getPoint());
        }
    }
}
public class HighState extends State {
    public HighState(ForumAccount forumAccount) {
        super(forumAccount);
        this.stateName = "专家级别";
    }

    @Override
    public void writeNote(int point) {
        System.out.println("专家版-写评论");
        forumAccount.setPoint(forumAccount.getPoint() + point);
        updateState(forumAccount.getPoint());
    }

    @Override
    public void replyNote(int point) {
        System.out.println("专家版-回复评论");
        forumAccount.setPoint(forumAccount.getPoint() + point * 2);
        updateState(forumAccount.getPoint());
    }

    @Override
    public void downloadNote(int point) {
        if (point / 2 > forumAccount.getPoint()) {
            System.out.println("积分不足,不能下载");
        } else {
            System.out.println("专家版-下载文件");
            forumAccount.setPoint(forumAccount.getPoint() - point / 2);
            updateState(forumAccount.getPoint());
        }
    }
}
5.4.4 状态模式的优缺点
优点缺点
1.封装了状态的转换规则,可以对状态转换代码进行集中管理,而不是需要冗长的if-else判断1.会增加系统中类和对象的个数,导致系统运行开销增大
2.将所有与某个状态有关的行为放到一个类中,注入一个不同的状态对象即可使环境对象拥有不同行为2.结构与实现都较为复杂,如果使用不当将导致程序结构和代码混乱,增加系统设计的难度
3.允许状态转换逻辑与状态对象合成一体,而不是提供一个巨大的条件语句块3.对开闭原则的支持并不太好
4.可让多个环境对象共享一个状态对象,从而减少系统中对象的个数
5.4.5 状态模式的适用场景
  • 对象的行为依赖于它的状态(例如某些属性值),状态的改变将导致行为的变化

  • 在代码中包含大量与对象状态有关的条件语句,这些条件语句的出现会导致代码的可维护性和灵活性变差,不能方便地增加和删除状态,并且导致客户类与类库之间的耦合增强

5.5 策略模式

5.5.1 策略模式的定义

1.模式动机:解决某一问题的一个算法族,允许用户从该算法族中任选一个算法解决某一问题,同时可以方便地更换算法或者增加新的算法。

2.模式定义:定义一系列算法,将每一个算法封装起来,并让它们可以相互替换。

5.5.2 策略模式的结构与分析

image-20241103171555824

  • 它将每一个算法封装在一个称为具体策略的类中,同时为其提供统一的抽象策略类,而使用这些算法完成某一业务功能的类称为环境类。

  • 策略模式实现了算法定义和算法使用的分离,它通过继承和多态的机制实现对算法族的使用和管理,是一种简单易用的对象行为型模式。

  • 各算法是由客户端决定的,不是策略模式自己决定的。

public abstract class Strategy {
    public abstract void algorithm();
}
public class ConcreteStrategy extends Strategy{
    @Override
    public void algorithm() {
    }
}
public class Context {
    private Strategy strategy;

    public void setStrategy(Strategy strategy) {
        this.strategy = strategy;
    }

    public void request() {
        strategy.algorithm();
    }
}
5.5.3 策略模式的案例

image-20241103172356690

image-20241103172459035

public class Person {
    private String name;
    private TravelStrategy travelStrategy;
    public Person(String name) {
        this.name = name;
    }

    public void setTravelStrategy(TravelStrategy travelStrategy) {
        this.travelStrategy = travelStrategy;
    }

    public void travel() {
        System.out.print(this.name);
        travelStrategy.travel();
    }
}

public interface TravelStrategy {
    public void travel();
}

public class TrainStrategy implements TravelStrategy{
    @Override
    public void travel() {
        System.out.println("乘坐火车去旅行");
    }
}

public class Main {
    public static void main(String[] args) {
        TravelStrategy bean = (AirPlaneTravelStrategy) XMLUtil.getBean();
        Person person = new Person("xyx");
        person.setTravelStrategy(bean);
        person.travel();
    }
}
5.5.4 策略模式的优缺点
优点缺点
1.符合开闭原则,用户可以在不修改原有系统的基础上选择算法或行为,也可以灵活地增加新的算法或行为1.客户端必须知道所有的策略类,并自行决定使用哪一个策略类
2.提供了管理相关的算法族的办法,避免多重条件选择语句2.将造成系统产生很多具体策略类
3.提供了一种可以替换继承关系的办法3.无法同时在客户端使用多个策略类
5.5.5 策略模式的适用场景
  • 一个系统需要动态地在几种算法中选择一种

  • 避免使用难以维护的多重条件选择语句

  • 不希望客户端知道复杂的、与算法相关的数据结构,提高算法的保密性与安全性

5.5.6 状态模式和策略模式的对比
状态模式策略模式
环境类状态个数多个状态,可以相互转化存在一个状态,一旦选择不能随意改变
环境类与状态类关系若环境类会影响状态的改变,为双向关联关系单向的关联关系
客户端无需知道具体状态,会根据行为自动切换需要知道所选策略
行为各状态下有多个行为一个行为的多种实现方式

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

tauri中shell的特殊字符

tauri中shell的特殊字符 官网例子:https://tauri.app/plugin/shell/ 中的入参是 \S 入参,但如果入参存在空格等特殊字符串,将无法传入 "permissions": [{"identifier": "shell:allow-execute","allow&qu…

电子电气架构 --- Trace 32(劳特巴赫)多核系统的调试

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 所有人的看法和评价都是暂时的,只有自己的经历是伴随一生的,几乎所有的担忧和畏惧,都是来源于自己的想象,只有你真的去做了,才会发现有多快乐。…

使用Web Workers实现JavaScript的多线程编程

💓 博客主页:瑕疵的CSDN主页 📝 Gitee主页:瑕疵的gitee主页 ⏩ 文章专栏:《热点资讯》 使用Web Workers实现JavaScript的多线程编程 引言 Web Workers 简介 创建和使用 Worker 通信机制 主线程向 Worker 发送消息 Wor…

【uni-app】创建自定义模板

1. 步骤 打开自定义模板文件夹 在此文件夹下创建模板文件(注意后缀名) 重新点击“新建页面” 即可看到新建的模板 2. 注意事项 创建的模板必须文件类型对应(vue模板就创建*.vue文件, uvue模板就创建*.uvue文件)

【云原生】Docker搭建开源翻译组件Deepl使用详解

目录 一、前言 二、微服务项目使用翻译组件的场景 2.1 多语言用户界面 2.2 业务逻辑中的翻译需求 2.3 满足实时通信的要求 2.4 内容管理系统 2.5 个性化推荐系统 2.6 日志和监控 三、开源类翻译组件解决方案 3.1 国内翻译组件方案汇总 3.1.1 百度翻译 3.1.2 腾讯翻…

Hms?: 1渗透测试

靶机:Hms?: 1 Hms?: 1 ~ VulnHub 攻击机:kail linux 2024 主机扫描阶段发现不了靶机,所以需要按DriftingBlues2一样手动配置网卡 1,将两台虚拟机网络连接都改为NAT模式,并查看靶机的MAC地址 2,攻击机上做主机扫描发现…

linux-环境变量

环境变量是系统提供的一组 name value 的变量,不同的变量有不同的用途,通常都具有全局属性 env 查看环境变量 PATH PATH是一个保存着系统指令路径的一个环境变量,系统提供的指令不需要路径,直接就可以使用就是因为指令的路径…

基于Spring Boot的私房菜定制上门服务系统的设计与实现

摘 要 如今社会上各行各业,都喜欢用自己行业的专属软件工作,互联网发展到这个时候,人们已经发现离不开了互联网。新技术的产生,往往能解决一些老技术的弊端问题。因为传统私房菜定制上门服务系统信息管理难度大,容错率…

qt QMenuBar详解

1、概述 QMenuBar是Qt框架中用于创建菜单栏的类,它继承自QWidget。QMenuBar通常位于QMainWindow对象的标题栏下方,用于组织和管理多个QMenu(菜单)和QAction(动作)。菜单栏提供了一个水平排列的容器&#x…

转载:【lwip】03-内存管理 - 李柱明 - 博客园

目录 前言3. 内存管理 3.1 内存分配策略 3.1.1 固定大小的内存块3.1.2 可变大小分配3.2 动态内存池(pool) 3.2.1 介绍3.2.2 内存池的预处理3.2.3 内存池的初始化3.2.4 内存分配3.2.5 内存释放3.2.6 内存池源码定义简要分析 定义内存池资源源码分析保存各…

「Mac畅玩鸿蒙与硬件25」UI互动应用篇2 - 计时器应用实现

本篇将带领你实现一个实用的计时器应用,用户可以启动、暂停或重置计时器。该项目将涉及时间控制、状态管理以及按钮交互,是掌握鸿蒙应用开发的重要步骤。 关键词 UI互动应用时间控制状态管理用户交互 一、功能说明 在这个计时器应用中,用户…

使用MongoDB Atlas构建无服务器数据库

💓 博客主页:瑕疵的CSDN主页 📝 Gitee主页:瑕疵的gitee主页 ⏩ 文章专栏:《热点资讯》 使用MongoDB Atlas构建无服务器数据库 MongoDB Atlas 简介 注册账户 创建集群 配置网络 设置数据库用户 连接数据库 设计文档模式…

从零开始的c++之旅——继承

1. 继承 1.继承概念及定义 继承是面向对象编程的三大特点之一,它使得我们可以在原有类特性的基础之上,增加方法 和属性,这样产生的新的类,称为派生类。 继承 呈现了⾯向对象程序设计的层次结构,以前我们接触的…

正向解析和反向解析

正向解析 服务端: [rootlocalhost rhel]# vim /etc/named.conf [rootlocalhost named]# vim /var/named/named.openlab.com 客户端: [rootlocalhost rhel]# nslookup 反向解析 服务端: [rootlocalhost rhel]# vim /etc/named.conf [ro…

计算机网络:网络层 —— 路由信息协议 RIP

文章目录 路由选择协议动态路由协议路由信息协议 RIPRIP 的重要特点RIP的基本工作过程RIP的距离向量算法RIP存在的问题RIP版本和相关报文的封装 路由选择协议 因特网是全球最大的互联网,它所采取的路由选择协议具有以下三个主要特点: 自适应&#xff1a…

基于yolov5的输电线,电缆检测系统,支持图像检测,视频检测和实时摄像检测功能(pytorch框架,python源码)

更多目标检测和图像分类识别项目可看我主页其他文章 功能演示: yolov5,输电线(线缆)检测系统,系统既支持图像检测,也支持视频和摄像实时检测【pytorch框架】_哔哩哔哩_bilibili (一)简介 基于yolov5的输…

删除WPS的智能识别目录

很烦,对吧 智能识别目录很垃圾,无法直接删除,如果你选择左边的目录,删除的话,会顺便把右边的正文也删除了。 那么如何只删除左边目录,保留右边的正文呢?只有一个办法: ctrlshiftC复…

客户端与微服务之间的桥梁---网关

当我们创建好了N多个微服务或者微服务的实例之后,每个服务暴露出不同的端口地址,一般对于客户端请求,只需要请求一个端口,要隔离客户端和微服务的直接关系,保证微服务的安全性和灵活性,避免敏感信息的泄露。…

构建您自己的 RAG 应用程序:使用 Ollama、Python 和 ChromaDB 在本地设置 LLM 的分步指南

在数据隐私至关重要的时代,建立自己的本地语言模型 (LLM) 为公司和个人都提供了至关重要的解决方案。本教程旨在指导您完成使用 Ollama、Python 3 和 ChromaDB 创建自定义聊天机器人的过程,所有这些机器人都托管在您的系统本地。以…

C++STL-deque、stack、queue、priority_queue

C教学总目录 deque、stack、queue、priority_queue 1、deque2、stack使用介绍3、stack实现4、queue使用介绍5、queue实现6、priority_queue使用介绍7、priority_queue实现8、反向迭代器 1、deque deque是双端队列,我们学习的队列是先进先出的(First in first out)&a…