【再探】设计模式—访问者模式、策略模式及状态模式

 访问者模式是用于访问复杂数据结构的元素,对不同的元素执行不同的操作。策略模式是对于具有多种实现的算法,在运行过程中可动态选择使用哪种具体的实现。状态模式是用于具有不同状态的对象,状态之间可以转换,且不同状态下对象的行为不同,客户端可以不必考虑其状态及转换,对所有的状态都可以执行同一的操作。

1 访问者模式

需求:需要对一个复杂数据结构进行操作,根据其不同的元素类型执行不同的操作。

1.1 访问者模式介绍

将数据结构与数据操作分离,通过定义一个访问者对象,来实现对数据结构中各个元素的访问和处理,从而达到解耦和灵活操作的目的。

图 访问者模式 UML

1.1.1 双分派

Java 是一种支持双分派的单分派语言。

单分派

调用哪个对象(多态)的方法,在运行期确定,调用对象的哪个方法,在编译期确定。

双分派

调用哪个对象(多态)的方法,在运行期确定,调用对象的哪个方法,在运行期确定。

图 单分派与双分派

访问者模式中的元素对象中的accept方法就是双分派。

public class VisitorPattern {

    public static void main(String[] args) {
        Element[] elements = {new MobilePhone(),new Ipad(),new Computer()};
        Visitor[] visitors = {new Student(),new Programmer()};
        for (Visitor visitor : visitors) {
            for (Element element : elements) {
                element.accept(visitor);
            }
        }
    }

    private interface Element {
        void accept(Visitor visitor);
    }

    private static class MobilePhone implements Element{

        @Override
        public void accept(Visitor visitor) {
            visitor.visit(this);
        }

        public void makeCall() {
            System.out.println("打电话");
        }

        public void wechat() {
            System.out.println("微信聊天");
        }

        public void playGame() {
            System.out.println("玩王者荣耀");
        }
    }

    private static class Ipad implements Element {

        @Override
        public void accept(Visitor visitor) {
            visitor.visit(this);
        }

        public void watchTv() {
            System.out.println("刷剧");
        }

        public void playGame() {
            System.out.println("玩王者荣耀,大屏更爽");
        }
    }

    private static class Computer implements Element {

        @Override
        public void accept(Visitor visitor) {
            visitor.visit(this);
        }

        public void playGame() {
            System.out.println("玩电脑游戏:使命召唤");
        }

        public void work() {
            System.out.println("用于工作");
        }
    }

    private interface Visitor {
        void visit(MobilePhone mobilePhone);
        void visit(Ipad ipad);
        void visit(Computer computer);
    }

    private static class Programmer implements Visitor {

        @Override
        public void visit(MobilePhone mobilePhone) {
            mobilePhone.makeCall();
        }

        @Override
        public void visit(Ipad ipad) {
            ipad.playGame();
        }

        @Override
        public void visit(Computer computer) {
            computer.work();
        }
    }

    private static class Student implements Visitor {

        @Override
        public void visit(MobilePhone mobilePhone) {
            mobilePhone.wechat();
        }

        @Override
        public void visit(Ipad ipad) {
            ipad.watchTv();
        }

        @Override
        public void visit(Computer computer) {
            computer.playGame();
        }
    }

}

1.2 优缺点

优点:

  1. 将数据结构与数据操作分离,当需要添加新的操作时,只添加新的子类即可,符合开闭原则。
  2. 将对元素的操作集中到一个类中,而不是分散在元素的子类中,使得职责更加明确,代码更加清晰,符合单一职责原则。

缺点:

  1. 不适合结构不稳定的数据结构,当新增或删除元素时,需要修改访问者类,不符合开闭原则。
  2. 如果访问者需要访问元素的内部信息,可能会破坏封装性。

2 策略模式

需求:一个算法有多种实现方式,或一个对象有很多的行为,这些行为需要使用多重条件选择语句来实现。期望把这些行为转移到具体的策略类中,避免使用难以维护的多重语句。

2.1 策略模式介绍

定义一个算法接口,然后将其不同的实现封装到具体的策略类中,并让它们可以相互替换。

图 策略模式 UML

public class StrategyPattern {

