当前位置: 首页 > news >正文

设计模式(2)行为型模式和七大原则

1、目标

本文的主要目标是学习设计模式的行为型模式并举例说明

2、行为型模式

2.1 观察者模式(Observer)

观察者模式是对象之间存在一对多的依赖关系,当一个对象的状态发生变化时,所有依赖它的对象都会得到通知并自动更新,这里的对象指的是Observable目标,依赖目标的对象是Observer观察者,Observer观察者必须调用addObserver方法将Observer观察者注册到Observable目标的容器中,Observable目标监听到事件发生会调用notify方法通知所有的Observer观察者自动更新,观察者模式又叫做发布订阅模式,它的优点是如果新增一个功能监听事件发生,可以创建一个新的Observer观察者,不需要修改Observable目标,耦合性小

需求:用户点击页面会先查询消费者的地址然后展示欢迎页
分析:在点击页面的代码前后加上这两个逻辑,硬编码,如果不同公司的用户点击页面的行为不一样呢

优化思路:采用观察者模式,定义Observer接口,每次添加一个逻辑会创建一个观察者对象,观察者对象需要先注册到Customer对象中的容器,然后调用notify方法会发布消息会循环调用每个观察者对象的update方法,Java提供了Observer观察者对象和Observable目标

程序:

public class ObservableFactory {public void clickPage(Customer customer, List<Observer> observerList) {for (Observer observer : observerList) {customer.addObserver(observer);}customer.clickPage();}public class Customer extends Observable {// 用户点击页面,会查询消费者的地址和展示欢迎邮件public void clickPage() {// 必须先设置changed=true才会循环调用Observer的update方法// Observable类的setChanged方法是protected只能在这个包下或者子类调用setChanged方法,// 每个业务对变化的定义不同,因此它是由子类判断是否变化super.setChanged();super.notifyObservers();}}public class WelcomeLetter implements Observer {@Overridepublic void update(Observable o, Object arg) {System.out.println("观察者Observer WelcomLetter向目标 " + o + " 发送一个欢迎信");}}public class AddVerify implements Observer {@Overridepublic void update(Observable o, Object arg) {System.out.println("观察者Observer AddVerify向目标 " + o + " 发送一个验证消费者的地址");}}}

Java包含了观察者模式的类,Observer类是观察者,Observable类是目标

创建WelcomeLetter、AddVerify两个Observer观察者对象,重写update方法

创建Customer类,继承Observable类,创建clickPage方法,当用户点击页面会先调用setChanged方法然后调用notifyObservers方法通知所有的观察者对象

public interface Observer {void update(Observable o, Object arg);
}

Observer接口包含update方法,入参是Observable目标和参数arg

public class Observable {private boolean changed = false;private Vector<Observer> obs;public Observable() {obs = new Vector<>();}public synchronized void addObserver(Observer o) {if (o == null)throw new NullPointerException();if (!obs.contains(o)) {obs.addElement(o);}}public synchronized void deleteObserver(Observer o) {obs.removeElement(o);}public void notifyObservers() {notifyObservers(null);}public void notifyObservers(Object arg) {Object[] arrLocal;synchronized (this) {if (!changed)return;arrLocal = obs.toArray();clearChanged();}for (int i = arrLocal.length-1; i>=0; i--)((Observer)arrLocal[i]).update(this, arg);}public synchronized void deleteObservers() {obs.removeAllElements();}protected synchronized void setChanged() {changed = true;}protected synchronized void clearChanged() {changed = false;}public synchronized boolean hasChanged() {return changed;}public synchronized int countObservers() {return obs.size();}
}

Observable类包含changed表示是否变化的标志和obs这个Vector容器,它保存了Observer集合,addObserver方法可以将Observer观察者对象添加到Vector容器中

notifyObservers方法会先判断changed必须是true才会复制Vector容器的元素到一个数组中,然后从后向前逆序遍历数组并调用Observer观察者对象的update方法,逆序是因为防止并发场景,如果正序遍历在循环遍历的时候某个观察者对象从Vector容器中取消了会导致数组的索引变化了,导致并发修改异常

setChanged方法是protected类型的,因为每个业务都变化的定义都不相同,因此可以创建一个子类继承Observable类自己定义什么时候需要调用setChanged方法表示数据变化了

public class TaskController {@Testpublic void f2() {ObservableFactory observableFactory = new ObservableFactory();ObservableFactory.Customer customer = observableFactory.new Customer();ObservableFactory.WelcomeLetter welcomeLetter = observableFactory.new WelcomeLetter();ObservableFactory.AddVerify addVerify = observableFactory.new AddVerify();List<Observer> list = new ArrayList<>(2);list.add(welcomeLetter);list.add(addVerify);observableFactory.clickPage(customer, list);}
}

TaskController是创建Customer对象、WelcomeLetter、AddVerify,调用clickPage方法可以发布并且观察者可以得到通知并自动更新

测试结果:

在这里插入图片描述

目标对象发布消息,观察者会得到通知并自动更新

2.2 状态模式(State)

状态模式是对象有多种状态,不同状态之间的变化封装在不同的状态类中,思路是定义一个状态接口和多个状态实现类,每个状态实现类都重写转换到其他状态的方法,最后定义一个活动类,通过map.get得到对应的状态类并调用方法

程序:

public class StateFactory {public class Activity {private String state;private Map<String, ActivityState> map = new HashMap<>();public Activity() {state = "结束";map.put("启动", new ActivityStartedState());map.put("暂停", new ActivityResumedState());map.put("结束", new ActivityEndedState());}public void setState(String state) {this.state = state;}public String getState() {return state;}public void startActivity() {map.get(state).startActivity(this);}public void resumeActivity() {map.get(state).resumeActivity(this);}public void endActivity() {map.get(state).endActivity(this);}}public interface ActivityState {public abstract void startActivity(Activity activity);public abstract void resumeActivity(Activity activity);public abstract void endActivity(Activity activity);}public class ActivityStartedState implements ActivityState {@Overridepublic void startActivity(Activity activity) {System.out.println("活动已经启动了,不能重复启动");}@Overridepublic void resumeActivity(Activity activity) {System.out.println("活动暂停");activity.setState("暂停");System.out.println("state = " + activity.getState());}@Overridepublic void endActivity(Activity activity) {System.out.println("活动结束");activity.setState("结束");System.out.println("state = " + activity.getState());}}public class ActivityResumedState implements ActivityState {@Overridepublic void startActivity(Activity activity) {System.out.println("活动重新启动");activity.setState("启动");System.out.println("state = " + activity.getState());}@Overridepublic void resumeActivity(Activity activity) {System.out.println("活动已经暂停了,不能重复暂停");}@Overridepublic void endActivity(Activity activity) {System.out.println("活动结束");activity.setState("结束");System.out.println("state = " + activity.getState());}}public class ActivityEndedState implements ActivityState {@Overridepublic void startActivity(Activity activity) {System.out.println("活动启动");activity.setState("启动");System.out.println("state = " + activity.getState());}@Overridepublic void resumeActivity(Activity activity) {System.out.println("活动已经结束,不能暂停");}@Overridepublic void endActivity(Activity activity) {System.out.println("活动已经结束,不能重复结束");}}}

定义一个状态接口和多个状态实现类,每个状态类都重写转换到其他状态的方法,还定义了一个活动类,封装了state状态表示活动的当前状态,封装了一个Map可以存放不同的state和对应的状态对象,通过map.get方法得到状态对象并调用状态对象的方法

@Test
public void f18() {StateFactory stateFactory = new StateFactory();StateFactory.Activity activity = stateFactory.new Activity();activity.startActivity();activity.resumeActivity();activity.endActivity();
}

创建一个活动对象,活动对象的状态初始化是结束状态,先调用活动对象的start方法可以从结束状态转换到启动状态,然后调用活动对象的resume方法可以从启动状态转换到暂停状态,最后调用活动对象的end方法可以从暂停状态转换到结束状态

测试结果:

在这里插入图片描述

测试结果表明状态模式应用成功,可以正常切换活动状态

2.3 责任链模式(Chain Of Responsibility)

责任链模式是允许多个对象都可以处理请求,会将这些对象连成一个链,直到有一个对象可以处理请求为止,思路是定义一个抽象类,组合一个对象因为每个对象都持有下一个对象的引用

程序:

public class LoggerFactory {abstract public class Logger {protected Logger nextLogger;public void setNextLogger(Logger nextLogger) {this.nextLogger = nextLogger;}abstract public void log(int logLevel);}public class DebugLogger extends Logger {@Overridepublic void log(int logLevel) {if(logLevel == 0) {System.out.println("打印debug日志");} else {nextLogger.log(logLevel);}}}public class InfoLogger extends Logger {@Overridepublic void log(int logLevel) {if(logLevel == 1) {System.out.println("打印info日志");} else {nextLogger.log(logLevel);}}}public class ErrorLogger extends Logger {@Overridepublic void log(int logLevel) {if(logLevel == 2) {System.out.println("打印error日志");} else {nextLogger.log(logLevel);}}}}

定义一个抽象类,它组合了下一个对象,抽象方法log方法用来打印日志,多个继承类可以重写log方法,如果条件不满足就调用下一个对象的log方法

@Test
public void f19() {LoggerFactory loggerFactory = new LoggerFactory();LoggerFactory.DebugLogger debugLogger = loggerFactory.new DebugLogger();LoggerFactory.InfoLogger infoLogger = loggerFactory.new InfoLogger();LoggerFactory.ErrorLogger errorLogger = loggerFactory.new ErrorLogger();debugLogger.setNextLogger(infoLogger);infoLogger.setNextLogger(errorLogger);debugLogger.log(2);debugLogger.log(1);debugLogger.log(0);
}

创建一个对象链,如果这个对象满足条件会打印日志,如果不满足条件会沿着对象链调用下一个对象的打印日志方法

输出结果:

在这里插入图片描述

第一个方法打印日志是error,第二个方法打印日志是info,第三个方法打印日志是debug

2.4 策略模式(Strategy)

策略模式是定义多个算法,它们可以相互替换,定义一个抽象类和多个实现类,寻找可变的参数并封装到这个抽象类中

需求:处理订单计算不同国家的税

分析:直接if else,硬编码,如果情况很多会有很多if else

优化思路:计算税用一个抽象类,定义一个抽象方法,多个实现类重写这个方法,就可以计算不同国家的税

程序:

public class SalesOrder {// CalcTax作为入参public void process(CalcTax calcTax, int num, double price) {double total = calcTax.taxAmount(num, price);System.out.println(calcTax.getClass() + "  total = " + total);}// 成员内部类public abstract class CalcTax {abstract public double taxAmount(int num, double price);}// 成员内部类public class USTax extends CalcTax {@Overridepublic double taxAmount(int num, double price) {return num * price * 2;}}// 成员内部类public class CanTax extends CalcTax {@Overridepublic double taxAmount(int num, double price) {return num * price * 1.5;}}}

CalcTax抽象类和继承类都封装在SalesOrder类中,CalcTax抽象类作为SalesOrder类的process方法的入参

public class TaskController {@Testpublic void f1() {SalesOrder salesOrder = new SalesOrder();SalesOrder.USTax usTax = salesOrder.new USTax();salesOrder.process(usTax, 2, 15);SalesOrder.CanTax canTax = salesOrder.new CanTax();salesOrder.process(canTax, 2, 15);}
}

TaskController类的f1方法创建SalesOrder类的对象和成员内部类的对象USTax和CanTax,最后SalesOrder对象调用process方法,入参传入成员内部类的对象USTax和CanTax

测试结果是:

在这里插入图片描述

2.5 模板方法模式(Template Method)

模板方法模式是定义算法的骨架,将一些步骤推迟到子类中实现,父类定义多个抽象方法,子类实现这些抽象方法,父类还定义了一个普通方法,这个普通方法封装了这些抽象方法

需求:不同数据库连接数据库并且查询数据库的数据

分析:对每个数据库都创建一个方法,重复代码多

优化思路:创建一个抽象类,封装了连接数据库的抽象方法和查询数据库的抽象方法,还封装了一个普通方法,这个普通方法封装了这些抽象方法,每个数据库都创建一个实现类,重写连接数据库的抽象方法和查询数据库的抽象方法

程序:

public class QueryDBFactory {public void doQuery(QueryDBTemplate queryDBTemplate) {queryDBTemplate.doQuery();}abstract public class QueryDBTemplate {abstract public void getConnection();abstract public void selectFromDB();public void doQuery() {getConnection();selectFromDB();}}public class MysqlDB extends QueryDBTemplate {@Overridepublic void getConnection() {System.out.println("MysqlDB获取连接");}@Overridepublic void selectFromDB() {System.out.println("MysqlDB查询数据库的数据");}}public class OracleDB extends QueryDBTemplate {@Overridepublic void getConnection() {System.out.println("OracleDB获取连接");}@Overridepublic void selectFromDB() {System.out.println("OracleDB查询数据库的数据");}}}

定义一个抽象类QueryDBTemplate,定义抽象方法getConnection和selectFromDB,抽象类中还定义了一个普通方法doQuery,doQuery方法封装了这两个抽象方法,可以减少重复代码

创建两个子类分别表示不同的数据库,每个子类都重写这两个抽象方法

public class TaskController {@Testpublic void f3() {QueryDBFactory queryDBFactory = new QueryDBFactory();QueryDBFactory.QueryDBTemplate queryDBTemplate = queryDBFactory.new MysqlDB();queryDBFactory.doQuery(queryDBTemplate);System.out.println("========================================");queryDBTemplate = queryDBFactory.new OracleDB();queryDBFactory.doQuery(queryDBTemplate);}
}

分别采用Mysql和Oracle的子类查询数据库

测试结果:

在这里插入图片描述

模板模式在抽象类中定义普通方法是每个数据库都先获取连接然后查询数据库的数据

2.6 命令模式(Command)

命令模式是将请求和执行解耦合,使得请求不知道执行的具体实现,用于封装操作并延迟执行这些操作,思路是创建一个命令接口,创建多个命令类实现这个接口,命令类中组合了请求对象

程序:

public class CommandFactory {public interface Command {void execute();}public class Person {public void work() {System.out.println("工作");}public void sport() {System.out.println("运动");}public void sleep() {System.out.println("睡觉");}}public class WorkCommand implements Command {private Person person;public WorkCommand(Person person) {this.person = person;}@Overridepublic void execute() {person.work();}}public class SportCommand implements Command {private Person person;public SportCommand(Person person) {this.person = person;}@Overridepublic void execute() {person.sport();}}public class SleepCommand implements Command {private Person person;public SleepCommand(Person person) {this.person = person;}@Overridepublic void execute() {person.sleep();}}}

创建一个命令接口Command,创建多个实现类作为命令,组合了Person对象

@Test
public void f13() {CommandFactory commandFactory = new CommandFactory();CommandFactory.Person person = commandFactory.new Person();CommandFactory.WorkCommand workCommand = commandFactory.new WorkCommand(person);CommandFactory.SportCommand sportCommand = commandFactory.new SportCommand(person);CommandFactory.SleepCommand sleepCommand = commandFactory.new SleepCommand(person);workCommand.execute();sportCommand.execute();sleepCommand.execute();
}

测试结果:

在这里插入图片描述

命令模式会创建多个对象,然后执行

2.7 迭代器模式(Iterator)

迭代器模式是顺序遍历数组的所有元素,不用关心数组的实现,可以将遍历和数组的实现解耦合

程序:

public class IteratorFactory {public interface Iterator {public boolean hasNext();public Object next();}public class Hobbies implements Iterator {private String[] arr;private int index;public Hobbies(String[] arr) {this.arr = arr;index = 0;}@Overridepublic boolean hasNext() {return index < arr.length;}@Overridepublic Object next() {return arr[index++];}}}

通过迭代器遍历数组,需要先创建迭代器接口,创建一个类实现迭代器接口

@Test
public void f14() {IteratorFactory iteratorFactory = new IteratorFactory();String[] arr = new String[]{"a","b","c","d","e"};IteratorFactory.Hobbies hobbies = iteratorFactory.new Hobbies(arr);while (hobbies.hasNext()) {System.out.println(hobbies.next());}
}

通过迭代器对象遍历数组,可以将遍历和数组的实现解耦合

测试结果:

在这里插入图片描述

迭代器模式可以顺序遍历数组中的所有元素

2.8 中介者模式(Mediator)

中介者模式是用一个中介者对象封装对象之间的交互,可以降低对象之间的耦合

程序:

public class MediatorFactory {// 中介者public class QQ {public void sendMsg(User sender, User receiver, String msg) {System.out.println(sender.getName() + " send msg: " + msg + " to " + receiver.getName());}}public class User {private String name;private QQ qq;public User(String name, QQ qq) {this.name = name;this.qq = qq;}public String getName() {return name;}// 用户通过QQ中介者发送消息public void sendMsg(String msg, User receiver) {qq.sendMsg(this, receiver, msg);}}}

用户对象之间的交互需要通过QQ这个中介者对象进行交互,因此用户对象应该持有中介者对象QQ

@Test
public void f15() {MediatorFactory mediatorFactory = new MediatorFactory();MediatorFactory.QQ qq = mediatorFactory.new QQ();MediatorFactory.User user01 = mediatorFactory.new User("user01", qq);MediatorFactory.User user02 = mediatorFactory.new User("user02", qq);user01.sendMsg("hello", user02);user02.sendMsg("hi", user01);
}

用户对象之间发送消息必须通过中介者对象QQ

测试结果:

在这里插入图片描述

用户对象发送消息会通过中介者对象QQ

2.9 备忘录模式(Memento)

备忘录模式是在破坏对象封装性的前提下恢复状态,思路是创建备忘录对象来保存对象状态

程序:

public class MementoFactory {public class Memento {private String state;public Memento(String state) {this.state = state;}public String getState() {return state;}public void setState(String state) {this.state = state;}}public class Activity {private String state;public String getState() {return state;}public Memento begin() {state = "开始";return new Memento(state);}public Memento resume() {state = "暂停";return new Memento(state);}public Memento end() {state = "结束";return new Memento(state);}public void restore(Memento memento) {state = memento.getState();}}}

创建备忘录类Memento,Activity活动类在修改活动状态的时候会返回备忘录对象,通过restore方法可以恢复活动对象的状态

@Test
public void f16() {MementoFactory mementoFactory = new MementoFactory();MementoFactory.Activity activity = mementoFactory.new Activity();MementoFactory.Memento beginMemento = activity.begin();MementoFactory.Memento resumeMemento = activity.resume();MementoFactory.Memento endMemento = activity.end();activity.restore(endMemento);System.out.println(activity.getState());activity.restore(resumeMemento);System.out.println(activity.getState());activity.restore(beginMemento);System.out.println(activity.getState());
}

创建活动对象,创建备忘录对象,通过restore方法可以恢复活动状态

测试结果:

在这里插入图片描述

备忘录模式可以恢复活动状态

2.10 解释器模式(Interpreter)

解释器模式是解释执行语言的表达式,会将每个表达式抽象成一个类,通过组合表达式可以构建复杂的表达式

程序:

public class InterpreterFactory {public interface Interpreter {int interpreter();}public class NumberInterpreter implements Interpreter {private int num;public NumberInterpreter(int num) {this.num = num;}@Overridepublic int interpreter() {return num;}}public class AddInterpreter implements Interpreter {private Interpreter number1;private Interpreter number2;public AddInterpreter(Interpreter number1, Interpreter number2) {this.number1 = number1;this.number2 = number2;}@Overridepublic int interpreter() {return number1.interpreter() + number2.interpreter();}}}

解释器模式会定义一个解释器接口,定义数字解释器和相加操作的解释器,重写interpreter方法

@Test
public void f17() {InterpreterFactory interpreterFactory = new InterpreterFactory();InterpreterFactory.NumberInterpreter number1 = interpreterFactory.new NumberInterpreter(3);InterpreterFactory.NumberInterpreter number2 = interpreterFactory.new NumberInterpreter(6);InterpreterFactory.AddInterpreter addInterpreter = interpreterFactory.new AddInterpreter(number1, number2);System.out.println(addInterpreter.interpreter());
}

定义两个数字解释器和一个相加操作的解释器,计算相加操作的结果

测试结果:

在这里插入图片描述

解释器模式可以计算多个表达式得到结果

2.11 访问者模式(Visitor)

访问者模式是在不改变元素类的前提下,定义这些元素的操作,将数据结构和操作分离开,使得操作可以独立的变化

程序:

public class VisitorFactory {public interface Visitor {abstract public void visitElement1(Element1 element1);abstract public void visitElement2(Element2 element2);}public class RealVisitor implements Visitor {@Overridepublic void visitElement1(Element1 element1) {element1.log1();}@Overridepublic void visitElement2(Element2 element2) {element2.log2();}}public interface Element {void accept(Visitor visitor);}public class Element1 implements Element {@Overridepublic void accept(Visitor visitor) {visitor.visitElement1(this);}public void log1() {System.out.println("打印元素1的日志");}}public class Element2 implements Element {@Overridepublic void accept(Visitor visitor) {visitor.visitElement2(this);}public void log2() {System.out.println("打印元素2的日志");}}}

定义元素,定义访问者接口和实现类

@Test
public void f20() {VisitorFactory visitorFactory = new VisitorFactory();VisitorFactory.Element1 element1 = visitorFactory.new Element1();VisitorFactory.Element2 element2 = visitorFactory.new Element2();VisitorFactory.RealVisitor realVisitor = visitorFactory.new RealVisitor();element1.accept(realVisitor);element2.accept(realVisitor);
}

调用元素的accept方法,可以调用访问者visitor的方法

测试结果:

在这里插入图片描述

3、七大设计原则

设计模式的七大原则是

(1)单一职责原则:一个类只负责一个职责

(2)开闭原则:对扩展开放,对修改关闭

(3)里氏替换原则:子类可以替换父类的方法保证程序运行正常,子类可以实现父类没有的方法完成新增功能,但是子类不应该重写父类的方法

假设有一个父类 Bird 和一个子类 Penguin。假设 Bird 类有一个方法 fly(),表示鸟类能够飞行。如果我们按照里氏替换原则,Penguin 类(企鹅)继承自 Bird,但企鹅是不会飞的
如果我们在 Penguin 类中实现了 fly() 方法,且它要么做得不如 Bird 类中的 fly() 方法好(即不能飞),要么导致错误或异常(例如抛出不适当的异常),则违反了里氏替换原则
正确的做法是将 fly() 方法移除或使其在 Penguin 中不适用,或者将 Penguin 类从 Bird 类中移除,因为企鹅并不符合“可以飞”的行为约定。在这种情况下,我们可能会创建一个 FlyingBird 类来继承自 Bird,而 Penguin 类则从 Bird 类中移除,确保只有那些确实可以飞的鸟类继承 FlyingBird

(4)依赖反转原则:抽象不要依赖细节,细节应该依赖抽象,这里抽象指的是接口和抽象类

(5)接口隔离原则:尽可能将一个接口拆分成多个小接口,客户端不应该依赖它不需要的接口

(6)合成复用原则:尽量使用组合替换继承

(7)迪米特法则:最少知识原则,一个类应该保持对另一个类最小的了解

相关文章:

设计模式(2)行为型模式和七大原则

1、目标 本文的主要目标是学习设计模式的行为型模式并举例说明 2、行为型模式 2.1 观察者模式&#xff08;Observer&#xff09; 观察者模式是对象之间存在一对多的依赖关系&#xff0c;当一个对象的状态发生变化时&#xff0c;所有依赖它的对象都会得到通知并自动更新&…...

学懂C++(三十一):高级教程——深入详解C++高级多线程编程技术之锁优化与替代

引言 随着多核处理器的普及&#xff0c;多线程编程技术已经成为提高应用程序性能的关键手段。在多线程环境下&#xff0c;如何高效、安全地管理线程之间的共享资源是开发者面临的主要挑战。传统的锁机制&#xff0c;如互斥锁&#xff08;Mutex&#xff09;、临界区&#xff08;…...

Linux - 基础工具使用

文章目录 一、yum1、介绍2、功能3、语法4、使用 二、rzsz1、安装rzsz的指令2、介绍3、使用 三、vim基础使用1、介绍2、基础使用 四、gcc/g使用1、生成可执行文件过程2、语法3、常用选项4、编译过程5、动静态库6、包含头文件的多文件编译7、链接外部库 一、yum 1、介绍 Linux中…...

理解线程id和简单封装原生线程库

一、理解线程id 首先我们要知道给用户提供的线程id不是内核里面LWP&#xff08;轻量级进程id&#xff09;&#xff0c;而是pthread库自己维护的一个唯一值。 我们理解为什么线程id不是内核里面LWP&#xff0c;因为用户没有权限使用内核里面的字段&#xff0c;那是专门给OS管理…...

Unified 阻抗控制 architecture、framework、approach

Unified 阻抗控制&#xff08;Unified Impedance Control&#xff09;作为一种控制策略&#xff0c;其architecture&#xff08;架构&#xff09;、framework&#xff08;框架&#xff09;和approach&#xff08;方法&#xff09;为&#xff1a; 一、Unified 阻抗控制 Archite…...

Java后端面试题(mq相关)(day9)

目录 为什么用MQ&#xff1f; 异步 、削峰、解耦1. 异步处理2. 解耦3. 削峰填谷 Exchange类型什么是死信队列&#xff1f;如何保证消息的可靠性&#xff1f;RabbitMQ中如何解决消息堆积问题?RabbitMQ中如何保证消息有序性?如何防止消息重复消费&#xff1f;(如何保证消息幂等…...

算法-华为OD机试-识别有效的IP地址和掩码并进行分类统计

1.描述 见牛客网 https://www.nowcoder.com/practice/de538edd6f7e4bc3a5689723a74356822. 分析 根据题目要求&#xff0c;分为以下几步 1. 提取IP地址和子网掩码 我们首先需要拆分输入的每一行&#xff0c;分别提取IP地址和子网掩码&#xff0c;并检查它们的合法性。 2.…...

钉钉开发网页应用JSAPI前端授权鉴权nodejs实现

钉钉开发网页应用JSAPI前端授权鉴权nodejs实现 使用钉钉进行H5网页开发的时候&#xff0c;需要调用一些钉钉提供具有原生能力的api&#xff0c;要调用这些api需要进行jsapi授权。 详见官方文档&#xff08;可选&#xff09;开发网页应用前端 - 钉钉开放平台 (dingtalk.com) 官方…...

uniapp 自定义全局弹窗

自定义全局弹窗可在js和.vue文件中调用&#xff0c;unipop样式不满足&#xff0c;需自定义样式。 效果图 目录结构 index.vue <template><view class"uni-popup" v-if"isShow"><view class"uni-popup__mask uni-center ani uni-cust…...

element+-ui图片无法使用--安装

element-ui图片无法使用 安装npm install element-plus/icons-vue 注册 // main.jsimport * as ElementPlusIconsVue from element-plus/icons-vueconst app createApp(App) for (const [key, component] of Object.entries(ElementPlusIconsVue)) {app.component(key, compo…...

Python编码系列—Python ORM(对象关系映射):高效数据库编程实践

&#x1f31f;&#x1f31f; 欢迎来到我的技术小筑&#xff0c;一个专为技术探索者打造的交流空间。在这里&#xff0c;我们不仅分享代码的智慧&#xff0c;还探讨技术的深度与广度。无论您是资深开发者还是技术新手&#xff0c;这里都有一片属于您的天空。让我们在知识的海洋中…...

一次日志记录中使用fastjson涉及到ByteBuffer的教训

背景 目前本人在公司负责的模块中&#xff0c;有一个模块是负责数据同步的&#xff0c;主要是将我们数据产线使用的 AWS Dynamodb 同步的我们的测试QA 的环境的 MongoDB 的库中&#xff0c;去年开始也提供了使用 EMR 批量同步的功能&#xff0c;但是有时候业务也需要少量的数据…...

掌握TCP连接管理与流量控制:从零开始

文章目录 1. TCP连接管理1.1 三次握手&#xff08;Three-way Handshake&#xff09;1.2 四次挥手&#xff08;Four-way Handshake&#xff09;1.3 TCP连接管理的重要性 2. TCP流量控制2.1 滑动窗口&#xff08;Sliding Window&#xff09;2.2 拥塞控制&#xff08;Congestion C…...

python提取b站视频的音频(提供源码

如果我想开一家咖啡厅&#xff0c;那么咖啡厅的音乐可得精挑细选&#xff01;又假设我非常喜欢o叔&#xff0c;而o叔只在b站弹钢琴&#xff0c;那这时候我就得想方设法把b站的视频转为音频咯&#xff01; 一、首先打开网页版bilibili&#xff0c;按F12&#xff1a; 二、刷新页面…...

嵌入式Linux ,QT5 鼠标键盘设备参数指定环境变量的方法

根文件系统中&#xff0c;一般用mdev来管理设备&#xff0c;不像udev方便&#xff0c;有时候在执行rcS脚本的时候因为&#xff0c;太快&#xff0c;有些设备比如鼠标还没在/dev/input中生成设备文件&#xff0c;最好使用前用mdev -s扫描并等待几秒钟&#xff0c;然后就可以在in…...

C语言钥匙迷宫2.0

目录 开头程序程序的流程图程序游玩的效果结尾 开头 大家好&#xff0c;我叫这是我58。废话不多说&#xff0c;咱们直接开始。 程序 #define _CRT_SECURE_NO_WARNINGS 1 #include <stdio.h> #include <string.h> #include <Windows.h> enum color {Y,B,R …...

【多线程】初步认识Thread类及其应用

&#x1f490;个人主页&#xff1a;初晴~ &#x1f4da;相关专栏&#xff1a;多线程 / javaEE初阶 上篇文章我们简单介绍了什么是进程与线程&#xff0c;以及他们之间的区别与联系&#xff0c;实际应用中还是以多线程编程为主的&#xff0c;所以这篇文章就让我们更加深入地去剖…...

algorithm算法库学习之——划分操作和排序操作

algorithm此头文件是算法库的一部分。本篇介绍划分操作和排序操作。 划分操作 is_partitioned (C11) 判断范围是否已按给定的谓词划分 (函数模板) partition 将范围中的元素分为两组 (函数模板) partition_copy (C11) 复制一个范围&#xff0c;将各元素分为两组 (函数模板) st…...

XSS实验记录

目录 XXS地址 实验过程 Ma Spaghet Jeff Ugandan Knuckles Ricardo Milos Ah Thats Hawt Ligma Mafia Ok, Boomer XXS地址 XSS Game - Learning XSS Made Simple! | Created by PwnFunction 实验过程 Ma Spaghet 要求我们弹出一个alert(1337)sandbox.pwnfuncti…...

Cortex-A7的GIC(全局中断控制器)使用方法(7):基于stm32MP135的GIC配置中断效果测试

0 参考资料 STM32MP13xx参考手册.pdf&#xff08;RM0475&#xff09; ARM Generic Interrupt Controller Architecture version 2.0 - Architecture Specification.pdf 1 GIC配置中断效果测试 前面我们已经实现了GIC的配置&#xff0c;为了验证GIC是否配置有效&#xff0c;本例…...

c++动态数组new和delete

文章目录 动态数组的使用大全1. **基本创建和初始化**2. **动态调整大小**3. **动态数组的使用与标准库 std::vector**4. **动态数组作为函数参数**输出 5. **使用动态数组存储用户输入** 动态数组的使用大全 1. 基本创建和初始化 示例&#xff1a; #include <iostream&g…...

Redis热点知识速览(redis的数据结构、高性能、持久化、主从复制、集群、缓存淘汰策略、事务、Pub/Sub、锁机制、常见问题等)

Redis是一个开源的、使用内存作为存储的、支持数据结构丰富的NoSQL数据库。它的高性能、灵活性和简单易用使其在许多场景下成为首选的缓存解决方案。以下是Redis的常见和热点知识总结。 数据结构 Redis支持五种基本数据结构&#xff1a; String&#xff1a;字符串是Redis中最…...

【C++浅析】lambda表达式:基本结构 使用示例

基本结构 [捕获列表](参数列表) -> 返回类型 { // 函数体 } 捕获列表 ([ ]): 用于指定外部变量的捕获方式。可以&#xff1a; 通过值捕获&#xff1a;[x]通过引用捕获&#xff1a;[&x]捕获所有变量通过值&#xff1a;[]捕获所有变量通过引用&#xff1a;[&]自…...

利用Redis获取权限的多种方式

更多实战内容&#xff0c;可前往无问社区查看http://www.wwlib.cn/index.php/artread/artid/10333.html Redis是我们在实战中经常接触到的一款数据库&#xff0c;因其在前期打点中被利用后可直接影响服务器安全所以在攻防过程中也备受红队关注&#xff0c;在本文中会重点分享一…...

LeetCode - LCR 146- 螺旋遍历二维数组

LCR 146题 题目描述&#xff1a; 给定一个二维数组 array&#xff0c;请返回「螺旋遍历」该数组的结果。 螺旋遍历&#xff1a;从左上角开始&#xff0c;按照 向右、向下、向左、向上 的顺序 依次 提取元素&#xff0c;然后再进入内部一层重复相同的步骤&#xff0c;直到提取完…...

如何获取Bing站长工具API密钥

Bing站长工具近期悄然上线了网站URL推送功能&#xff0c;似乎有意跟随百度的步伐。这个新功能允许站长通过API向Bing提交链接数据&#xff0c;当然也可以通过Bing站长工具手动提交。 本文将详细介绍如何通过Bing站长工具生成用于网站链接推送的API密钥。 首先&#xff0c;访问…...

NC 调整数组顺序使奇数位于偶数前面(一)

系列文章目录 文章目录 系列文章目录前言 前言 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站&#xff0c;这篇文章男女通用&#xff0c;看懂了就去分享给你的码吧。 描述 输入一个长度…...

Unity异步把图片数据从显存下载到内存(GPU->CPU)

Unity异步把图片数据从显存下载到内存&#xff08;GPU->CPU&#xff09; 1.c#核心代码 using System.Collections; using System.Collections.Generic; using Unity.Collections; using UnityEditor.PackageManager.Requests; using UnityEngine; using UnityEngine.Rende…...

【MySQL】C/C++连接MySQL客户端,MySQL函数接口认知,图形化界面进行连接

【MySQL】C/C引入MySQL客户端 安装mysqlclient库mysql接口介绍初始化mysql_init链接数据库mysql_real_connect下发mysql命令mysql_query获取出错信息mysql_error获取执行结果mysql_store_result获取结果行数mysql_num_rows获取结果列数mysql_num_fields判断结果列数mysql_field…...

Wireshark分析工具

简单用例 首先打开软件&#xff0c;左上角点文件&#xff0c;选中要分析的文件列表。 导入用tcpdump抓的包后进行分析&#xff0c;这里要输入过滤条件&#xff0c;对网络包进行一定的过滤处理。&#xff08;这里172网段是阿里云的地址&#xff0c;用自己写的python2脚本对阿里…...