图解设计模式【3】

本系列共分为三篇文章,其中包含的设计模式如下表:

名称设计模式
图解设计模式【1】Iterator、Adapter、Template Method、Factory Method、Singleton、Prototype、 Builder、Abstract Factory、 Bridge、 Strategy
图解设计模式【2】Composite、 Decorator、 Visitor、 Chain of Responsibility、 Facade、 Mediator、 Observer、 Memento
图解设计模式【3】State、 Flyweight、 Proxy、 Command、 Interpreter、

State模式

State模式中,用类来表示状态。以类来表示状态就可以通过切换类来方便地改变对象的状态。当需要增加新的状态时,如何修改代码这个问题也会很明确。

示例

设计一个简单的报警系统,每秒会改变一次状态。

金库报警系统
- 有一个金库,金库与报警中心相连,金库里有警铃和正常通话用的电话。金库里有时钟,监视着现在的时间。
- 白天的时间范围时9:00-16:59,晚上的时间范围是17:00-23:59和0:00-8:59。
- 金库只能在白天使用。白天使用金库的话,会在报警中心留下记录;晚上使用金库的话,会向报警中心发送紧急事态通知。
- 任何时候都可以使用警铃。使用警铃的话,会向警报中心发送紧急事态通知。
- 任何时候都可以使用警铃。使用警铃的话,会向警报中心发送紧急事态通知。
- 任何时候都可以使用电话(晚上只有留言电话)。白天使用电话的话,会呼叫报警中心。晚上使用电话的话,会呼叫报警中心的留言电话。

如果不使用State模式,我们可以使用如下的伪码逻辑