    public static void main(String[] args) {
        SortAlgorithm[] sortAlgorithms = {new BubbleSortAlgorithm(),new SelectionSortAlgorithm()};
        Integer[][] array = {{34,23,2,4,6,44,11,53,221,123},{3,66,27,212,45,565,11,44,33,12,465,55,22,14,56}};
        for (SortAlgorithm sortAlgorithm : sortAlgorithms) {
            for (Integer[] arr : array) {
                sortAlgorithm.sort(arr);
            }
            System.out.println();
        }
    }

    private static abstract class SortAlgorithm {
        public void sort(Integer[] array) { // 升序
            if (array != null && array.length > 1) {
                array = Arrays.copyOf(array,array.length);
                opera(array);
                System.out.println(Arrays.asList(array));
            }
        }

        protected abstract void opera(Integer[] array);
    }

    /**
     * 冒泡排序
     */
    private static class BubbleSortAlgorithm extends SortAlgorithm{
        @Override
        protected void opera(Integer[] array) {
            for (int i = 0; i < array.length; i++) {
                for (int j = i + 1; j < array.length; j++) {
                    if (array[i] > array[j]) {
                        int temp = array[j];
                        array[j] = array[i];
                        array[i] = temp;
                    }
                }
            }
        }
    }

    /**
     * 选择排序
     */
    private static class SelectionSortAlgorithm extends SortAlgorithm {
        @Override
        protected void opera(Integer[] array) {
            for (int i = 0; i < array.length; i++) {
                int minPos = i;
                for (int j = i+1; j <array.length; j++) {
                    if (array[minPos] > array[j]) {
                        minPos = j;
                    }
                }
                int temp = array[i];
                array[i] = array[minPos];
                array[minPos] = temp;
            }
        }
    }

}

2.2 优缺点

优点:

  1. 符合开闭原则,增加新的算法或行为时只需要添加子类即可。
  2. 避免了多重条件选择语句。

缺点:

  1. 客户端必须知道所有的策略类,并自行决定使用哪个策略类。
  2. 会增加类的数量。

3 状态模式

需求:系统中某个对象存在多个状态,这些状态之间可以进行转换,且对象在不同状态下的行为不同。

3.1 状态模式介绍

将一个对象的状态从该对象中分离出来,封装到专门的状态类中。对于客户端而言,无须关心对象的状态及转换,无论对象处于哪种状态,客户端都可以一致性地处理。

图 状态模式 UML

public class StatePattern {

    public static void main(String[] args) {
        BackCard backCard = new BackCard();
        backCard.save(100);
        backCard.withdraw(1000);
        backCard.withdraw(600);
        backCard.withdraw(400);
        backCard.withdraw(100);
    }

    private static class BackCard {

        private final static State[] stateList = {new NormalState(),new Overdraft(),new LimitState()};

        private State state = stateList[0];

        private double balance = 0;

        public double getBalance() {
            return balance;
        }

        public void setBalance(double balance) {
            this.balance = balance;
        }

        public void save(double money) {
            System.out.println("存钱操作:" + money);
            this.balance += money;
            changeState();
        }

        public void withdraw(double money) {
            System.out.println("取钱操作:" + money);
            try {
                state.withdraw(this,money);
            } catch (RuntimeException e) {
                System.out.println("取钱失败:" + e.getMessage());
            }
            changeState();
        }

        private void changeState() {
            System.out.println("-----余额:" + balance + "-----");
            if (balance >= 0) state = stateList[0];
            else if (balance >= -1000) state = stateList[1];
            else state = stateList[2];
        }

    }

    private interface State {
        void withdraw(BackCard backCard,double money);
    }

    private static class NormalState implements State {
        @Override
        public void withdraw(BackCard backCard,double money) {
            backCard.setBalance(backCard.getBalance() - money);
        }
    }

    private static class Overdraft implements State {
        @Override
        public void withdraw(BackCard backCard,double money) {
            if (money > 500) {
                throw new RuntimeException("卡被透支,取钱不能超过500元");
            }
            backCard.setBalance(backCard.getBalance() - money);
        }
    }

    private static class LimitState implements State {
        @Override
        public void withdraw(BackCard backCard,double money) {
            throw new RuntimeException("卡被限制,不能取钱");
        }
    }

}

3.2 优缺点

优点:

  1. 客户端可以不必关心对象的状态及转换,对所有状态都可进行同一操作。
  2. 将与具体状态相关的行为都封装在一个类中,符合单一职责原则,更容易扩展及维护。

