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

news2025/2/26 14:31:14

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

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. 增加了类的个数。

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

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

相关文章

记mapboxGL实现鼠标经过高亮时的一个问题

概述 mapboxGL实现鼠标经过高亮可通过注册图层的mousemove和moveout事件来实现&#xff0c;在mousemove事件中可以拿到当前经过的要素&#xff0c;但是当使用该要素时&#xff0c;发现在某个地图级别下会有线和面数据展示不全的情况。究其原因&#xff0c;发现是mapboxGL在绘图…

2024Dragon Knight CTF复现web

穿梭隐藏的密钥 首先看看页面的源代码&#xff0c;但是发现f12和鼠标右键都被禁用了 用ctrlu查看&#xff0c;发现一个可疑页面 访问看看&#xff0c;发现还是只有一张图&#xff0c;查看源代码发现提示 扩展&#xff1a; Fuzz&#xff1a;Fuzz是一种基于黑盒的自动化软件模糊…

数据结构与算法笔记:基础篇 - 栈:如何实现浏览器的前进和后退功能?

概述 浏览器的前进、后退功能&#xff0c;你肯定很熟悉吧&#xff1f; 当依次访问完一串页面 a-b-c 之后&#xff0c;点击浏览器的后退按钮&#xff0c;就可以查看之前浏览过的页面 b 和 a。当后退到页面 a&#xff0c;点击前进按钮&#xff0c;就可以重新查看页面 b 和 c。但…

C/S模型测试

1 1.1代码示例 #include<stdio.h> #include<stdio.h>#include <sys/types.h> /* See NOTES */ #include <sys/socket.h>#include <netinet/in.h> #include <netinet/ip.h> /* superset of previous */ #include <arpa/inet.…

004 仿muduo实现高性能服务器组件_Buffer模块与Socket模块的实现

​&#x1f308;个人主页&#xff1a;Fan_558 &#x1f525; 系列专栏&#xff1a;仿muduo &#x1f339;关注我&#x1f4aa;&#x1f3fb;带你学更多知识 文章目录 前言Buffer模块Socket模块 小结 前言 这章将会向你介绍仿muduo高性能服务器组件的buffer模块与socket模块的实…

12k Star!Continue:Github Copilot 开源本地版、开发效率和隐私保护兼得、丰富功能、LLM全覆盖!

原文链接&#xff1a;&#xff08;更好排版、视频播放、社群交流、最新AI开源项目、AI工具分享都在这个公众号&#xff01;&#xff09; 12k Star&#xff01;Continue&#xff1a;Github Copilot 开源本地版、开发效率和隐私保护兼得、丰富功能、LLM全覆盖&#xff01; &…

CSS--学习

