设计模式 四、行为设计模式(2)

五、状态模式

        1、概述

         状态设计模式是一种行为型设计模式,它允许对象在其内部状态发生时改变其行为,这种模式可以消除大量的条件语句,并将每个状态的行为封装到单独的类中。

        状态模式的主要组成部分如下:
        1)上下文(Context):上下文通常包含一个具体状态的引用,用于维护当前状态,上下文委托给当前对象处理状态相关行为。
        2)抽象状态(State):定义一个接口,用于封装与上下文的特定状态相关的行为。
        3)具体状态(Concrete State):实现抽象状态接口,为具体状态定义行为。每个具体状态类对应一个状态。

        简单示例,假设要模拟一个简易的电视遥控器,具有开启、关闭和调整音量的功能。
        假设不使用设计模式:

public class TV {private boolean isOn;private int volume;public TV() {isOn = false;volume = 0;}public void turnOn() {// 如果是开启状态if (isOn) {System.out.println("TV is already on.");// 否则打开电视} else {isOn = true;System.out.println("Turning on the TV.");}}public void turnOff() {if (isOn) {isOn = false;System.out.println("Turning off the TV.");} else {System.out.println("TV is already off.");}}public void adjustVolume(int volume) {if (isOn) {this.volume = volume;System.out.println("Adjusting volume to: " + volume);} else {System.out.println("Cannot adjust volume, TV is off.");}}
}
public class Main {public static void main(String[] args) {TV tv = new TV();tv.turnOn();tv.adjustVolume(10);tv.turnOff();}
}

        该例子中我们状态比较少,所以代码看起来也不是很复杂,但是状态如果变多了就会变得不好控制,比如增加换台,快捷键,静音等功能。
        使用状态设计模式之后:
        首先定义抽象状态接口TVState,将每一个修改状态的动作抽象成一个接口:

public interface TVState {void turnOn();void turnOff();void adjustVolume(int volume);
}

        接下来为每个具体的状态创建类,实现TVState接口,例如:创建TVOnState 和 TVOffState 类:

// 在on状态下,去执行以下各种操作
public class TVOnState implements TVState {@Overridepublic void turnOn() {System.out.println("TV is already on.");}@Overridepublic void turnOff() {System.out.println("Turning off the TV.");}@Overridepublic void adjustVolume(int volume) {System.out.println("Adjusting volume to: " + volume);}
}
// 在关机的状态下执行以下的操作
public class TVOffState implements TVState {@Overridepublic void turnOn() {System.out.println("Turning on the TV.");}@Overridepublic void turnOff() {System.out.println("TV is already off.");}@Overridepublic void adjustVolume(int volume) {System.out.println("Cannot adjust volume, TV is off.");}
}

        接下来定义上下文类TV:

public class TV {// 当前状态private TVState state;public TV() {state = new TVOffState();}public void setState(TVState state) {this.state = state;}public void turnOn() {// 打开state.turnOn();// 设置为开机状态setState(new TVOnState());}public void turnOff() {// 关闭state.turnOff();// 设置为关机状态setState(new TVOffState());}public void adjustVolume(int volume) {state.adjustVolume(volume);}
}

        最后通过以下方式使用这些类:

public class Main {public static void main(String[] args) {TV tv = new TV();tv.turnOn();tv.adjustVolume(10);tv.turnOff();}
}

        这个例子展示了状态模式的基本结构和用法。通过使用状态模式,我们可以更好地组织和管理与特定状态相关的代码。当状态较多时,这种模式的优势就会凸显出来,同时我们在代码时,因为我们会对每个状态进行独立封装,所以也会简化代码编写。

        2、有限状态机

        有限状态机,英文翻译时Flinite State Machine,缩写为FSM,简称状态机,比较官方的说法是:有限状态机是描述对象在它的生命周期内所经历的状态序列,以及如何响应来自外界的各种事件。状态机有3个组成部分:状态(State)、事件(Event)、动作(Action)。其中,事件也成为转移条件(Transition Condition)。事件触发状态的转移及动作的执行,不过,动作不是必须的,也可能只转移状态,不执行任何动作。

        2.1 分支法