缺点:

  1. 状态转换无论是在环境类还是状态类中,当增加新的状态或修改转换逻辑时,都需要修改转换代码,不符合开闭原则。
  2. 增加了类的个数。

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

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

相关文章

Threejs(WebGL)绘制线段优化:Shader修改gl.LINES模式为gl.LINE_STRIP

目录 背景 思路 Threejs实现 记录每条线的点数 封装原始裁剪索引数据 封装合并几何体的缓冲数据&#xff1a;由裁剪索引组成的 IntArray 守住该有的线段&#xff01; 修改顶点着色器 修改片元着色器 完整代码 WebGL实现类似功能&#xff08;简易版&#xff0c;便于测…

极验4点选逆向 JS逆向分析 最新版验证码

目录 声明&#xff01; 一、请求流程分析 二、加密参数w与payload 三、参数w生成位置 四、结果展示&#xff1a; 原创文章&#xff0c;请勿转载&#xff01; 本文内容仅限于安全研究&#xff0c;不公开具体源码。维护网络安全&#xff0c;人人有责。 声明&#xff01; 本文章…

mirth Connect 自定义JAVA_HOME

mirth Connect 自定义JAVA_HOME 1、背景 服务器上安装了两个不同版本的Java&#xff0c;我希望Mirth服务使用与默认系统不同的版本。自定义指定java版本 2、解决方法 2.1 优先级说明 系统变量JAVA_HOME (设置后&#xff0c;mirth会根据这个进行启动运行服务&#xff0c;优先级…

家政预约小程序10公众号集成

目录 1 使用测试号3 工作流配置4 配置关注事件脚本5 注册开放平台6 获取公众号access_token6 实现关注业务逻辑总结 我们本次实战项目构建的相当于一个预约平台&#xff0c;既有家政企业&#xff0c;也有家政服务人员还有用户。不同的人员需要收到不同的消息&#xff0c;比如用…

根据状态转移图实现时序电路 (三段式状态机)

看图编程 * ** 代码 module seq_circuit(input C ,input clk ,input rst_n,output wire Y ); reg [1:0] current_stage ; reg [1:0] next_stage ; reg Y_reg; //输出//第一段 &#xff1a; 初始化当前状态和…

vmware esxi虚拟化数据迁移

1、启用esxi的ssh 登录esxi的web界面&#xff0c;选择主机-》操作——》服务——》启动ssh 2.xshell登录esxi 3、找到虚拟机所在目录 blog.csdnimg.cn/direct/d57372536a4145f2bcc1189d02cc7da8.png)#### 3在传输数据前需关闭防火墙服务 查看防火墙状态&#xff1a;esxcli …

vue3学习(六)

前言 接上一篇学习笔记&#xff0c;今天主要是抽空学习了vue的状态管理&#xff0c;这里学习的是vuex&#xff0c;版本4.1。学习还没有学习完&#xff0c;里面有大坑&#xff0c;难怪现在官网出的状态管理用Pinia。 一、vuex状态管理知识点 上面的方式没有写全&#xff0c;还有…

如何修改开源项目中发现的bug?

如何修改开源项目中发现的bug&#xff1f; 目录 如何修改开源项目中发现的bug&#xff1f;第一步&#xff1a;找到开源项目并建立分支第二步&#xff1a;克隆分支到本地仓库第三步&#xff1a;在本地对项目进行修改第四步&#xff1a;依次使用命令行进行操作注意&#xff1a;Gi…

OAK相机如何将 YOLOv9 模型转换成 blob 格式?

编辑&#xff1a;OAK中国 首发&#xff1a;oakchina.cn 喜欢的话&#xff0c;请多多&#x1f44d;⭐️✍ 内容可能会不定期更新&#xff0c;官网内容都是最新的&#xff0c;请查看首发地址链接。 Hello&#xff0c;大家好&#xff0c;这里是OAK中国&#xff0c;我是Ashely。 专…

逆天工具一键修复图片,视频去码。简直不要太好用!

今天&#xff0c;我要向您推荐一款功能强大的本地部署软件&#xff0c;它能够在您的计算机上一键修复图片和视频&#xff0c;去除令人不悦的码赛克&#xff08;轻度马赛克&#xff09;。这款软件是开源的&#xff0c;并在GitHub上公开可用&#xff0c;您可以免费下载并使用。 …

