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

设计模式之观察者模式与访问者模式详解和应用

目录

  • 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 类图设计

img

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());}
}

测试结果:

img

说明:

访问者顶层接口定义时,内部会定义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 访问者模式中伪动态双分派

在数据结构中,一般会对集合元素进行遍历处理。如下所示:

img

可以看到,这里是一次动态分派,调用accept方法,具体的类型要到运行时,才能确定。

而且Employee也是一个抽象接口,类型不能确定。

img

当进入到一个子类中,如Engineer,accept方法调用visit方法,传参this,也是动态分派。需要到

运行时,才能确定类型。【因为需要在运行时创建this实例】

img

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 类图设计

img

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");}
}

测试结果:img

2.4 观察者模式应用案例之鼠标事件交互

当鼠标发起某个动作【如:单击,移动,滚动。。。】,得到相应的响应。

针对发出动作,需要进行监听【现实中操作系统进行监听】

鼠标:当成被观察者实现。

事件监听器:监听动作【触发一个事件】

业务类:事件类【传递参数,区分不同事件或动作】

事件回调:监听后,需要作出响应,进行回调。充当观察者。

2.4.1 类图设计

img

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();}
}

测试结果如下:

img

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.…...

[SSD固态硬盘技术 18] Over-Provisioning (OP 预留空间)详解,谁“偷”走了SSD的容量?

</...

spring注解方式整合Dubbo源码解析

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

大数值金额大写转换(C语言)

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

迷宫问题图解 : 基于骨架提取、四邻域

目录 1. 迷宫的连通域 2. How to remove branch &#xff1f; 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…...

设计模式 - 如何在库和主程序之间互相调用数据和函数

背景&#xff1a;在项目开发过程中&#xff0c;难免碰到这种情况&#xff0c;当我们想要通过我们开发的库&#xff0c;调用主程序中的一些变量或者函数的时候&#xff0c;就会导致一些问题&#xff0c;因为在项目构建过程中&#xff0c;库都是不依赖于主程序编译的&#xff0c;…...

Redis面试题:1~2亿条数据需要缓存,请问如何设计这个存储案例

目录 前言 一、哈希取余分区 优点 缺点 二、一致性哈希算法分区 背景 步骤 ① 算法构建一致性哈希环 ② 服务器IP节点映射 ③ key落到服务器的落键规则 优点 ① 容错性 ② 扩展性 缺点 三、哈希槽分区 前言 单机单台100%不可能&#xff0c;肯定是分布式存储&am…...

程序员必备的软技能-《如何阅读一本书》

阅读很重要&#xff0c;我们真的会阅读吗&#xff1f; 这本书的初版是 1940年&#xff0c;时隔 80年&#xff0c;其内容仍然不过时。第一次读这本书时&#xff0c;给我最大的影响就是主题阅读&#xff0c;每次学习一个新理论、技术&#xff0c;都入手多本关于这项理论、技术的书…...

Java数据结构-栈、队列常用类(Stack、ArrayDeque、LinkedLList)

数据结构的三要素包括&#xff1a;逻辑结构、存储结构、数据的运算。逻辑结构描述的是数据之间的逻辑关系&#xff0c;分为线性结构&#xff08;线性表&#xff08;数组、链表&#xff09;、栈、队列&#xff09;和非线性结构&#xff08;图、树、集合&#xff09;。物理结构也…...

拯救了大批爬虫程序员,因为一个简单的神器

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

2023年美赛C题Wordle预测问题三、四建模及Python代码详细讲解

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

相关性-回忆录(持续更新)

1.TODO方向 &#xff08;1&#xff09;数据增强&#xff1a;finetuning阶段需要大量人工标注样本&#xff0c;消耗时间和成本。用户点击数据作为弱监督学习&#xff0c;可以尝试图网络构建节点和边&#xff08;query聚合&#xff09;&#xff1b; 使用展现未点击生成对抗网络进…...

(必备技能)使用Python实现屏幕截图

(必备技能)使用Python实现屏幕截图 文章目录 (必备技能)使用Python实现屏幕截图 一、序言二、环境配置 1、下载pyautogui包2、下载opencv-python包3、下载PyQt5包4、下载pypiwin32包 三、屏幕截屏源码与解析 1、使用pyautogui方法实现截屏2、使用PyQt方法实现截屏 a.获取窗口…...

「数据仓库」怎么选择现代数据仓库?

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

6.3 使用 Swagger 生成 Web API 文档

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

Day894.加锁规则的一些问题 -MySQL实战

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

【Flutter入门到进阶】Dart进阶篇---Dart异步编程

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

点云配准方法原理(NDT、ICP)

