设计模式之观察者模式与访问者模式详解和应用
目录
- 1.访问者模式详解
- 1.1 访问者模式的定义
- 1.1.1 访问者模式在生活中的体现
- 1.1.2 访问者模式的适用场景
- 1.2 访问者模式的通用实现
- 1.3 访问者模式的使用案例之KPI考核
- 1.3.1 类图设计
- 1.3.2 代码实现
- 1.4 访问者模式扩展---分派
- 1.4.1 java中静态分派示例代码
- 1.4.2 java中动态分派
- 1.4.3 访问者模式中伪动态双分派
- 1.5 访问者模式在源码中应用
- 1.5.1 jdk中FileVisitor
- 1.5.2 spring中BeanDefinitionVisitor
- 1.6 访问者模式的使用总结
- 1.6.1 优缺点总结
- 2 观察者模式详解
- 2.1 观察者模式的定义
- 2.1.1 观察者模式在生活场景中的应用
- 2.1.2 观察者模式适用场景
- 2.2 观察者模式应用案例之问答提示角标
- 2.2.1 类图设计
- 2.2.2 代码实现
- 2.3 google开源组件实现观察者模式
- 2.3.1 示例代码
- 2.4 观察者模式应用案例之鼠标事件交互
- 2.4.1 类图设计
- 2.4.2 代码实现
- 2.5 观察者模式在源码中的应用
- 2.5.1 jdk中ServletContextListener
- 2.5.2 spring中ContextLoaderListener
- 2.6 观察者的使用总结
- 2.6.1 优点总结
- 2.6.2 缺点总结
1.访问者模式详解
1.1 访问者模式的定义
定义:
访问者模式【visitor Pattern】,是一种将数据结构与数据操作分离设计模式。是指
封装一些作用于某种数据结构中的各元素的操作。
特征:
可以在不改变数据结构的前提下定义作用于这些元素的新操作。
属于行为型模式。
说明:
访问者模式,被称为最复杂的设计模式。运用并不多。
1.1.1 访问者模式在生活中的体现
1.参与KPI考核的人员
KPI的考核标准,一般是固定不变的,但参与KPI考核的员工会经常变化。
kpi考核打分的人也会经常变化,
2.餐厅就餐人员
餐厅吃饭,餐厅的菜单是基本稳定的,就餐人员基本每天都在变化。就餐人员就是一个访问者。
总结:
访问者,好像就是变化的元素,与不变的结构【标准,规则】的构成关系处理角色。
1.1.2 访问者模式的适用场景
访问者模式很少能用到,一旦需要使用,涉及到的系统往往比较复杂。
1.数据结构稳定,作用于数据结构的操作经常变化的场景。
2.需要数据结构与数据操作分离的场景。
3.需要对不同数据类型(元素) 进行操作,而不使用分支判断具体类型的场景。
1.2 访问者模式的通用实现
1.3 访问者模式的使用案例之KPI考核
1.3.1 类图设计
1.3.2 代码实现
1.元素顶层接口定义
package com.oldlu.visitor.demo.kpi;import java.util.Random;/*** @ClassName Employee* @Description 员工,元素抽象* @Author oldlu* @Date 2020/6/24 10:38* @Version 1.0*/
public abstract class Employee {private String name;private int kpi;public Employee(String name) {this.name = name;this.kpi = new Random().nextInt(10);}public abstract void accept(IVisitor visitor);public String getName() {return name;}public int getKpi() {return kpi;}
}
2.元素具体实现
package com.oldlu.visitor.demo.kpi;import java.util.Random;/*** @ClassName Engineer* @Description 普通开发人员* @Author oldlu* @Date 2020/6/24 10:43* @Version 1.0*/
public class Engineer extends Employee{public Engineer(String name) {super(name);}@Overridepublic void accept(IVisitor visitor) {visitor.visit(this);}//考核:代码量public int getCodingLine(){return new Random().nextInt(100000);}}
package com.oldlu.visitor.demo.kpi;import java.util.Random;/*** @ClassName Manager* @Description 项目经理* @Author oldlu* @Date 2020/6/24 10:44* @Version 1.0*/
public class Manager extends Employee {public Manager(String name) {super(name);}@Overridepublic void accept(IVisitor visitor) {visitor.visit(this);}//考核:每年的新产品研发数量public int getProducts(){return new Random().nextInt(10);}
}
3.访问者顶层接口及实现
package com.oldlu.visitor.demo.kpi;/*** @ClassName IVisitor* @Description 访问者接口* @Author oldlu* @Date 2020/6/24 10:41* @Version 1.0*/
public interface IVisitor {//传参具体的元素void visit(Engineer engineer);void visit(Manager manager);
}
package com.oldlu.visitor.demo.kpi;/*** @ClassName CTOVisitor* @Description ceo考核者,只有看kpi打分就行* @Author oldlu* @Date 2020/6/24 10:49* @Version 1.0*/
public class CEOVisitor implements IVisitor{@Overridepublic void visit(Engineer engineer) {System.out.println("工程师:"+engineer.getName()+" ,KPI:"+engineer.getKpi());}@Overridepublic void visit(Manager manager) {System.out.println("项目经理:"+manager.getName()+" ,KPI:"+manager.getKpi());}
}
package com.oldlu.visitor.demo.kpi;/*** @ClassName CTOVisitor* @Description cto考核者* @Author oldlu* @Date 2020/6/24 10:49* @Version 1.0*/
public class CTOVisitor implements IVisitor{@Overridepublic void visit(Engineer engineer) {System.out.println("工程师:"+engineer.getName()+" ,编写代码行数:"+engineer.getCodingLine());}@Overridepublic void visit(Manager manager) {System.out.println("项目经理:"+manager.getName()+" ,产品数量:"+manager.getProducts());}
}
4.数据结构定义
package com.oldlu.visitor.demo.kpi;import java.util.LinkedList;
import java.util.List;/*** @ClassName BusinessReport* @Description 业务报表,数据结构* @Author oldlu* @Date 2020/6/24 10:55* @Version 1.0*/
public class BusinessReport {private List<Employee> employeeList = new LinkedList<>();public BusinessReport() {employeeList.add(new Manager("项目经理A"));employeeList.add(new Manager("项目经理B"));employeeList.add(new Engineer("程序员A"));employeeList.add(new Engineer("程序员B"));employeeList.add(new Engineer("程序员C"));}public void showReport(IVisitor visitor){for (Employee employee : employeeList) {employee.accept(visitor);}}
}
5.测试代码
package com.oldlu.visitor.demo.kpi;/*** @ClassName Test* @Description 测试类* @Author oldlu* @Date 2020/6/24 10:54* @Version 1.0*/
public class Test {public static void main(String[] args) {BusinessReport report = new BusinessReport();System.out.println("===========CEO看报表===============");report.showReport(new CEOVisitor());System.out.println("===========CTO看报表===============");report.showReport(new CTOVisitor());}
}
测试结果:
说明:
访问者顶层接口定义时,内部会定义visit重载方法,针对不同的访问元素实现子类进行重载。
为什么不设计成一个方法呢?
因为这里一个方法,把具体元素关联起来。
当系统需要增加元素实现子类时,只需要增加一个实现子类,该接口中增加一个重载方法。
系统方便扩展。
1.4 访问者模式扩展—分派
java中静态分派,和动态分派。还有双分派。
Java中分派,是方法重载的一种特殊形式。即重载方法,方法名相同,参数个数相同,类型不同的形式。
1.4.1 java中静态分派示例代码
package com.oldlu.visitor.dispatch;/*** @ClassName Main* @Description 测试静态分派* @Author oldlu* @Date 2020/6/24 11:20* @Version 1.0*/
public class Main {public static void main(String[] args) {String str = "1";Integer integer = 1;Main main = new Main();main.test(integer);main.test(str);}public void test(String str){System.out.println("String "+str);}public void test(Integer integer){System.out.println("Integer "+integer);}
}
说明:
上面测试代码中,test方法存在两个重载方法,参数个数相同,类型不同,
在编译阶段,就能清楚地知道参数类型,称为静态分派。
相同方法名,不同类型的不同方法,这种形式也称为多分派。
1.4.2 java中动态分派
在程序编译阶段,不能明确是哪种类型,只有在运行时,才能知道是哪个类型,
称为动态分派。
1.定义接口及实现
package com.oldlu.visitor.dispatch.dynamic;/*** @ClassName Person* @Description 接口定义* @Author oldlu* @Date 2020/6/24 11:33* @Version 1.0*/
public interface Person {void test();
}
package com.oldlu.visitor.dispatch.dynamic;/*** @ClassName Women* @Description 女人* @Author oldlu* @Date 2020/6/24 11:35* @Version 1.0*/
public class Women implements Person{@Overridepublic void test() {System.out.println("女人");}
}
package com.oldlu.visitor.dispatch.dynamic;/*** @ClassName Man* @Description 男人* @Author oldlu* @Date 2020/6/24 11:34* @Version 1.0*/
public class Man implements Person {@Overridepublic void test() {System.out.println("男人");}
}
2.测试类
package com.oldlu.visitor.dispatch.dynamic;/*** @ClassName Main* @Description 测试类* @Author oldlu* @Date 2020/6/24 11:35* @Version 1.0*/
public class Main {public static void main(String[] args) {Person man = new Man();Person women = new Women();man.test();women.test();}
}
说明:
当在编译期时,man或women并不知道自己是什么类型,只有在运行期,
通过new创建实例时,才能知道具体的类型,所以,是动态分派。
1.4.3 访问者模式中伪动态双分派
在数据结构中,一般会对集合元素进行遍历处理。如下所示:
可以看到,这里是一次动态分派,调用accept方法,具体的类型要到运行时,才能确定。
而且Employee也是一个抽象接口,类型不能确定。
当进入到一个子类中,如Engineer,accept方法调用visit方法,传参this,也是动态分派。需要到
运行时,才能确定类型。【因为需要在运行时创建this实例】
1.5 访问者模式在源码中应用
1.5.1 jdk中FileVisitor
FileVisitResult visitFile(T file, BasicFileAttributes attrs)throws IOException;
FileVisitor接口中定义visitFile方法。传参BasicFileAtributes也是一个接口。
1.5.2 spring中BeanDefinitionVisitor
public void visitBeanDefinition(BeanDefinition beanDefinition) {visitParentName(beanDefinition);visitBeanClassName(beanDefinition);visitFactoryBeanName(beanDefinition);visitFactoryMethodName(beanDefinition);visitScope(beanDefinition);if (beanDefinition.hasPropertyValues()) {visitPropertyValues(beanDefinition.getPropertyValues());}if (beanDefinition.hasConstructorArgumentValues()) {ConstructorArgumentValues cas = beanDefinition.getConstructorArgumentValues();visitIndexedArgumentValues(cas.getIndexedArgumentValues());visitGenericArgumentValues(cas.getGenericArgumentValues());}}
访问时,并未改变其中的内容,只是返回相应结果。把数据操作与结构进行分离。
1.6 访问者模式的使用总结
1.6.1 优缺点总结
优点:
1.解耦数据结构与数据操作,使用操作集合可以独立变化
2.扩展性好:可以通过扩展访问者角色,实现对数据集的不同操作
3.元素具体类型并非单一,访问者均可操作
4.各角色职责分离,符合单一职责原则。
缺点:
1.无法增加元素类型:若系统数据结构对象易于变化,经常有新的数据对象增加进来,
则访问者类必须增加对应元素的操作,违背开闭原则。
2.具体元素变更困难:具体元素的增加属性,删除属性等操作会导致对应访问者类需要
相应的修改,尤其有大量访问者类时,修改范围太大。
3.违背依赖倒置原则:为了达到”区别对待“,访问者依赖的是具体元素类型,而不是抽象。
2 观察者模式详解
2.1 观察者模式的定义
定义:
观察者模式【Observer Pattern】,又叫发布-订阅【Publish/Subcribe】模式,模型-视图【Model/View】模式、
源监听器【Source/Listener】模式,从属者【Dependents】模式。
定义一种一对多的关系,一个主题对象可被多个观察者对象同时监听,使得每当主题对象状态变化时,所有
依赖它的对象都会得到通知并被自动更新。
属于行为型模式。
2.1.1 观察者模式在生活场景中的应用
1.App角标通知
2.起床闹钟设置
2.1.2 观察者模式适用场景
1.当一个抽象模型包含两个方面内容,其中一个方面依赖于另一个方面。
2.其他一个或多个对象的变化依赖于另一个对象的变化。
3.实现类似广播机制的功能,无需知道具体收听者,只需分发广播,系统中感兴趣
的对象会自动接收该广播。
4.多层嵌套使用,形成一种链式触发机制,使得事件具备跨域(跨越两种观察者类型)通知。
2.2 观察者模式应用案例之问答提示角标
在学习社区,我们有疑问,可以发布问题求助。发布问题时,可以邀请某个人【或某个老师】进行
解答。
但是,老师平时会很忙,不会总是去刷新页面。于是,就会做一个通知功能。
一旦有一个问题,向老师提出,通知图标上就会数字加1.
当老师登录到页面时,只要查看通知角标,就能知道是否有人向他提问,就方便回答。
2.2.1 类图设计
2.2.2 代码实现
说明:这里是基于jdk的发布-订阅api实现。
1.被观察者定义
package com.oldlu.observer.demo.gper;import java.util.Observable;/*** @ClassName GPer* @Description 社区生态圈,被观察者* @Author oldlu* @Date 2020/6/24 17:31* @Version 1.0*/
public class GPer extends Observable {private String name = "GPer 生态圈";public String getName() {return name;}private static final GPer gper = new GPer();private GPer() {}public static GPer getInstance(){return gper;}public void publishQuestion(Question question){System.out.println(question.getUserName()+" 在" +this.name +"提交了一个问题");//调用jdk apisetChanged();notifyObservers(question);}
}
2.数据结构,问题类
package com.oldlu.observer.demo.gper;/*** @ClassName Question* @Description 问题* @Author oldlu* @Date 2020/6/24 17:34* @Version 1.0*/
public class Question {//问题发布者private String userName;//内容private String content;public void setUserName(String userName) {this.userName = userName;}public void setContent(String content) {this.content = content;}public String getUserName() {return userName;}public String getContent() {return content;}
}
3.观察者定义
package com.oldlu.observer.demo.gper;import java.util.Observable;
import java.util.Observer;/*** @ClassName Teacher* @Description 观察者* @Author oldlu* @Date 2020/6/24 17:38* @Version 1.0*/
public class Teacher implements Observer {private String name;public Teacher(String name) {this.name = name;}@Overridepublic void update(Observable ob, Object arg) {GPer gper = (GPer) ob;Question question = (Question) arg;System.out.println("===================");System.out.println(name+"老师,你好\n" +",您收到一个来自"+gper.getName()+"的提问,希望你解答,问题内容如下:\n"+question.getContent()+"\n提问者:"+question.getUserName());}
}
4.测试类
package com.oldlu.observer.demo.gper;import javax.management.Query;/*** @ClassName Test* @Description 测试类* @Author oldlu* @Date 2020/6/24 17:44* @Version 1.0*/
public class Test {public static void main(String[] args) {GPer gper = GPer.getInstance();Teacher tom = new Teacher("tom");Teacher jerry = new Teacher("Jerry");gper.addObserver(tom);gper.addObserver(jerry);//用户行为Question question = new Question();question.setUserName("张三");question.setContent("观察者模式适用于哪些场景?");gper.publishQuestion(question);}
}
2.3 google开源组件实现观察者模式
依赖包:
<dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>18.0</version></dependency>
2.3.1 示例代码
1.观察者类
package com.oldlu.observer.guava;import com.google.common.eventbus.Subscribe;/*** @ClassName GuavaEvent* @Description 观察者* @Author oldlu* @Date 2020/6/24 18:07* @Version 1.0*/
public class GuavaEvent {//表示观察者回调@Subscribepublic void observer(String str){System.out.println("执行observer方法,传参为:"+str);}
}
2.测试类
package com.oldlu.observer.guava;import com.google.common.eventbus.EventBus;/*** @ClassName Test* @Description 测试类* @Author oldlu* @Date 2020/6/24 18:09* @Version 1.0*/
public class Test {public static void main(String[] args) {EventBus eventBus = new EventBus();GuavaEvent event = new GuavaEvent();eventBus.register(event);eventBus.post("tom");}
}
测试结果:
2.4 观察者模式应用案例之鼠标事件交互
当鼠标发起某个动作【如:单击,移动,滚动。。。】,得到相应的响应。
针对发出动作,需要进行监听【现实中操作系统进行监听】
鼠标:当成被观察者实现。
事件监听器:监听动作【触发一个事件】
业务类:事件类【传递参数,区分不同事件或动作】
事件回调:监听后,需要作出响应,进行回调。充当观察者。
2.4.1 类图设计
2.4.2 代码实现
1.事件监听器
package com.oldlu.observer.mouseclick.core;import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;/*** @ClassName EventListener* @Description 事件监听器,被观察者的抽象* @Author oldlu* @Date 2020/6/24 18:21* @Version 1.0*/
public class EventListener {protected Map<String, Event> events = new HashMap<String,Event>();public void addListener(String eventType, Object target, Method callback){events.put(eventType,new Event(target,callback));}public void addListener(String eventType, Object target){try{this.addListener(eventType,target,target.getClass().getMethod("on"+ toUpperFirstCase(eventType),Event.class));}catch (Exception e){e.printStackTrace();}}private String toUpperFirstCase(String eventType) {char [] chars = eventType.toCharArray();if(chars[0]> 'a' && chars[0] < 'z'){chars[0] -= 32;}return String.valueOf(chars);}private void trigger(Event event){event.setSource(this);event.setTime(System.currentTimeMillis());try{if(null != event.getCallback()){//反射调用 回调函数event.getCallback().invoke(event.getTarget(),event);}}catch (Exception e){e.printStackTrace();}}protected void trigger(String trigger){if(!this.events.containsKey(trigger)){return;}//如果已进行注册,回调trigger(this.events.get(trigger).setTrigger(trigger));}
}
2.被监听对象,鼠标类
package com.oldlu.observer.mouseclick.event;import com.oldlu.observer.mouseclick.core.EventListener;/*** @ClassName Mouse* @Description 具体的被观察者* @Author oldlu* @Date 2020/6/24 18:21* @Version 1.0*/
public class Mouse extends EventListener {public void click() {System.out.println("调用单机方法");this.trigger(MouseEventType.ON_CLICK);}
}
3.事件回调
package com.oldlu.observer.mouseclick.event;import com.oldlu.observer.mouseclick.core.Event;/*** @ClassName MouseCallback* @Description 事件响应,回调,观察者* @Author oldlu* @Date 2020/6/24 18:22* @Version 1.0*/
public class MouseEventCallback {public void onClick(Event event){System.out.println("=============触发鼠标单击事件========\n"+event);}public void onMove(Event event){System.out.println("触发鼠标双击事件");}}
4.传参bean,事件类
package com.oldlu.observer.mouseclick.core;import java.lang.reflect.Method;/*** @ClassName Event* @Description 事件抽象,传参对象* @Author oldlu* @Date 2020/6/24 18:22* @Version 1.0*/
public class Event {//事件源,如:鼠标,键盘private Object source;//事件触发,要通知谁(观察者)private Object target;private Method callback;//事件名称private String trigger;//事件触发时间private Long time;public Event(Object target, Method callback) {this.target = target;this.callback = callback;}public Object getSource() {return source;}public Event setSource(Object source) {this.source = source;return this;}public Object getTarget() {return target;}public Event setTarget(Object target) {this.target = target;return this;}public Method getCallback() {return callback;}public Event setCallback(Method callback) {this.callback = callback;return this;}public String getTrigger() {return trigger;}public Event setTrigger(String trigger) {this.trigger = trigger;return this;}public Long getTime() {return time;}public Event setTime(Long time) {this.time = time;return this;}@Overridepublic String toString() {return "Event{" +"source=" + source +", target=" + target +", callback=" + callback +", trigger='" + trigger + '\'' +", time=" + time +'}';}
}
5.事件类型常量定义
package com.oldlu.observer.mouseclick.event;/*** @ClassName MouseEventType* @Description 鼠标事件* @Author oldlu* @Date 2023/2/19 10:47* @Version 1.0*/
public interface MouseEventType {String ON_CLICK = "click";
}
6.测试类
package com.oldlu.observer.mouseclick;import com.oldlu.observer.mouseclick.event.Mouse;
import com.oldlu.observer.mouseclick.event.MouseEventCallback;
import com.oldlu.observer.mouseclick.event.MouseEventType;/*** @ClassName Test* @Description 测试类* @Author oldlu* @Date 2023/2/19 11:15* @Version 1.0*/
public class Test {public static void main(String[] args) {MouseEventCallback callback = new MouseEventCallback();Mouse mouse = new Mouse();mouse.addListener(MouseEventType.ON_CLICK,callback);mouse.click();}
}
测试结果如下:
2.5 观察者模式在源码中的应用
2.5.1 jdk中ServletContextListener
实际上,观察者模式的典型提示,*Listener【监听器】
public interface ServletContextListener extends EventListener {public void contextInitialized ( ServletContextEvent sce );public void contextDestroyed ( ServletContextEvent sce );
}
2.5.2 spring中ContextLoaderListener
public class ContextLoaderListener extends ContextLoader implements ServletContextListener {public ContextLoaderListener() {}public ContextLoaderListener(WebApplicationContext context) {super(context);}public void contextInitialized(ServletContextEvent event) {this.initWebApplicationContext(event.getServletContext());}public void contextDestroyed(ServletContextEvent event) {this.closeWebApplicationContext(event.getServletContext());ContextCleanupListener.cleanupAttributes(event.getServletContext());}
}
2.6 观察者的使用总结
2.6.1 优点总结
1.观察者与被观察者是松耦合的,符合依赖倒置原则。
2.分离表示层【观察者】和数据逻辑层【被观察者】,并且建立了一套触发机制,使得数据的变化可以
响应到多个表示层上。
3.实现一对多的通讯机制,支持事件注册机制,支持兴趣分发机制,当被观察者触发事件时,只有感兴趣
的观察者可以接收到通知。
2.6.2 缺点总结
1.如果观察者数量过多,,则事件通知会耗时较长。
2.事件通知呈线性关系,如果其中一个观察者处理事件卡壳,会影响后续的观察者接收该事件。
3.如果观察者和被观察者之间存在循环依赖,则可能造成两者之间循环调用,导致系统崩溃。
相关文章:

设计模式之观察者模式与访问者模式详解和应用
目录1.访问者模式详解1.1 访问者模式的定义1.1.1 访问者模式在生活中的体现1.1.2 访问者模式的适用场景1.2 访问者模式的通用实现1.3 访问者模式的使用案例之KPI考核1.3.1 类图设计1.3.2 代码实现1.4 访问者模式扩展---分派1.4.1 java中静态分派示例代码1.4.2 java中动态分派1.…...

spring注解方式整合Dubbo源码解析
系列文章目录 前言 本节我们的Dubbo源码版本基于2.6.x 在前一章我们的整合案例中,我们有几个比较关键的步骤: 在启动类上标注了EnableDubbo注解在provider类上面标注了Service注解来提供dubbo服务在消费的时候通过Reference注解引入dubbo服务在配置文件…...

大数值金额大写转换(C语言)
关于大数值金额大写转换,在财务管理的应用方面没什么意义。一般来说,千亿级,万亿级的数值就够了。因为在国家级层面是以亿为单位的,也就表达为千万亿,万万亿。在企业层面数值金额转换设置到千亿、万亿就行了。大的集团…...

迷宫问题图解 : 基于骨架提取、四邻域
目录 1. 迷宫的连通域 2. How to remove branch ? 3. 基于4邻域的 remove 分支 3.1 找到分支的端点 3.2 4邻域的 remove 分支 3.3 循环移除分支 3.4 code 4. 迷宫路线 4.1 预处理 4.2 提取骨架 4.3 分支的端点 4.4 去除分支的端点 4.5 循环去除分支 4…...
设计模式 - 如何在库和主程序之间互相调用数据和函数
背景:在项目开发过程中,难免碰到这种情况,当我们想要通过我们开发的库,调用主程序中的一些变量或者函数的时候,就会导致一些问题,因为在项目构建过程中,库都是不依赖于主程序编译的,…...

Redis面试题:1~2亿条数据需要缓存,请问如何设计这个存储案例
目录 前言 一、哈希取余分区 优点 缺点 二、一致性哈希算法分区 背景 步骤 ① 算法构建一致性哈希环 ② 服务器IP节点映射 ③ key落到服务器的落键规则 优点 ① 容错性 ② 扩展性 缺点 三、哈希槽分区 前言 单机单台100%不可能,肯定是分布式存储&am…...
程序员必备的软技能-《如何阅读一本书》
阅读很重要,我们真的会阅读吗? 这本书的初版是 1940年,时隔 80年,其内容仍然不过时。第一次读这本书时,给我最大的影响就是主题阅读,每次学习一个新理论、技术,都入手多本关于这项理论、技术的书…...

Java数据结构-栈、队列常用类(Stack、ArrayDeque、LinkedLList)
数据结构的三要素包括:逻辑结构、存储结构、数据的运算。逻辑结构描述的是数据之间的逻辑关系,分为线性结构(线性表(数组、链表)、栈、队列)和非线性结构(图、树、集合)。物理结构也…...

拯救了大批爬虫程序员,因为一个简单的神器
相信大家应该都写过爬虫,简单的爬虫只需要使用 requests 即可。遇到复杂的爬虫,就需要在程序里面加上请求头和参数信息。类似这种:我们一般的步骤是,先到浏览器的网络请求中找到我们需要的请求,然后将请求头和参数信息…...

2023年美赛C题Wordle预测问题三、四建模及Python代码详细讲解
更新时间:2023-2-19 16:30 相关链接 (1)2023年美赛C题Wordle预测问题一建模及Python代码详细讲解 (2)2023年美赛C题Wordle预测问题二建模及Python代码详细讲解 (3)2023年美赛C题Wordle预测问题三、四建模…...

相关性-回忆录(持续更新)
1.TODO方向 (1)数据增强:finetuning阶段需要大量人工标注样本,消耗时间和成本。用户点击数据作为弱监督学习,可以尝试图网络构建节点和边(query聚合); 使用展现未点击生成对抗网络进…...
(必备技能)使用Python实现屏幕截图
(必备技能)使用Python实现屏幕截图 文章目录 (必备技能)使用Python实现屏幕截图 一、序言二、环境配置 1、下载pyautogui包2、下载opencv-python包3、下载PyQt5包4、下载pypiwin32包 三、屏幕截屏源码与解析 1、使用pyautogui方法实现截屏2、使用PyQt方法实现截屏 a.获取窗口…...

「数据仓库」怎么选择现代数据仓库?
构建自己的数据仓库时要考虑的基本因素我们用过很多数据仓库。当我们的客户问我们,对于他们成长中的公司来说,最好的数据仓库是什么时,我们会根据他们的具体需求来考虑答案。通常,他们需要几乎实时的数据,价格低廉&…...

6.3 使用 Swagger 生成 Web API 文档
第6章 构建 RESTful 服务 6.1 RESTful 简介 6.2 构建 RESTful 应用接口 6.3 使用 Swagger 生成 Web API 文档 6.4 实战:实现 Web API 版本控制 6.3 使用 Swagger 生成 Web API 文档 高质量的 API 文档在系统开发的过程中非常重要。本节介绍什么是 Swaggerÿ…...

Day894.加锁规则的一些问题 -MySQL实战
加锁规则的一些问题 Hi,我是阿昌,今天学习记录的是关于加锁规则的一些问题的内容。 加锁规则,这个规则中,包含了两个“原则”、两个“优化”和一个“bug”: 原则 1:加锁的基本单位是 next-key lock。nex…...

【Flutter入门到进阶】Dart进阶篇---Dart异步编程
1 并行与并发的编程区别 1.1 并发与并行 1.1.1 说明 我们举个例子,如果有条高速公路 A 上面并排有 8 条车道,那么最大的并行车辆就是 8 辆此条高速公路 A 同时并排行走的车辆小于等于 8 辆的时候,车辆就可以并行运行。 CPU 也是这个原理,一个 CPU 相当于一个高速公路 A,核心数…...

点云配准方法原理(NDT、ICP)
配准是点云处理中的一个基础问题,众多学者此问题进行了广泛而深入的研究,也出现了一系列优秀成熟的算法,在三维建模、自动驾驶等领域发挥着重要的作用。 本文主要介绍粗配准NDT (Normal Distribution Transform) 与 精配准ICP (Iterative Cl…...
大规模 IoT 边缘容器集群管理的几种架构-0-边缘容器及架构简介
📚️Reference: IoT 边缘计算系列文章 什么是边缘容器? 边缘容器的概念 边缘容器是分散的计算资源,尽可能靠近最终用户或设备,以减少延迟、节省带宽并增强整体数字体验。 可以访问互联网的设备数量每天都在增加。有包括但不限于…...

代码随想录算法训练营第45天动态规划 背包基础 1 2、 416. 分割等和子集
文章目录01背包基础 (二维数组)思路递推公式初始化遍历顺序一维dp数组(滚动数组)一维数组的递推公式遍历顺序LeetCode 416. 分割等和子集思路总结01背包基础 (二维数组) 思路 根据动态规划五部进行分析&a…...

Prompt Tuning、P-Tuning、Prefix Tuning的区别
一、Prompt Tuning、P-Tuning、Prefix Tuning的区别 1. Prompt Tuning(提示调优) 核心思想:固定预训练模型参数,仅学习额外的连续提示向量(通常是嵌入层的一部分)。实现方式:在输入文本前添加可训练的连续向量(软提示),模型只更新这些提示参数。优势:参数量少(仅提…...
Frozen-Flask :将 Flask 应用“冻结”为静态文件
Frozen-Flask 是一个用于将 Flask 应用“冻结”为静态文件的 Python 扩展。它的核心用途是:将一个 Flask Web 应用生成成纯静态 HTML 文件,从而可以部署到静态网站托管服务上,如 GitHub Pages、Netlify 或任何支持静态文件的网站服务器。 &am…...
linux 错误码总结
1,错误码的概念与作用 在Linux系统中,错误码是系统调用或库函数在执行失败时返回的特定数值,用于指示具体的错误类型。这些错误码通过全局变量errno来存储和传递,errno由操作系统维护,保存最近一次发生的错误信息。值得注意的是,errno的值在每次系统调用或函数调用失败时…...
2023赣州旅游投资集团
单选题 1.“不登高山,不知天之高也;不临深溪,不知地之厚也。”这句话说明_____。 A、人的意识具有创造性 B、人的认识是独立于实践之外的 C、实践在认识过程中具有决定作用 D、人的一切知识都是从直接经验中获得的 参考答案: C 本题解…...
Python Einops库:深度学习中的张量操作革命
Einops(爱因斯坦操作库)就像给张量操作戴上了一副"语义眼镜"——让你用人类能理解的方式告诉计算机如何操作多维数组。这个基于爱因斯坦求和约定的库,用类似自然语言的表达式替代了晦涩的API调用,彻底改变了深度学习工程…...
NPOI操作EXCEL文件 ——CAD C# 二次开发
缺点:dll.版本容易加载错误。CAD加载插件时,没有加载所有类库。插件运行过程中用到某个类库,会从CAD的安装目录找,找不到就报错了。 【方案2】让CAD在加载过程中把类库加载到内存 【方案3】是发现缺少了哪个库,就用插件程序加载进…...
前端高频面试题2:浏览器/计算机网络
本专栏相关链接 前端高频面试题1:HTML/CSS 前端高频面试题2:浏览器/计算机网络 前端高频面试题3:JavaScript 1.什么是强缓存、协商缓存? 强缓存: 当浏览器请求资源时,首先检查本地缓存是否命中。如果命…...
41道Django高频题整理(附答案背诵版)
解释一下 Django 和 Tornado 的关系? Django和Tornado都是Python的web框架,但它们的设计哲学和应用场景有所不同。 Django是一个高级的Python Web框架,鼓励快速开发和干净、实用的设计。它遵循MVC设计,并强调代码复用。Django有…...
虚幻基础:角色旋转
能帮到你的话,就给个赞吧 😘 文章目录 移动组件使用控制器所需旋转:组件 使用 控制器旋转将旋转朝向运动:组件 使用 移动方向旋转 控制器旋转和移动旋转 缺点移动旋转:必须移动才能旋转,不移动不旋转控制器…...
算法刷题-回溯
今天给大家分享的还是一道关于dfs回溯的问题,对于这类问题大家还是要多刷和总结,总体难度还是偏大。 对于回溯问题有几个关键点: 1.首先对于这类回溯可以节点可以随机选择的问题,要做mian函数中循环调用dfs(i&#x…...