智能制造案例专题|与MongoDB一起解锁工业4.0转型与增长的无限潜力!

MongoDB 智能制造 数字化技术的洪流在各个产业链的主干和枝节涌现。在工业制造领域&#xff0c;能否通过数字化技术实现各生产要素、生产环节之间的紧密配合&#xff0c;高效规划、管理整个生产流程&#xff0c;是企业提升韧性、赢得竞争的关键。随着工业4.0的深入发展和智能…

Kafka自定义分区器编写教程

1.创建java类MyPartitioner并实现Partitioner接口 点击灯泡选择实现方法&#xff0c;导入需要实现的抽象方法 2.实现方法 3.自定义分区器的使用 在自定义生产者消息发送时&#xff0c;属性配置上加入自定义分区器 properties.put(ProducerConfig.PARTITIONER_CLASS_CONFIG,&q…

stack和queue(1)

一、stack的简单介绍和使用 1.1 stack的介绍 1.stack是一种容器适配器&#xff0c;专门用在具有先进后出&#xff0c;后进先出操作的上下文环境中&#xff0c;其删除只能从容器的一端进行元素的插入和弹出操作。 2.stack是作为容器适配器被实现的&#xff0c;容器适配器即是…

C++ 混合运算的类型转换

一 混合运算和隐式转换 257 整型2 浮点5 行吗&#xff1f;成吗&#xff1f;中不中&#xff1f; C 中允许相关的数据类型进行混合运算。 相关类型。 尽管在程序中的数据类型不同&#xff0c;但逻辑上进行这种运算是合理的相关类型在混合运算时会自动进行类型转换&#xff0c;再…

EXCEL数据透视图中的日期字段,怎样自动分出年、季度、月的功能?

在excel里&#xff0c;这个果然是有个设置的地方&#xff0c;修改后就好了。 点击文件选项卡&#xff0c;选项&#xff0c;在高级里&#xff0c;将图示选项的勾选给取消&#xff0c;然后再创建数据透视表或透视图&#xff0c;日期就不会自动组合了&#xff1a; 这个选项只对新…

Scroll 生态明星项目Pencils Protocol,发展潜力巨大

近日&#xff0c;完成品牌升级的 Pencils Prtocol 结束了 Season 2 并无缝开启了 Season 3&#xff0c;在 Season 3 中&#xff0c;用户可以通过质押系列资产包括 $ETH、$USDT、$USDC、$STONE 、$wrsETH、$pufETH 等来获得可观收益&#xff0c;并获得包括 Scroll Marks、 Penci…

html+CSS部分基础运用9

项目1 参会注册表 1.设计参会注册表页面&#xff0c;效果如图9-1所示。 图9-1 参会注册表页面 项目2 设计《大学生暑期社会实践调查问卷》 1.设计“大学生暑期社会实践调查问卷”页面&#xff0c;如图9-2所示。 图9-2 大学生暑期社会调查表页面 2&#xff0e;调查表前导语的…

C/C++中互斥量(锁)的实现原理探究

互斥量的实现原理探究 文章目录 互斥量的实现原理探究互斥量的概念何为原子性操作原理探究 互斥量的概念 ​ 互斥量&#xff08;mutex&#xff09;是一种同步原语&#xff0c;用于保护多个线程同时访问共享数据。互斥量提供独占的、非递归的所有权语义&#xff1a;一个线程从成…

LeetCode374猜数字大小

题目描述 我们正在玩猜数字游戏。猜数字游戏的规则如下&#xff1a;我会从 1 到 n 随机选择一个数字。 请你猜选出的是哪个数字。如果你猜错了&#xff0c;我会告诉你&#xff0c;我选出的数字比你猜测的数字大了还是小了。你可以通过调用一个预先定义好的接口 int guess(int n…

代码随想录算法训练营第四十四天 | 01背包问题 二维、 01背包问题 一维、416. 分割等和子集

01背包问题 二维 代码随想录 视频讲解&#xff1a;带你学透0-1背包问题&#xff01;| 关于背包问题&#xff0c;你不清楚的地方&#xff0c;这里都讲了&#xff01;| 动态规划经典问题 | 数据结构与算法_哔哩哔哩_bilibili 1.dp数组定义 dp[i][j] 下标为[0,i]之间的物品&…