        如何实现状态机,总结了三种方式。其中,最简单直接的实现方式是,参照状态转移图,将每一个状态转移。原模原样的直译成代码。这样编写的代码会包含大量的 if - else或 switch-case 分支判断逻辑,甚至是嵌套的分支判断逻辑。所以我们把这种方法暂且命名为分支法:

        下面是一个使用if-else 语句实现的马里奥形态变化的代码示例:

public class Mario {private MarioState state;public Mario() {state = MarioState.SMALL;}public void handleEvent(Event event) {MarioState newState = state;// 处理吃蘑菇事件if (event == Event.MUSHROOM) {if (state == MarioState.SMALL) {newState = MarioState.BIG;}// 处理吃火花事件} else if (event == Event.FIRE_FLOWER) {if (state == MarioState.BIG) {newState = MarioState.FIRE;}// 处理遇到小怪事件} else if (event == Event.ENEMY_ATTACK) {if (state == MarioState.BIG) {newState = MarioState.SMALL;} else if (state == MarioState.FIRE) {newState = MarioState.BIG;} else if (state == MarioState.SMALL) {newState = MarioState.DEAD;}// 处理掉坑事件} else if (event == Event.FALL_INTO_PIT) {newState = MarioState.DEAD;}System.out.printf("从 %s 变为 %s%n", state, newState);state = newState;}
}
public class MarioDemo {public static void main(String[] args) {Mario mario = new Mario();mario.handleEvent(Event.MUSHROOM); // 变为大马里奥mario.handleEvent(Event.FIRE_FLOWER); // 变为火焰马里奥mario.handleEvent(Event.ENEMY_ATTACK); // 变为死亡马里奥}
}
        在这个示例中,我们使用 if-else 语句来处理状态转换。 handleEvent 方法中,我们根据事件和当前状态的组合来确定新状态 ,并 更新马里奥的状态 。这种实现方法相较于查表法和面向对象实现更为简单,但可能在状态和事件更多的情况下变得难以维护。选择合适的实现方法取决于实际需求和场景。

        2.2 查表法

           该种方法略,不怎么用,可以自己查资料。

        2.3 状态模式

        在查表法的代码实现中,事件触发的动作只是简单的状态或者数值,所以,我们用一个 MarioState类型的二维数组 TRANSITION_TABLE 就能表示,二维数组中的值表示出发事件后的新状态。但是,如果要执行的动作并非这么简单,而是一系列复杂的逻辑操作(比如加减分数、处理位置信息等等),我们就没法用如此简单的二维数组来表示了。这也就是说,查表法的实现方式有一定局限性

        虽然分支逻辑的实现方式不存在这个问题,但它又存在前面讲到的其他问题,比如分支判断逻辑较多,导致代码可读性和可维护性不好等。实际上,针对分支逻辑法存在的问题,我们可以使用状态模式来解决

        状态模式通过将事件触发的状态转移和动作执行,拆分到不同的状态类中,来避免分支判断逻辑。我们还是结合代码来理解这句话。利用状态模式,我们来补全 MarioStateMachine 类,补全后的代码如下所示。