警报系统的类{使用金库时被调用的方法(){if (白天) {向警报中心报告使用记录} else if (晚上){向警报中心报告紧急事态}警铃响起时被调用的方法(){向警报中心报告紧急事态}正常通话时被调用的方法(){if (白天){呼叫报警中心} else if (晚上){呼叫警报中心的留言电话}}
}

使用了State模式的伪代码

表示白天的状态的类{使用金库时被调用的方法(){向警报中心报告使用记录}警铃响起时被调用的方法(){向警报中心报告紧急事态}正常通话时被调用的方法(){呼叫警报中心}
}
表示晚上的状态的类{使用金库时被调用的方法(){向警报中心报告紧急事态}警铃响起时被调用的方法(){向警报中心报告紧急事态}正常通话时被调用的方法(){呼叫警报中心的留言电话}
}

两者的区别在于,使用State模式,不需要用if语句判断是白天还是晚上。

«abstract»
Context
setClock()
changeState()
callSecurityCenter()
recordLog()
SafeFrame
-state
setClock()
changeState()
callSecurityCenter()
recordLog()
«interface»
State
doClock()
doUse()
doAlarm()
doPhone()
DayState
-singleton$
-DayState()
getInstance()
doClock()
doUse()
doAlarm()
doPhone()
NightState
-singleton$
-NightState()
getInstance()
doClock()
doUse()
doAlarm()
doPhone()

State接口时表示金库状态的接口。其中包括设置时间、使用金库、按下警铃、正常通话等API

public interface State {public abstract void doClock(Context context, int hour);    // 设置时间public abstract void doUse(Context context);                // 使用金库public abstract void doAlarm(Context context);              // 按下警铃public abstract void doPhone(Context context);              // 正常通话
}

DayState类表示白天的状态。该类实现了State接口,因此还实现了State接口中声明的所有方法。

public class DayState implements State {private static DayState singleton = new DayState();private DayState() {                                // 构造函数的可见性是private}public static State getInstance() {                 // 获取唯一实例return singleton;}public void doClock(Context context, int hour) {    // 设置时间if (hour < 9 || 17 <= hour) {context.changeState(NightState.getInstance());}}public void doUse(Context context) {                // 使用金库context.recordLog("使用金库(白天)");}public void doAlarm(Context context) {              // 按下警铃context.callSecurityCenter("按下警铃(白天)");}public void doPhone(Context context) {              // 正常通话context.callSecurityCenter("正常通话(白天)");}public String toString() {                          // 显示表示类的文字return "[白天]";}
}

NightState类表示晚上的状态。它与DateState类一样,也是用了Singleton模式。

public class NightState implements State {private static NightState singleton = new NightState();private NightState() {                              // 构造函数的可见性是private}public static State getInstance() {                 // 获取唯一实例return singleton;}public void doClock(Context context, int hour) {    // 设置时间if (9 <= hour && hour < 17) {context.changeState(DayState.getInstance());}}public void doUse(Context context) {                // 使用金库context.callSecurityCenter("紧急:晚上使用金库!");}public void doAlarm(Context context) {              // 按下警铃context.callSecurityCenter("按下警铃(晚上)");}public void doPhone(Context context) {              // 正常通话context.recordLog("晚上的通话录音");}public String toString() {                          // 显示表示类的文字return "[晚上]";}
}

Context接口是负责管理状态和联系警报中心的接口。

public interface Context {public abstract void setClock(int hour);                // 设置时间public abstract void changeState(State state);          // 改变状态public abstract void callSecurityCenter(String msg);    // 联系警报中心public abstract void recordLog(String msg);             // 在警报中心留下记录
}

SafeFrame类是使用GUI实现警报系统界面的类,它实现了Context接口。

import java.awt.Frame;
import java.awt.Label;
import java.awt.Color;
import java.awt.Button;
import java.awt.TextField;
import java.awt.TextArea;
import java.awt.Panel;
import java.awt.BorderLayout;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;public class SafeFrame extends Frame implements ActionListener, Context {private TextField textClock = new TextField(60);        // 显示当前时间private TextArea textScreen = new TextArea(10, 60);     // 显示警报中心的记录private Button buttonUse = new Button("使用金库");      // 金库使用按钮private Button buttonAlarm = new Button("按下警铃");    // 按下警铃按钮private Button buttonPhone = new Button("正常通话");    // 正常通话按钮private Button buttonExit = new Button("结束");         // 结束按钮private State state = DayState.getInstance();           // 当前的状态// 构造函数public SafeFrame(String title) {super(title);setBackground(Color.lightGray);setLayout(new BorderLayout());//  配置textClockadd(textClock, BorderLayout.NORTH);textClock.setEditable(false);// 配置textScreenadd(textScreen, BorderLayout.CENTER);textScreen.setEditable(false);// 为界面添加按钮Panel panel = new Panel();panel.add(buttonUse);panel.add(buttonAlarm);panel.add(buttonPhone);panel.add(buttonExit);// 配置界面add(panel, BorderLayout.SOUTH);// 显示pack();show();// 设置监听器buttonUse.addActionListener(this);buttonAlarm.addActionListener(this);buttonPhone.addActionListener(this);buttonExit.addActionListener(this);}// 按钮被按下后该方法会被调用public void actionPerformed(ActionEvent e) {System.out.println(e.toString());if (e.getSource() == buttonUse) {           // 金库使用按钮state.doUse(this);} else if (e.getSource() == buttonAlarm) {  // 按下警铃按钮state.doAlarm(this);} else if (e.getSource() == buttonPhone) {  // 正常通话按钮state.doPhone(this);} else if (e.getSource() == buttonExit) {   // 结束按钮System.exit(0);} else {System.out.println("?");}}// 设置时间public void setClock(int hour) {String clockstring = "现在时间是";if (hour < 10) {clockstring += "0" + hour + ":00";} else {clockstring += hour + ":00";}System.out.println(clockstring);textClock.setText(clockstring);state.doClock(this, hour);}// 改变状态public void changeState(State state) {System.out.println("从" + this.state + "状態变为了" + state + "状态。");this.state = state; // 给代表状态的字段赋予表示当前状态的类的实例,就相当于进行了状态迁移。}// 联系警报中心public void callSecurityCenter(String msg) {textScreen.append("call! " + msg + "\n");}// 在警报中心留下记录public void recordLog(String msg) {textScreen.append("record ... " + msg + "\n");}
}

Main类生成了一个SafeFrame类的实例,并且每秒钟调用一次setClock方法。

public class Main {public static void main(String[] args) {SafeFrame frame = new SafeFrame("State Sample");while (true) {for (int hour = 0; hour < 24; hour++) {frame.setClock(hour);   // 设置时间try {Thread.sleep(1000);} catch (InterruptedException e) {}}}}
}

解析

  • State

    State表示状态,定义了根据不同状态进行不同处理的API。该API是那些处理内容依赖于状态的方法的集和。

  • ConcreteState

    ConcreteState表示各个具体的状态,实现了State接口。

  • Context

    Context持有表示当前状态的ConcreteState。它还定义了提供外部调用者使用State模式的API。

Context
state
requestX()
requestY()
requestZ()
«abstract»
State
methodA()
methodB()
methodC()
methodD()
ConcreteState1
methodA()
methodB()
methodC()
methodD()
ConcreteState2
methodA()
methodB()
methodC()
methodD()

编程时,我们经常使用分而治之的方针。这种方针非常适用于大规模的复杂处理。当遇到庞大且复杂的问题,不能用一般的方法解决时,我们会将该问题分解为多个小问题。在State模式中,我们用类来表示状态,并为每一种具体的状态都定义一个相应的类。

State模式易于增加新的状态,但是在State模式中增加其他“依赖于状态的处理”是很困难的。

Flyweight模式

Flyweight是轻量级的意思。关于Flyweight模式,一言以蔽之就是“通过尽量共享实例来避免new出实例“。

示例

Uses
creates
Uses
Uses
BigChar
-charname
-fontdate
print()
BigCharFactory
-pool
-singleton$
-BigCharFactory()
getInstacne()
getBigChar()
BigString
-bigchars
print()
Main

BigChar表示大型字符类。

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;public class BigChar {// 字符名字private char charname;// 大型字符对应的字符串(由'#' '.' '\n'组成)private String fontdata;// 构造函数public BigChar(char charname) {this.charname = charname;try {BufferedReader reader = new BufferedReader(new FileReader("big" + charname + ".txt"));String line;StringBuffer buf = new StringBuffer();while ((line = reader.readLine()) != null) {buf.append(line);buf.append("\n");}reader.close();this.fontdata = buf.toString();} catch (IOException e) {this.fontdata = charname + "?";}}// 显示大型字符public void print() {System.out.print(fontdata);}
}

BigCharFactory类是生成BigChar类的实例的工厂。它实现了共享实例的功能。

import java.util.HashMap;public class BigCharFactory {// 管理已经生成的BigChar的实例private HashMap pool = new HashMap();// Singleton模式private static BigCharFactory singleton = new BigCharFactory();// 构造函数private BigCharFactory() {}// 获取唯一的实例public static BigCharFactory getInstance() {return singleton;}// 生成(共享)BigChar类的实例public synchronized BigChar getBigChar(char charname) {BigChar bc = (BigChar)pool.get("" + charname);if (bc == null) {bc = new BigChar(charname); // 生成BigChar的实例pool.put("" + charname, bc);}return bc;}
}

BigString类是由BigChar组成的大型字符串的类。

public class BigString {// “大型字符”的数组private BigChar[] bigchars;// 构造函数public BigString(String string) {bigchars = new BigChar[string.length()];BigCharFactory factory = BigCharFactory.getInstance();for (int i = 0; i < bigchars.length; i++) {bigchars[i] = factory.getBigChar(string.charAt(i));}}// 显示public void print() {for (int i = 0; i < bigchars.length; i++) {bigchars[i].print();}}
}

Main类

public class Main {public static void main(String[] args) {if (args.length == 0) {System.out.println("Usage: java Main digits");System.out.println("Example: java Main 1212123");System.exit(0);}BigString bs = new BigString(args[0]);bs.print();}
}

解析

  • Flyweight

    按照通常方式编写程序会导致程序变重,所以如果能够共享实例会比较好,而Flyweight表示的就是那些实例会被共享的类。

  • FlyweightFactory

    FlyweightFactory是生成Flyweight的工厂。在工厂中生成Flyweight可以实现共享实例。

  • Client

    Client使用FlyweightFactory来生成Flyweight。

Uses
Uses
Creates
Flyweight
methodA()
methodB()
FlyweightFactory
pool
getFlyweight()
Client

Flyweight模式的主题是共享。在共享实例时,要想到“如果要改变被共享的对象,就会对多个地方产生影响”。在决定Flyweight中的字段时,需要精挑细选。只将那些真正应该在多个地方共享的字段定义在Flyweight中即可。应当共享的信息叫做Intrinsic信息,不应当共享的信息被称作Extrinsic信息

Command模式

一个类在进行工作时会调用自己或是其他类的方法,虽然调用结果会反映在对象的状态中,但是并不会留下工作的历史记录。这时,如果有一个类用来表示“请进行这项工作”的”命令“,就会方便很多。每一项想知道的工作就不再是”方法的调用“这种动态处理,而是一个表示命令的类的实例,可以用”物“来表示。想管理工作的历史记录,只需要管理这些事例的集和即可,而且可以随时再次执行过去的命令,或是将多个过去的命令整合为一个新命令并执行。

示例

Main
history
canvas
cleaButton
DrawCanvas
history
color
radius
draw()
paint()
«interface»
Drawable
draw()
DrawCommand
drawable
position
execute()
MacroCommand
commands
execute()
append()
undo()
clear()
«interface»
Command
execute()

Command接口时表示命令的接口。在该接口中只定义了一个方法,即execute。至于调用execute方法后具体会进行什么样的处理,则取决于实现了Command接口的类。

package command;public interface Command {public abstract void execute();
}

MacroCommand类表示由多条命令整合成的命令。该类实现了Command接口。

package command;import java.util.Stack;
import java.util.Iterator;public class MacroCommand implements Command {// 命令的集合private Stack commands = new Stack();// 执行public void execute() {Iterator it = commands.iterator();while (it.hasNext()) {((Command)it.next()).execute();}}// 添加命令public void append(Command cmd) {if (cmd != this) {commands.push(cmd);}}// 删除最后一条命令public void undo() {if (!commands.empty()) {commands.pop();}}// 删除所有命令public void clear() {commands.clear();}
}

DrawCommand类实现了Command接口,表示绘制一个点的命令。

package drawer;import command.Command;
import java.awt.Point;public class DrawCommand implements Command {// 绘制对象protected Drawable drawable;// 绘制位置private Point position;// 构造函数public DrawCommand(Drawable drawable, Point position) {this.drawable = drawable;this.position = position;}// 执行public void execute() {drawable.draw(position.x, position.y);}
}

Drawable接口是表示绘制对象的接口。draw方法是用于绘制的方法。

package drawer;public interface Drawable {public abstract void draw(int x, int y);
}

DrawCanvas实现了Drawable接口。

package drawer;import command.*;import java.util.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;public class DrawCanvas extends Canvas implements Drawable {// 颜色private Color color = Color.red;// 要绘制的圆点的半径private int radius = 6;// 命令的历史记录private MacroCommand history;// 构造函数public DrawCanvas(int width, int height, MacroCommand history) {setSize(width, height);setBackground(Color.white);this.history = history;}// 重新全部绘制public void paint(Graphics g) {history.execute();}// 绘制public void draw(int x, int y) {Graphics g = getGraphics();g.setColor(color);g.fillOval(x - radius, y - radius, radius * 2, radius * 2);}
}

Main是启动程序的类。

import command.*;
import drawer.*;import java.awt.*;
import java.awt.event.*;
import javax.swing.*;public class Main extends JFrame implements ActionListener, MouseMotionListener, WindowListener {// 绘制的历史记录private MacroCommand history = new MacroCommand();// 绘制区域private DrawCanvas canvas = new DrawCanvas(400, 400, history);// 删除按钮private JButton clearButton  = new JButton("clear");// 构造函数public Main(String title) {super(title);this.addWindowListener(this);canvas.addMouseMotionListener(this);clearButton.addActionListener(this);Box buttonBox = new Box(BoxLayout.X_AXIS);buttonBox.add(clearButton);Box mainBox = new Box(BoxLayout.Y_AXIS);mainBox.add(buttonBox);mainBox.add(canvas);getContentPane().add(mainBox);pack();show();}// ActionListener接口中的方法public void actionPerformed(ActionEvent e) {if (e.getSource() == clearButton) {history.clear();canvas.repaint();}}// MouseMotionListener接口中的方法public void mouseMoved(MouseEvent e) {}public void mouseDragged(MouseEvent e) {Command cmd = new DrawCommand(canvas, e.getPoint());history.append(cmd);cmd.execute();}// WindowListener接口中的方法public void windowClosing(WindowEvent e) {System.exit(0);}public void windowActivated(WindowEvent e) {}public void windowClosed(WindowEvent e) {}public void windowDeactivated(WindowEvent e) {}public void windowDeiconified(WindowEvent e) {}public void windowIconified(WindowEvent e) {}public void windowOpened(WindowEvent e) {}public static void main(String[] args) {new Main("Command Pattern Sample");}
}

解析

Creates
Receiver
action()
ConcreteCommand
receiver
execute()
Client
«abstract»
Command
execute()
Invoker
  • Command

    Command负责定义命令的API。

  • ConcreteCommand

    ConcreteCommand负责实现在Command中定义的API。

  • Reciever

    Receiver是Command执行命令时的对象,可以称之为命令接收者。

  • Client

    Client负责生成ConcreteCommand并分配了Receiver。

  • Invoker

    Invoker是开始执行命令的角色,它会调用在Command中定义的API。

命令的目的不同,应该包含的信息也不同。比如DrawCommand中包含了要绘制的点的位置信息,但不包含点的大小、颜色和形状等信息。

示例中,MacroCommand的实例代表了绘制的历史记录。在该字段中保存了之前所有的绘制信息。如果我们将他保存为文件,就可以永久保存历史记录。

Interpreter模式

Interpreter模式中,程序要解决的问题会被用非常简单的”迷你语言“表述出来,即用”迷你语言“编写的”迷你程序“把具体的问题表述出来。迷你程序是无法单独工作的,还需要用java编写一个负责”翻译”的程序。翻译程序会理解迷你语言,并解释和运行迷你程序。这段翻译程序被称为解释器

示例

Creates
Uses
Main
Context
nextToken()
currentToken()
skipToken()
currentNumber()
«abstract»
Node
parser()
ProgramNode
commandListNode
parse()
RepeatCommandNode
number
commandListNode
parse()
commandListNode
list
parse()
CommandNode
node
parse()
PrimitiveCommandNode
name
parse()
CommandListNode

Node类是语法树中各个部分中最顶层的类。在Node中只声明了一个parse抽象方法,该方法用于进行语法解析处理。

public abstract class Node {public abstract void parse(Context context) throws ParseException;
}

ProgramNode类表示程序<program>

// <program> ::= program <command list>
public class ProgramNode extends Node {private Node commandListNode;public void parse(Context context) throws ParseException {context.skipToken("program");commandListNode = new CommandListNode();commandListNode.parse(context);}public String toString() {return "[program " + commandListNode + "]";}
}

CommandListNode表示<command list>

import java.util.ArrayList;// <command list> ::= <command>* end
public class CommandListNode extends Node {private ArrayList list = new ArrayList();public void parse(Context context) throws ParseException {while (true) {if (context.currentToken() == null) {throw new ParseException("Missing 'end'");} else if (context.currentToken().equals("end")) {context.skipToken("end");break;} else {Node commandNode = new CommandNode();commandNode.parse(context);list.add(commandNode);}}}public String toString() {return list.toString();}
}

CommandNode表示<command>

// <command> ::= <repeat command> | <primitive command>
public class CommandNode extends Node {private Node node;public void parse(Context context) throws ParseException {if (context.currentToken().equals("repeat")) {node = new RepeatCommandNode();node.parse(context);} else {node = new PrimitiveCommandNode();node.parse(context);}}public String toString() {return node.toString();}
}

RepeatCommandNode表示<repeat command>

// <repeat command> ::= repeat <number> <command list>
public class RepeatCommandNode extends Node {private int number;private Node commandListNode;public void parse(Context context) throws ParseException {context.skipToken("repeat");number = context.currentNumber();context.nextToken();commandListNode = new CommandListNode();commandListNode.parse(context);}public String toString() {return "[repeat " + number + " " + commandListNode + "]";}
}

PrimitiveCommandNode表示 <primitive command>

// <primitive command> ::= go | right | left
public class PrimitiveCommandNode extends Node {private String name;public void parse(Context context) throws ParseException {name = context.currentToken();context.skipToken(name);if (!name.equals("go") && !name.equals("right") && !name.equals("left")) {throw new ParseException(name + " is undefined");}}public String toString() {return name;}
}

Context类提供了语法解析所必须的方法。

import java.util.*;public class Context {private StringTokenizer tokenizer;private String currentToken;public Context(String text) {tokenizer = new StringTokenizer(text);nextToken();}public String nextToken() {if (tokenizer.hasMoreTokens()) {currentToken = tokenizer.nextToken();} else {currentToken = null;}return currentToken;}public String currentToken() {return currentToken;}public void skipToken(String token) throws ParseException {if (!token.equals(currentToken)) {throw new ParseException("Warning: " + token + " is expected, but " + currentToken + " is found.");}nextToken();}public int currentNumber() throws ParseException {int number = 0;try {number = Integer.parseInt(currentToken);} catch (NumberFormatException e) {throw new ParseException("Warning: " + e);}return number;}
}

ParseException类是表示语法解析时可能发生异常的类。

public class ParseException extends Exception {public ParseException(String msg) {super(msg);}
}

Main类是启动解释器的程序。

import java.util.*;
import java.io.*;public class Main {public static void main(String[] args) {try {BufferedReader reader = new BufferedReader(new FileReader("program.txt"));String text;while ((text = reader.readLine()) != null) {System.out.println("text = \"" + text + "\"");Node node = new ProgramNode();node.parse(new Context(text));System.out.println("node = " + node);}} catch (Exception e) {e.printStackTrace();}}
}

解析

  • AbstractExpression

    AbstractExpression定义了语法树节点的共同APi。

  • TerminalExpression

    TerminalExpression对应BNF中的终结符表达式。

  • NonterminalExpression

    NonterminalExpression对应BNF中的非终结符表达式。

  • Context

    Context为解释器进行语法解析提供了必要的信息。

  • Client

    为了推导语法树,Client会调用TerminalExpression和NonteminalExpression。

Creates
Uses
Client
Context
getInfoInterpreter()
«abstract»
AbstractExpression
interpret()
TerminalExpression
interpret()
NonterminalExpression
childExpressions
interpret()

迷你语言包括:正则表达式、检索表达式、批处理语言等。

Reference

图解设计模式 【日】结成浩 著 杨文轩 译

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

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

相关文章

(纯新手教学)计算机视觉(opencv)实战十四——模板与多个对象匹配

图片旋转、图片镜像相关教学&#xff1a; &#xff08;纯新手教学&#xff09;计算机视觉&#xff08;opencv&#xff09;实战十三——图片旋转、图片镜像 的几种常用方法-CSDN博客https://blog.csdn.net/2302_78022640/article/details/151356600?spm1011.2415.3001.5331 模板…

Java面试核心知识点总结:Redis与MySQL高可用、高并发解决方案

在分布式系统开发中&#xff0c;高并发场景下的数据一致性、系统可用性以及性能优化始终是核心挑战。本文基于Java技术栈&#xff0c;结合Redis与MySQL的工程实践&#xff0c;系统梳理分布式系统设计的关键技术要点。一、Redis集群架构演进与高可用实践1.1 主从哨兵模式部署方案…

R 语言科研绘图第 72 期 --- mantel检验图

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

4.2-中间件之MySQL

4.2.1MySQL的基本知识SQL语句用于存取数据以及查询、更新和管理关系数据库系统。包括&#xff1a;DQL&#xff08;select&#xff09;、DML&#xff08;insert,update,delete&#xff09;、DDL&#xff08;create,alter,drop&#xff09;、DCL&#xff08;grant,revoke&#xf…

LVS + Keepalived 高可用负载均衡集群

目录 一、核心组件与作用 1. LVS&#xff08;Linux Virtual Server&#xff09; 2. Keepalived 二、DR 模式下的 LVS Keepalived 工作原理 1. 整体架构 2. 数据包流向&#xff08;DR 模式&#xff09; 三、部署步骤&#xff08;DR 模式&#xff09; 3.1 环境规划 3.2…

知识沉淀过于碎片化如何形成体系化框架

要将过于碎片化的知识沉淀转变为体系化的框架&#xff0c;必须采取一套自上而下设计与自下而上归集相结合的系统性方法&#xff0c;其核心路径在于首先进行战略性诊断与顶层蓝图设计、其次构建统一且可扩展的知识架构&#xff08;分类与标签体系&#xff09;、然后实施系统性的…

XLua教程之C#调用Lua

上一篇文章 XLua教程之入门篇-CSDN博客 在C#脚本中访问lua全局数据&#xff0c;特别是table以及function&#xff0c;代价比较大&#xff0c;建议尽量少做相关操作。 LuaEnv.Global.Get 用于获取一个全局变量&#xff0c;但是无法获取局部变量(用local修饰) 全局基本类型变量…

C++ 标准库中的哈希函数:从std::hash到自定义哈希器

C 标准库中的哈希函数&#xff1a;从 std::hash 到自定义哈希器 1. 引言 在上一篇中&#xff0c;我们介绍了哈希表为什么能够实现 O(1) 查找。 核心秘密在于&#xff1a;哈希函数。 在 C 标准库中&#xff0c;哈希表容器&#xff08;如 unordered_map、unordered_set&#xff0…

在图形 / 游戏开发中,为何 Pixels Per Unit(PPU)数值越小,物体在屏幕上显示的尺寸越大?

1. 什么是 PPU&#xff1f; PPU&#xff08;Pixels Per Unit&#xff09;指的是 多少像素对应游戏世界中的一个单位&#xff08;Unit&#xff09;。 在 Unity 等游戏引擎中&#xff0c;1 Unit 通常被视为世界空间的基本长度&#xff0c;比如 1 米。2. PPU 与物体大小的关系PPU …

【ZYNQ开发篇】Petalinux和电脑端的静态ip地址配置

使用Petalinux工具为ZYNQ板卡搭建嵌入式Linux操作系统&#xff0c;成功搭建后&#xff0c;用户通常会使用客户端软件对ZYNQ板卡上的Linux系统进行访问&#xff0c;软件需要知道ZYNQ板卡的ip地址才能进行访问&#xff0c;如果ip地址是动态变化的&#xff0c;软件每次访问都要重新…

AVL树知识总结

AVL树概念性质一颗AVL树或是空树&#xff0c;或者具有一下性质的二叉搜索树&#xff1a;左右都是AVL树&#xff0c;左右子树高度差的绝对值不超过1AVL树有n个结果&#xff0c;高度保持在O&#xff08;logN&#xff09; 搜索时间复杂度O(logN&#xff09;模拟实现插入定义&#…

返利app的跨域问题解决方案:CORS与反向代理在前后端分离架构中的应用

返利app的跨域问题解决方案&#xff1a;CORS与反向代理在前后端分离架构中的应用 大家好&#xff0c;我是阿可&#xff0c;微赚淘客系统及省赚客APP创始人&#xff0c;是个冬天不穿秋裤&#xff0c;天冷也要风度的程序猿&#xff01; 在返利APP的前后端分离架构中&#xff0c;跨…

【dl】python基础 深度学习中需要用到的python基础

直接在jupyter写笔记然后导出md格式真的太好用了本文笔记来自小破站视频BV1K14y1c75ePython 基础 1. 变量 1.1 三种基本变量类型 # 字符串 str str_v "123"# 数字 int或float num_v 11 float_v 12.0# 布尔型 bool bool_v True1.1.1 字符串 f字符串&#xff1a;在…

Vue FullPage.js 完整使用指南:Vue 3 官方全屏滚动解决方案

概述 vue-fullpage.js 是 FullPage.js 的官方 Vue.js 3 包装器&#xff0c;为 Vue 3 应用提供了强大的全屏滚动功能。该插件基于成熟的 FullPage.js 库&#xff0c;支持多种滚动效果和丰富的配置选项&#xff0c;特别适用于企业级数据大屏、产品展示、单页应用等场景。 官方信…

软件工程实践一:Git 使用教程(含分支与 Gitee)

文章目录目标一、快速上手1. Windows 安装 Git2. 初始化 / 克隆二、核心概念速览三、常用命令清单1) 查看状态与差异2) 添加与提交3) 历史与回溯4) 撤销与恢复&#xff08;Git 2.23 推荐新命令&#xff09;5) 忽略文件四、分支与合并&#xff08;Branch & Merge&#xff09…

css`min()` 、`max()`、 `clamp()`

min() 用来计算多个数值中最小的那个&#xff0c;非常适合做自适应。 width: min(50vw, 500px) 50vw 表示 视口宽度的 50% 500px 表示 500px min(50vw, 500px) 表示会取两者中 最小的那个 作为最终的宽度&#xff0c;。 使用场景 限制某个元素宽度不超过某个值&#xff1b; 响…

【WRF-VPRM 预处理器】HEG 安装(服务器)-MRT工具替代

目录 HEG 安装 验证 HEG 安装与否 设置环境变量(建议) 命令行接口(Command Line Interface) hegtool 工具 hegtool 用法 Header File 格式 功能1:`gdtif` 工具 – MISR 数据处理 `gdtif` 使用方式 参数文件格式(Parameter File Format) 功能2:`resample` 工具 – 重采样…

PyTorch 神经网络

神经网络是一种模仿人脑神经元链接的计算模型&#xff0c; 由多层节点组成&#xff0c; 用于学习数据之间的复杂模式和关系。神经网络通过调整神经元之间的连接权重来优化预测结果&#xff0c;这个过程可以涉及到向前传播&#xff0c;损失计算&#xff0c;反向传播和参数更新。…

详细解析苹果iOS应用上架到App Store的完整步骤与指南

&#x1f4f1;苹果商店上架全流程详解 &#x1f469;‍&#x1f4bb;想要将你的App上架到苹果商店&#xff1f;跟随这份指南&#xff0c;一步步操作吧&#xff01; 1️⃣ 申请开发者账号&#xff1a;访问苹果开发者网站&#xff0c;注册并支付99美元年费&#xff0c;获取开发者…

三维GIS开发实战!Cesium + CZML 实现火箭飞行与分离的 3D 动态模拟

CZML是一种基于JSON的数据格式&#xff0c;专门用于在Cesium中描述3D场景和时间动态数据。本文将详细介绍了CZML的特点&#xff08;JSON格式、时间动态性、层次结构等&#xff09;和基本组件&#xff0c;并给出了一个火箭发射的实例。通过搭建Cesium开发环境&#xff08;使用vi…