CSS 1简介 1.1定义 层叠样式表 (Cascading Style Sheets&#xff0c;缩写为 CSS&#xff09;&#xff0c;是一种 样式表 语言&#xff0c;用来描述 HTML 文档的呈现&#xff08;美化内容&#xff09;。 1.2 特性 继承性 子级默认继承父级的文字控制属性。层叠性 相同的属性…

Elasticsearch 认证模拟题 - 5

一、题目 .在集群上有一个索引 food_ingredient&#xff0c;搜索需要满足以下要求&#xff1a; 三个字段 manufacturer&#xff0c;name&#xff0c;brand 都能匹配到文本 cake mix高亮 字段 name&#xff0c;并加标签排序&#xff0c;对字段 brand 正序&#xff0c;_score 降…

【Linux】Linux环境基础开发工具_3

文章目录 四、Linux环境基础开发工具2. vim3. gcc和g动静态库的理解 未完待续 四、Linux环境基础开发工具 2. vim vim 怎么批量化注释呢&#xff1f;最简单的方法就是在注释开头和结尾输入 /* 或 */ 。当然也可以使用快捷键&#xff1a; Ctrl v 按 hjkl 光标移动进行区域选择…

VR导航的实现原理、技术优势和应用场景

VR导航通过虚拟现实技术提供沉浸式环境&#xff0c;结合室内定位技术实现精准导航。目前&#xff0c;VR导航已在多个领域展现出其独特的价值和潜力&#xff0c;预示着智能导航系统的未来发展。 一、实现原理 VR导航技术依托于虚拟现实(VR)和室内定位系统。VR技术利用计算机模…

IMU状态预积分代码实现 —— IMU状态预积分类

IMU状态预积分代码实现 —— IMU状态预积分类 实现IMU状态预积分类 实现IMU状态预积分类 首先&#xff0c;实现预积分自身的结构。一个预积分类应该存储一下数据&#xff1a; 预积分的观测量 △ R ~ i j , △ v ~ i j , △ p ~ i j \bigtriangleup \tilde{R} _{ij},\bigtrian…

MySQL基础索引知识【索引创建删除 | MyISAM InnoDB引擎原理认识】

博客主页&#xff1a;花果山~程序猿-CSDN博客 文章分栏&#xff1a;MySQL之旅_花果山~程序猿的博客-CSDN博客 关注我一起学习&#xff0c;一起进步&#xff0c;一起探索编程的无限可能吧&#xff01;让我们一起努力&#xff0c;一起成长&#xff01; 目录 一&#xff0c;索引用…

数据在内存中的存储<C语言>

导言 在计算机中不同类型的数据在计算机内部存储形式各不相同&#xff0c;弄懂各种数据在计算机内部存储形式是有必要的&#xff0c;C语言的学习不能浮于表面&#xff0c;更要锻炼我们的“内功”&#xff0c;将来在写程序的时候遇见各种稀奇古怪的bug时&#xff0c;也便能迎刃而…

Beamer中二阶导、一阶导数的显示问题

Beamer中二阶导、一阶导数的显示问题 在beamer中表示 f ′ f f′和 f ′ ′ f f′′时发现导数符号距离 f f f很近 \documentclass{beamer} \usepackage{amsmath,amssymb}\begin{document} \begin{frame}\frametitle{Derivative}Derivative:\[f^{\prime}(x) \quad f \quad…

4月啤酒品类线上销售数据分析

近期&#xff0c;中国啤酒行业正处于一个重要的转型期。首先&#xff0c;消费者对高品质啤酒的需求不断增加&#xff0c;这推动了行业向高端化、场景化和社交化的方向发展。精酿啤酒作为这一趋势的代表&#xff0c;其发展势头强劲&#xff0c;不仅满足了消费者对品质化、个性化…

Java集合【超详细】2 -- Map、可变参数、Collections类

文章目录 一、Map集合1.1 Map集合概述和特点【理解】1.2 Map集合的基本功能【应用】1.3 Map集合的获取功能【应用】1.4 Map集合的两种遍历方式 二、HashMap集合2.1 HashMap集合概述和特点【理解】2.2 HashMap的组成、构造函数2.3 put、查找方法2.4 HashMap集合应用案例【应用】…

退出登录后选择记住登录状态回显用户名和密码

项目背景 : react ant 需求 : 退出登录后 , 选择了记住登录 , 回显用户名和密码 ; 未选择记住 , 则不回显用户名和密码 如图注意 : 发现一个鸡肋的问题 , 未勾选退出后 , 还是会回显 , 后来我查看了cookie和自己的逻辑都没问题 , 原来是因为我保存了密码 , 浏览器保存后自动渲…

C# 代码配置的艺术

文章目录 1、代码配置的定义及其在软件工程中的作用2、C# 代码配置的基本概念和工具3、代码配置的实践步骤4、实现代码配置使用属性&#xff08;Properties&#xff09;使用配置文件&#xff08;Config Files&#xff09;使用依赖注入&#xff08;Dependency Injection&#xf…

Echarts 让柱状图在图表中展示,离开X轴

文章目录 需求分析需求 分析 话不多说,直接源码展示 option = {title: {text: Waterfall Chart,subtext: Li

数据隐私重塑:Web3时代的隐私保护创新

随着数字化时代的不断深入&#xff0c;数据隐私保护已经成为了人们越来越关注的焦点之一。而在这个数字化时代的新篇章中&#xff0c;Web3技术作为下一代互联网的代表&#xff0c;正在为数据隐私保护带来全新的创新和可能性。本文将深入探讨数据隐私的重要性&#xff0c;Web3时…