        以下是一个使用 Java 实现的简化版马里奥形态变化的案例代码,我为代码添加了中文注释以便理解:

// 定义事件枚举类型
enum Event {// 吃蘑菇,吃火花,遇到小怪,调入深坑MUSHROOM, FIRE_FLOWER, ENEMY_ATTACK, FALL_INTO_PIT
}
// 定义马里奥状态接口
interface MarioState {void handleEvent(Event event);
}
// 实现死亡马里奥状态
class DeadMario implements MarioState {private Mario mario;public DeadMario(Mario mario) {this.mario = mario;}@Overridepublic void handleEvent(Event event) {System.out.println("马里奥已死亡,无法处理事件");}
}
// 实现小马里奥状态
class SmallMario implements MarioState {private Mario mario;public SmallMario(Mario mario) {this.mario = mario;}@Overridepublic void handleEvent(Event event) {switch (event) {case MUSHROOM:System.out.println("变为大马里奥");mario.setState(new BigMario(mario));break;case FIRE_FLOWER:System.out.println("小马里奥不能直接变为火焰马里奥");break;case ENEMY_ATTACK:System.out.println("小玛丽奥去世了");mario.setState(new DeadMario(mario));break;case FALL_INTO_PIT:System.out.println("小玛丽奥去世了");mario.setState(new DeadMario(mario));break;}}
}
// 实现大马里奥状态
class BigMario implements MarioState {private Mario mario;public BigMario(Mario mario) {this.mario = mario;}@Overridepublic void handleEvent(Event event) {switch (event) {case MUSHROOM:System.out.println("保持大马里奥");break;case FIRE_FLOWER:System.out.println("变为火焰马里奥");mario.setState(new FireMario(mario));break;case ENEMY_ATTACK:System.out.println("变为小马里奥");mario.setState(new SmallMario(mario));break;case FALL_INTO_PIT:System.out.println("马里奥去世了");mario.setState(new DeadMario(mario));break;}}
}
// 实现火焰马里奥状态
class FireMario implements MarioState {private Mario mario;public FireMario(Mario mario) {this.mario = mario;}@Overridepublic void handleEvent(Event event) {switch (event) {case MUSHROOM:System.out.println("保持火焰马里奥");break;case FIRE_FLOWER:System.out.println("保持火焰马里奥");break;case ENEMY_ATTACK:System.out.println("变为大马里奥");mario.setState(new BigMario(mario));break;case FALL_INTO_PIT:System.out.println("马里奥去世了");mario.setState(new DeadMario(mario));break;}}
}
// 定义马里奥类,作为状态的上下文
class Mario {private MarioState state;public Mario() {state = new SmallMario(this);}public void setState(MarioState state) {this.state = state;}public void handleEvent(Event event) {state.handleEvent(event);}
}
// 测试类
public class MarioDemo {public static void main(String[] args) {Mario mario = new Mario();mario.handleEvent(Event.MUSHROOM); // 变为大马里奥mario.handleEvent(Event.FIRE_FLOWER); // 变为火焰马里奥mario.handleEvent(Event.ENEMY_ATTACK); // 变为大马里奥}
}

        在这个简化示例中,我们定义了 MarioState 接口以及实现了 DeadMario 、SmallMario 、 BigMario FireMario 类,分别表示马里奥的四种形态。每个形态类实现了 handleEvent 方法,用于处理不同的游戏事件并根据有限状态机规 则进行状态转换。

        Mario 类作为状态的上下文,用于管理和切换马里奥的状态。它有一个 setState方法,用于更新当前状态。 handleEvent 方法将事件传递给当前状态,以便根据事件执行相应的状态转换。

        在 MarioDemo 测试类中,我们创建了一个 Mario 实例,并通过调用handleEvent 方法模拟游戏中的事件。通过运行这个测试类,你可以观察到马里奥根据有限状态机的规则在不同形态之间切换。

        这个简化示例展示了如何使用有限状态机来实现马里奥角色的形态变化。在实际游戏开发中,你可能需要考虑更多的事件和状态,以及与游戏引擎或框架集成的方式。不过,这个示例可以帮助你理解有限状态机在游戏中的应用。

六、迭代器模式

        迭代器模式,它用来遍历集合对象,不过,很多编程语言都将迭代器作为一个基础的类库,直接提供出来了。在平时的开发中,特别是业务开发,我们直接使用即可,很少会自己去实现一个迭代器。不过弄懂原理能帮助我们更好的使用这些工具类。

        不怎么用,省略,需要可以自行查询。

        

七、访问者模式

        1、概述和原理

        访问者设计模式(Visitor Pattern)是一种行为型设计模式,它允许你再不修改现有类结构的情况下,为类添加新的操作,这种模式可以实现良好的解耦和扩展性,尤其适用于现有类层次结构中添加新的功能的情况。

        访问者模式主要包含以下角色:
        1. 访问者(Visitor):定义一个访问具体元素的接口,为每种具体元素类型声明一个访问操作。
        2. 具体访问者(ConcreteVisitor):实现访问者接口,为每种具体元素提供具体的访问操作实现。
        3. 元素(Element):定义一个接口,声明接受访问者的方法。
        4. 具体元素(ConcreteElement):实现元素接口,提供接受访问者的具体实现。
        5. 对象结构(ObjectStructure
):包含一个元素集合,提供一个方法以遍历这些元素并让访问者访问它们。