配准是点云处理中的一个基础问题&#xff0c;众多学者此问题进行了广泛而深入的研究&#xff0c;也出现了一系列优秀成熟的算法&#xff0c;在三维建模、自动驾驶等领域发挥着重要的作用。 本文主要介绍粗配准NDT (Normal Distribution Transform) 与 精配准ICP (Iterative Cl…...

大规模 IoT 边缘容器集群管理的几种架构-0-边缘容器及架构简介

&#x1f4da;️Reference: IoT 边缘计算系列文章 什么是边缘容器&#xff1f; 边缘容器的概念 边缘容器是分散的计算资源&#xff0c;尽可能靠近最终用户或设备&#xff0c;以减少延迟、节省带宽并增强整体数字体验。 可以访问互联网的设备数量每天都在增加。有包括但不限于…...

代码随想录算法训练营第45天动态规划 背包基础 1 2、 416. 分割等和子集

文章目录01背包基础 &#xff08;二维数组&#xff09;思路递推公式初始化遍历顺序一维dp数组&#xff08;滚动数组&#xff09;一维数组的递推公式遍历顺序LeetCode 416. 分割等和子集思路总结01背包基础 &#xff08;二维数组&#xff09; 思路 根据动态规划五部进行分析&a…...

UE5 学习系列(二)用户操作界面及介绍

这篇博客是 UE5 学习系列博客的第二篇&#xff0c;在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下&#xff1a; 【Note】&#xff1a;如果你已经完成安装等操作&#xff0c;可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作&#xff0c;重…...

第19节 Node.js Express 框架

Express 是一个为Node.js设计的web开发框架&#xff0c;它基于nodejs平台。 Express 简介 Express是一个简洁而灵活的node.js Web应用框架, 提供了一系列强大特性帮助你创建各种Web应用&#xff0c;和丰富的HTTP工具。 使用Express可以快速地搭建一个完整功能的网站。 Expre…...

19c补丁后oracle属主变化,导致不能识别磁盘组

补丁后服务器重启&#xff0c;数据库再次无法启动 ORA01017: invalid username/password; logon denied Oracle 19c 在打上 19.23 或以上补丁版本后&#xff0c;存在与用户组权限相关的问题。具体表现为&#xff0c;Oracle 实例的运行用户&#xff08;oracle&#xff09;和集…...

Neo4j 集群管理:原理、技术与最佳实践深度解析

Neo4j 的集群技术是其企业级高可用性、可扩展性和容错能力的核心。通过深入分析官方文档,本文将系统阐述其集群管理的核心原理、关键技术、实用技巧和行业最佳实践。 Neo4j 的 Causal Clustering 架构提供了一个强大而灵活的基石,用于构建高可用、可扩展且一致的图数据库服务…...

Module Federation 和 Native Federation 的比较

前言 Module Federation 是 Webpack 5 引入的微前端架构方案&#xff0c;允许不同独立构建的应用在运行时动态共享模块。 Native Federation 是 Angular 官方基于 Module Federation 理念实现的专为 Angular 优化的微前端方案。 概念解析 Module Federation (模块联邦) Modul…...

Map相关知识

数据结构 二叉树 二叉树&#xff0c;顾名思义&#xff0c;每个节点最多有两个“叉”&#xff0c;也就是两个子节点&#xff0c;分别是左子 节点和右子节点。不过&#xff0c;二叉树并不要求每个节点都有两个子节点&#xff0c;有的节点只 有左子节点&#xff0c;有的节点只有…...

AI病理诊断七剑下天山,医疗未来触手可及

一、病理诊断困局&#xff1a;刀尖上的医学艺术 1.1 金标准背后的隐痛 病理诊断被誉为"诊断的诊断"&#xff0c;医生需通过显微镜观察组织切片&#xff0c;在细胞迷宫中捕捉癌变信号。某省病理质控报告显示&#xff0c;基层医院误诊率达12%-15%&#xff0c;专家会诊…...

Web中间件--tomcat学习

Web中间件–tomcat Java虚拟机详解 什么是JAVA虚拟机 Java虚拟机是一个抽象的计算机&#xff0c;它可以执行Java字节码。Java虚拟机是Java平台的一部分&#xff0c;Java平台由Java语言、Java API和Java虚拟机组成。Java虚拟机的主要作用是将Java字节码转换为机器代码&#x…...

【Android】Android 开发 ADB 常用指令

查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...

elementUI点击浏览table所选行数据查看文档

项目场景&#xff1a; table按照要求特定的数据变成按钮可以点击 解决方案&#xff1a; <el-table-columnprop"mlname"label"名称"align"center"width"180"><template slot-scope"scope"><el-buttonv-if&qu…...