设计模式(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 观察者模式(Observer) 观察者模式是对象之间存在一对多的依赖关系,当一个对象的状态发生变化时,所有依赖它的对象都会得到通知并自动更新&…...
学懂C++(三十一):高级教程——深入详解C++高级多线程编程技术之锁优化与替代
引言 随着多核处理器的普及,多线程编程技术已经成为提高应用程序性能的关键手段。在多线程环境下,如何高效、安全地管理线程之间的共享资源是开发者面临的主要挑战。传统的锁机制,如互斥锁(Mutex)、临界区(…...

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(轻量级进程id),而是pthread库自己维护的一个唯一值。 我们理解为什么线程id不是内核里面LWP,因为用户没有权限使用内核里面的字段,那是专门给OS管理…...

Unified 阻抗控制 architecture、framework、approach
Unified 阻抗控制(Unified Impedance Control)作为一种控制策略,其architecture(架构)、framework(框架)和approach(方法)为: 一、Unified 阻抗控制 Archite…...
Java后端面试题(mq相关)(day9)
目录 为什么用MQ? 异步 、削峰、解耦1. 异步处理2. 解耦3. 削峰填谷 Exchange类型什么是死信队列?如何保证消息的可靠性?RabbitMQ中如何解决消息堆积问题?RabbitMQ中如何保证消息有序性?如何防止消息重复消费?(如何保证消息幂等…...
算法-华为OD机试-识别有效的IP地址和掩码并进行分类统计
1.描述 见牛客网 https://www.nowcoder.com/practice/de538edd6f7e4bc3a5689723a74356822. 分析 根据题目要求,分为以下几步 1. 提取IP地址和子网掩码 我们首先需要拆分输入的每一行,分别提取IP地址和子网掩码,并检查它们的合法性。 2.…...
钉钉开发网页应用JSAPI前端授权鉴权nodejs实现
钉钉开发网页应用JSAPI前端授权鉴权nodejs实现 使用钉钉进行H5网页开发的时候,需要调用一些钉钉提供具有原生能力的api,要调用这些api需要进行jsapi授权。 详见官方文档(可选)开发网页应用前端 - 钉钉开放平台 (dingtalk.com) 官方…...

uniapp 自定义全局弹窗
自定义全局弹窗可在js和.vue文件中调用,unipop样式不满足,需自定义样式。 效果图 目录结构 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(对象关系映射):高效数据库编程实践
🌟🌟 欢迎来到我的技术小筑,一个专为技术探索者打造的交流空间。在这里,我们不仅分享代码的智慧,还探讨技术的深度与广度。无论您是资深开发者还是技术新手,这里都有一片属于您的天空。让我们在知识的海洋中…...

一次日志记录中使用fastjson涉及到ByteBuffer的教训
背景 目前本人在公司负责的模块中,有一个模块是负责数据同步的,主要是将我们数据产线使用的 AWS Dynamodb 同步的我们的测试QA 的环境的 MongoDB 的库中,去年开始也提供了使用 EMR 批量同步的功能,但是有时候业务也需要少量的数据…...

掌握TCP连接管理与流量控制:从零开始
文章目录 1. TCP连接管理1.1 三次握手(Three-way Handshake)1.2 四次挥手(Four-way Handshake)1.3 TCP连接管理的重要性 2. TCP流量控制2.1 滑动窗口(Sliding Window)2.2 拥塞控制(Congestion C…...

python提取b站视频的音频(提供源码
如果我想开一家咖啡厅,那么咖啡厅的音乐可得精挑细选!又假设我非常喜欢o叔,而o叔只在b站弹钢琴,那这时候我就得想方设法把b站的视频转为音频咯! 一、首先打开网页版bilibili,按F12: 二、刷新页面…...
嵌入式Linux ,QT5 鼠标键盘设备参数指定环境变量的方法
根文件系统中,一般用mdev来管理设备,不像udev方便,有时候在执行rcS脚本的时候因为,太快,有些设备比如鼠标还没在/dev/input中生成设备文件,最好使用前用mdev -s扫描并等待几秒钟,然后就可以在in…...

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

【多线程】初步认识Thread类及其应用
💐个人主页:初晴~ 📚相关专栏:多线程 / javaEE初阶 上篇文章我们简单介绍了什么是进程与线程,以及他们之间的区别与联系,实际应用中还是以多线程编程为主的,所以这篇文章就让我们更加深入地去剖…...

algorithm算法库学习之——划分操作和排序操作
algorithm此头文件是算法库的一部分。本篇介绍划分操作和排序操作。 划分操作 is_partitioned (C11) 判断范围是否已按给定的谓词划分 (函数模板) partition 将范围中的元素分为两组 (函数模板) partition_copy (C11) 复制一个范围,将各元素分为两组 (函数模板) 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(RM0475) ARM Generic Interrupt Controller Architecture version 2.0 - Architecture Specification.pdf 1 GIC配置中断效果测试 前面我们已经实现了GIC的配置,为了验证GIC是否配置有效,本例…...
Linux链表操作全解析
Linux C语言链表深度解析与实战技巧 一、链表基础概念与内核链表优势1.1 为什么使用链表?1.2 Linux 内核链表与用户态链表的区别 二、内核链表结构与宏解析常用宏/函数 三、内核链表的优点四、用户态链表示例五、双向循环链表在内核中的实现优势5.1 插入效率5.2 安全…...
Oracle查询表空间大小
1 查询数据库中所有的表空间以及表空间所占空间的大小 SELECTtablespace_name,sum( bytes ) / 1024 / 1024 FROMdba_data_files GROUP BYtablespace_name; 2 Oracle查询表空间大小及每个表所占空间的大小 SELECTtablespace_name,file_id,file_name,round( bytes / ( 1024 …...

YSYX学习记录(八)
C语言,练习0: 先创建一个文件夹,我用的是物理机: 安装build-essential 练习1: 我注释掉了 #include <stdio.h> 出现下面错误 在你的文本编辑器中打开ex1文件,随机修改或删除一部分,之后…...

关于iview组件中使用 table , 绑定序号分页后序号从1开始的解决方案
问题描述:iview使用table 中type: "index",分页之后 ,索引还是从1开始,试过绑定后台返回数据的id, 这种方法可行,就是后台返回数据的每个页面id都不完全是按照从1开始的升序,因此百度了下,找到了…...

蓝牙 BLE 扫描面试题大全(2):进阶面试题与实战演练
前文覆盖了 BLE 扫描的基础概念与经典问题蓝牙 BLE 扫描面试题大全(1):从基础到实战的深度解析-CSDN博客,但实际面试中,企业更关注候选人对复杂场景的应对能力(如多设备并发扫描、低功耗与高发现率的平衡)和前沿技术的…...
linux 错误码总结
1,错误码的概念与作用 在Linux系统中,错误码是系统调用或库函数在执行失败时返回的特定数值,用于指示具体的错误类型。这些错误码通过全局变量errno来存储和传递,errno由操作系统维护,保存最近一次发生的错误信息。值得注意的是,errno的值在每次系统调用或函数调用失败时…...

MODBUS TCP转CANopen 技术赋能高效协同作业
在现代工业自动化领域,MODBUS TCP和CANopen两种通讯协议因其稳定性和高效性被广泛应用于各种设备和系统中。而随着科技的不断进步,这两种通讯协议也正在被逐步融合,形成了一种新型的通讯方式——开疆智能MODBUS TCP转CANopen网关KJ-TCPC-CANP…...
AI编程--插件对比分析:CodeRider、GitHub Copilot及其他
AI编程插件对比分析:CodeRider、GitHub Copilot及其他 随着人工智能技术的快速发展,AI编程插件已成为提升开发者生产力的重要工具。CodeRider和GitHub Copilot作为市场上的领先者,分别以其独特的特性和生态系统吸引了大量开发者。本文将从功…...

UR 协作机器人「三剑客」:精密轻量担当(UR7e)、全能协作主力(UR12e)、重型任务专家(UR15)
UR协作机器人正以其卓越性能在现代制造业自动化中扮演重要角色。UR7e、UR12e和UR15通过创新技术和精准设计满足了不同行业的多样化需求。其中,UR15以其速度、精度及人工智能准备能力成为自动化领域的重要突破。UR7e和UR12e则在负载规格和市场定位上不断优化…...

网络编程(UDP编程)
思维导图 UDP基础编程(单播) 1.流程图 服务器:短信的接收方 创建套接字 (socket)-----------------------------------------》有手机指定网络信息-----------------------------------------------》有号码绑定套接字 (bind)--------------…...