        以下是一个简单的访问者模式示例:

        假设我们有一个表示计算机组件的类层次结构(如 CPU、内存和硬盘等),我们需要为这些组件实现一个功能,比如展示它们的详细信息。使用访问者模式,我们可以将【展示详细信息】的功能与【组件类】分离,从而实现解耦和扩展性。
        1、不同的元素(被访问的对象)可以接收不同的访问者。
        2、不同的访问者会对不同的被访问者产生不同的行为。
        3、如果想要扩展,则独立重新实现访问者接口,产生一个新的具体访问者就可以了。
        4、他实际解耦的是【被访问者】和【对被访问者的操作】。

        简单理解就是不同的访问者,到了同一个被访问对象的家里会干不同的事。这个【事】就是行为,通过访问者模式,我们可以将行为和对象分离解耦,如下图。

        下边是一个使用了访问者模式的案例:
// 访问者接口
interface ComputerPartVisitor {// 访问 Computer 对象void visit(Computer computer);// 访问 Mouse 对象void visit(Mouse mouse);// 访问 Keyboard 对象void visit(Keyboard keyboard);
}
// 具体访问者
class ComputerPartDisplayVisitor implements ComputerPartVisitor {// 访问 Computer 对象@Overridepublic void visit(Computer computer) {System.out.println("Displaying Computer.");}// 访问 Mouse 对象@Overridepublic void visit(Mouse mouse) {System.out.println("Displaying Mouse.");}// 访问 Keyboard 对象@Overridepublic void visit(Keyboard keyboard) {System.out.println("Displaying Keyboard.");}
}
// 元素接口
interface ComputerPart {// 接受访问者的访问void accept(ComputerPartVisitor computerPartVisitor);
}
// 具体元素
class Computer implements ComputerPart {// 子元素数组ComputerPart[] parts;public Computer() {// 初始化子元素数组parts = new ComputerPart[]{new Mouse(), new Keyboard()};}// 接受访问者的访问@Overridepublic void accept(ComputerPartVisitor computerPartVisitor) {// 遍历所有子元素并接受访问者的访问for (int i = 0; i < parts.length; i++) {parts[i].accept(computerPartVisitor);}// 访问 Computer 对象本身computerPartVisitor.visit(this);}
}
// 具体元素:鼠标
class Mouse implements ComputerPart {// 接受访问者的访问@Overridepublic void accept(ComputerPartVisitor computerPartVisitor) {// 访问 Mouse 对象computerPartVisitor.visit(this);}
}
// 具体元素:键盘
class Keyboard implements ComputerPart {// 接受访问者的访问@Overridepublic void accept(ComputerPartVisitor computerPartVisitor) {// 访问 Keyboard 对象computerPartVisitor.visit(this);}
}
// 客户端代码
public class VisitorPatternDemo {public static void main(String[] args) {// 创建一个 Computer 对象ComputerPart computer = new Computer();// 创建一个具体访问者ComputerPartVisitor visitor = new ComputerPartDisplayVisitor();// 让 Computer 对象接受访问者的访问computer.accept(visitor);}
}

        在这个示例中,我们定义了一个表示计算机组件的类层次结构,包括 Computer 、Mouse 和 Keyboard 。这些类实现了 ComputerPart 接口,该接口声明了一个接受访问者的方法。我们还定义了一个 ComputerPartVisitor 接口,用于访问这些计算机组件,并为每种组件类型声明了一个访问操作。

        ComputerPartDisplayVisitor 类实现了 ComputerPartVisitor 接口,为每种计算机组件提供了展示详细信息的功能。在客户端代码中,我们创建了一个Computer 对象和一个 ComputerPartDisplayVisitor 对象。当我们调用computer.accept() 方法时,计算机的所有组件都会被访问者访问,并显示相应的详细信息。

        这个示例展示了如何使用访问者模式将功能与类结构分离,实现解耦和扩展性。如果我们需要为计算机组件添加新功能,只需创建一个新的访问者类,而无需修改现有的组件类。这使得在不影响现有代码的情况下,为系统添加新功能变得容易。

// 添加一个更新计算机部件的访问者实现
class ComputerPartUpdateVisitorImpl implements ComputerPartVisitor {// 访问 Computer 对象并执行更新操作@Overridepublic void visit(Computer computer) {System.out.println("Updating Computer.");}// 访问 Mouse 对象并执行更新操作@Overridepublic void visit(Mouse mouse) {System.out.println("Updating Mouse.");}// 访问 Keyboard 对象并执行更新操作@Overridepublic void visit(Keyboard keyboard) {System.out.println("Updating Keyboard.");}
}
// 客户端代码,几乎不用任何修改
public class VisitorPatternDemo {public static void main(String[] args) {// 创建一个 Computer 对象ComputerPart computer = new Computer();// 创建一个具体访问者ComputerPartVisitor visitor = new ComputerPartUpdateVisitorImpl();// 让 Computer 对象接受访问者的访问computer.accept(visitor);}
}

        访问者模式可以算是 23 种经典设计模式中最难理解的几个之一。因为它难理解、难实现,应用它会导致代码的可读性、可维护性变差,所以,访问者模式在实际的软件开发中很少被用到,在没有特别必要的情况下,建议你不要使用访问者模式。

        2、使用场景

        2.1 抽象语法树

        访问者模式在实际项目中的一个常见使用场景是处理抽象语法树(AST)。例如,在编译器或解释器中,我们需要处理不同类型的语法结构,如声明、表达式、循环等。 使用访问者模式,我们可以将处理这些结构的功能与结构类分离,实现解耦和扩展性。
        以下是一个简单的示例,展示了如何使用访问者模式处理抽象语法树

// AST 节点基类
abstract class AstNode {// 接受访问者的方法abstract void accept(AstVisitor visitor);
}
// 访问者接口
interface AstVisitor {// 访问表达式节点的方法void visit(ExpressionNode node);// 访问数字节点的方法void visit(NumberNode node);// 访问加法节点的方法void visit(AdditionNode node);
// 省略其他节点
}
// 数字节点,表示一个整数值
class NumberNode extends AstNode {int value;// 构造方法,接收一个整数作为值NumberNode(int value) {this.value = value;}// 实现基类的 accept 方法,接受访问者void accept(AstVisitor visitor) {visitor.visit(this);}
}
// 加法节点,表示两个子节点的相加
class AdditionNode extends AstNode {AstNode left;AstNode right;// 构造方法,接收两个子节点AdditionNode(AstNode left, AstNode right) {this.left = left;this.right = right;}// 实现基类的 accept 方法,接受访问者void accept(AstVisitor visitor) {visitor.visit(this);}
}
// 表达式节点,包含一个子节点
class ExpressionNode extends AstNode {AstNode node;// 构造方法,接收一个子节点ExpressionNode(AstNode node) {this.node = node;}// 实现基类的 accept 方法,接受访问者void accept(AstVisitor visitor) {visitor.visit(this);}
}
        现在,我们可以创建一个实现了 AstVisitor 接口的类,用于遍历 AST 并计算其结果:
class AstEvaluator implements AstVisitor {int result;AstEvaluator() {result = 0;}void visit(ExpressionNode node) {node.node.accept(this);}void visit(NumberNode node) {result = node.value;}void visit(AdditionNode node) {node.left.accept(this);int leftValue = result;node.right.accept(this);int rightValue = result;result = leftValue + rightValue;}
}

        最后,我们可以使用这个访问者类计算一个简单的 AST

public class Main {public static void main(String[] args) {// 创建一个简单的 AST:(2 + 3)AstNode ast = new ExpressionNode(new AdditionNode(new NumberNode(2),new NumberNode(3)));// 创建一个访问者实例AstEvaluator evaluator = new AstEvaluator();// 使用访问者计算 AST 的结果ast.accept(evaluator);// 输出计算结果System.out.println("AST 的结果是: " + evaluator.result);}
}
        这个示例将输出 AST 的结果是 : 5

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

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

相关文章

大模型学习八:‌Sealos 私有化部署之VMware 安装ubuntu22.04 虚拟机安装(实操)

一、说明 windows 11 ubuntu22.04.5 安装5个虚拟机&#xff0c;3个master 2个node 二、安装 Vmware 17&#xff08;没成功&#xff0c;但你可以成功&#xff09; 我的电脑配置比较旧&#xff0c;直接提示处理器不支持xsave 无法打开虚拟机的电源&#xff0c;网上方法试过了…

Win32++ 使用初探

文章目录 1. 环境要求2. Win32安装3. 项目创建3.1 项目创建&#xff08;1&#xff09;直接使用Win32里的示例Sample&#xff08;2&#xff09;自行创建项目 最近想用 VC写些 UI&#xff0c;但又不太想用 MFC&#xff0c;正好对界面要求不太高&#xff0c;就使用了一下 Win3…

R 语言科研绘图第 38 期 --- 饼状图-玫瑰

在发表科研论文的过程中&#xff0c;科研绘图是必不可少的&#xff0c;一张好看的图形会是文章很大的加分项。 为了便于使用&#xff0c;本系列文章介绍的所有绘图都已收录到了 sciRplot 项目中&#xff0c;获取方式&#xff1a; R 语言科研绘图模板 --- sciRplothttps://mp.…

Linux驱动开发进阶(六)- 多线程与并发

文章目录 1、前言2、进程与线程3、内核线程4、底半步机制4.1、软中断4.2、tasklet4.3、工作队列4.3.1、普通工作项4.3.2、延时工作项4.3.3、工作队列 5、中断线程化6、进程6.1、内核进程6.2、用户空间进程 7、锁机制7.1、原子操作7.2、自旋锁7.3、信号量7.4、互斥锁7.5、comple…

第四节:React Hooks进阶篇-useEffect依赖项为空数组[]与不写的区别

陷阱题&#xff1a;闭包问题、Stale Closure举例 一、依赖项为空数组[]与不写的核心区别 行为空数组[]不写依赖项执行时机仅在组件挂载时执行一次&#xff08;类似componentDidMount&#xff09;组件每次渲染后都执行&#xff08;类似componentDidUpdate&#xff09;更新触发…

【第39节】windows编程:打造MFC版本任务管理器

目录 一、项目概述 二、项目开发的各种功能关键 2.1 进程信息的获取 2.2 线程信息的获取 2.3 进程模块信息的获取 2.3.1 模块快照 2.3.2 枚举模块 2.4 进程堆信息的获取 2.5 窗口信息的获取 2.6 文件信息的获取 2.7 内存信息和CPU占用率的获取 2.7.1 内存信息相关结…

计算轴承|滚动轴承故障频率

一、轴承故障频率概述 在旋转机械故障诊断中&#xff0c;轴承故障频率&#xff08;BPFO、BPFI、BSF、FTF&#xff09;是重要的分析依据。通过计算这些特征频率&#xff0c;可以帮助工程师&#xff1a; 识别轴承故障类型&#xff08;内圈/外圈/滚动体故障&#xff09;制定振动…

【数据结构与算法】ArrayList 和 顺序表

文章目录 &#x1f332;List&#x1f332;1. 线性表&#x1f332;2. 顺序表&#x1f33f;2.1 MyArrayList2.1.1 类中重写所有接口方法1.新增元素2.在pos位置新增元素(指定位置)3.判定是否包含了某个特定元素 4.查找特定元素对应的位置 5.获取pos下标的元素 6.给pos位置的元素替…

OceanBase 推出单机版 ,为中小规模业务提供高性价比方案

近日&#xff0c;OceanBase正式推出了全新的单机版数据库。这款产品基于OceanBase自主研发的单机分布式一体化架构&#xff0c;具有精简的架构设计和出色的兼容性&#xff0c;能够为中小规模业务场景提供高性价比的数据库解决方案&#xff0c;充分满足客户在不同业务规模下的多…

如何在 Vue 3 中实现百度地图位置选择器组件

如何在 Vue 3 中实现百度地图位置选择器组件 前言 在开发前端应用时&#xff0c;地图选择器是一个非常常见的需求。尤其是在一些需要用户选择地址的场景&#xff0c;如电商平台、旅游网站、酒店预定等&#xff0c;百度地图组件能提供准确的地理位置服务。在本文中&#xff0c…

Python中如何用正则表达式精准匹配IP地址?

在网络编程和数据处理时&#xff0c;我们经常需要从文本中提取或验证IP地址。Python的正则表达式(re模块)是完成这个任务的利器。但你知道怎么写才能准确匹配各种合法的IP地址吗&#xff1f;今天我们就来详细探讨这个问题。 为什么需要IP正则表达式&#xff1f; 假设你正在分…

spring--声明式事务

声明式事务 1、回顾事务 要么都成功&#xff0c;要么都失败&#xff01; 事务在项目开发中&#xff0c;十分重要&#xff0c;涉及数据的一致性问题 确保完整性和一致性 事务ACID&#xff1a; 原子性&#xff1a;事务是原子性操作&#xff0c;由一系列动作组成&#xff0c;…

Kotlin 学习-集合

/*** kotlin 集合* List:是一个有序列表&#xff0c;可通过索引&#xff08;下标&#xff09;访问元素。元素可以在list中出现多次、元素可重复* Set:是元素唯一的集合。一般来说 set中的元素顺序并不重要、无序集合* Map:&#xff08;字典&#xff09;是一组键值对。键是唯一的…

WPF 五子棋项目文档

WPF 五子棋项目文档 1. 项目概述 本项目是一个使用 Windows Presentation Foundation (WPF) 技术栈和 C# 语言实现的桌面版五子棋&#xff08;Gomoku&#xff09;游戏。它遵循 MVVM&#xff08;Model-View-ViewModel&#xff09;设计模式&#xff0c;旨在提供一个结构清晰、可…

计算机操作系统——死锁(详细解释和处理死锁)

系列文章目录 计算机操作系统-计算机系统中的死锁 文章目录 系列文章目录前言一、资源问题&#xff1a; 计算机系统当中的死锁&#xff1a; 二、死锁的定义、必要条件和处理方法&#xff1a; 1.死锁的定义&#xff1a;2.产生死锁的必要条件&#xff1a;3.处理死锁的方法&#…

Springboot项目正常启动,访问资源却出现404错误如何解决?

我在自己的springboot项目中的启动类上同时使用了SprinBootApplication和ComponentScan注解, 虽然项目能够正常启动,但是访问资源后,返回404错误,随后在启动类中输出bean,发现controller创建失败: 而后我将ComponentScan去掉后资源就能访问到了. 原因 SprinBootApplication本身…

第十五届蓝桥杯C/C++B组省赛真题讲解(分享去年比赛的一些真实感受)

试题A——握手问题 一、解题思路 直接用高中学的排列组合思路 二、代码示例 #include<bits/stdc.h> using namespace std; int fun(int n) {int sum0;for(int i0;i<n;i){for(int ji1;j<n;j)sum; } return sum; } int main() {cout<<fun(50)-fun(7); }三、…

动态规划(6)——01背包问题

欢迎来到博主的专栏&#xff1a;算法解析 博主ID&#xff1a;代码小号 文章目录 牛客网——【模板】01背包题目解析题目1算法原理题目1题解代码。问题2算法原理问题2题解代码01背包问题的滚动数组优化 牛客网——【模板】01背包 题目解析 关于I/O相关的东西博主就不多赘述了&a…

TQTT_KU5P开发板教程---实现流水灯

文档实现功能介绍 本文档是学习本开发板的基础&#xff0c;通过设置计数器使led0到led7依次闪烁&#xff0c;让用户初步认识vivado基本的开发流程以及熟悉项目的创建。本开发板的所有教程所使用的软件都是vivado2024.1版本的。可以根据网上的教程下载与安装。 硬件资源 此次教程…

Spring 中的 @Cacheable 缓存注解

1 什么是缓存 第一个问题&#xff0c;首先要搞明白什么是缓存&#xff0c;缓存的意义是什么。 对于普通业务&#xff0c;如果要查询一个数据&#xff0c;一般直接select数据库进行查找。但是在高流量的情况下&#xff0c;直接查找数据库就会成为性能的瓶颈。因为数据库查找的…