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

news2024/11/15 13:48:29

目录

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

    @Override
    public 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);
    }

    @Override
    public 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{

    @Override
    public void visit(Engineer engineer) {
        System.out.println("工程师:"+engineer.getName()+" ,KPI:"+engineer.getKpi());
    }

    @Override
    public 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{

    @Override
    public void visit(Engineer engineer) {
        System.out.println("工程师:"+engineer.getName()+" ,编写代码行数:"+engineer.getCodingLine());
    }

    @Override
    public 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{
    @Override
    public 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 {
    @Override
    public 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 api
        setChanged();
        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;
    }

    @Override
    public 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 {
    //表示观察者回调
    @Subscribe
    public 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;
    }


    @Override
    public 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.如果观察者和被观察者之间存在循环依赖,则可能造成两者之间循环调用,导致系统崩溃。

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

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

相关文章

C语言 基于Ncurse库的贪吃蛇游戏项目

为了敲键盘及时响应&#xff0c;需要用到ncurse 测试代码&#xff1a; ncurse1.c /* ncurse1.c */ #include <curses.h> //ncurse的头文件。int main() {char c;int i 0;//ncurse界面的初始化函数。initscr(); for(i0;i<2;i){c getch();printw("\n");//…

一起学 pixijs(2):修改图形属性

大家好&#xff0c;我是前端西瓜哥。 我们做动画、游戏、编辑器&#xff0c;需要根据用户的交互等操作&#xff0c;去实时地改变图形的属性&#xff0c;比如位置&#xff0c;颜色等信息。今天西瓜哥带大家来看看在 pixijs 怎么修改图形的属性。 因为 pixijs 的底层维护了图形…

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预测问题三、四建模…

Android 基础知识4-2.8 TableLayout(表格布局)详解

一、TableLayout的概述 表格布局是以行数和列数来确定位置进行排列。就像一间教室&#xff0c;确定好行数与列数就能让同学有序入座。 注意&#xff1a;我们需要先添加<TableRow容器&#xff0c;每添加一个就会多一行&#xff0c;然后再往<TableRow容器中添加其它组件。…

研报精选230219

目录 【行业230219山西证券】煤炭行业周报&#xff1a;复工改善&#xff0c;港口价格企稳反弹【行业230219中航证券】农林牧渔行业周观点&#xff1a;一号文件落地&#xff0c;生物育种超势不改【行业230219华西证券】汽车行业周报&#xff1a;新车密集上市 自主转型提速【个股…

[vue3] pinia的基本使用

使用Pinia npm install piniastore文件里index.js import { createPinia } from piniaconst pinia createPinia()export default piniamain.js导入并引用 import { createApp } from vue import App from ./App.vue import pinia from ./storescreateApp(App).use(pinia).m…

「技术选型」深度学习软件如何选择?

深度学习(DL, Deep Learning)是机器学习(ML, Machine Learning)领域中一个新的研究方向&#xff0c;它被引入机器学习使其更接近于最初的目标——人工智能(AI, Artificial Intelligence)。 深度学习是学习样本数据的内在规律和表示层次&#xff0c;这些学习过程中获得的信息对…

【Flutter入门到进阶】Dart进阶篇---DartVM单线程设计原理

1 虚拟机的指令执行设计 1.1 虚拟机的分类 基于栈的虚拟机&#xff0c;比如JVM虚拟机 基于寄存器的虚拟机&#xff0c;比如Dalvik虚拟机 1.2 虚拟机的概念 首先问一个基本的问题&#xff0c;作为一个虚拟机&#xff0c;它最基本的要实现哪些功能&#xff1f; 他应该能够模拟…

使用uni-app框架中uni.chooseAddress()接口,获取不到用户收货地址

错误描述 在我们使用uni-app框架或微信原生开发微信小程序时&#xff0c;使用到uni.chooseAddress(OBJECT)接口获取用户收货地址时&#xff0c;无法跳转到收货地址页面获取。 打印接口返回信息&#xff0c;显示 "chooseAddress:fail the api need to be declared in the …

LeetCode-17. 电话号码的字母组合

题目来源 17. 电话号码的字母组合 题目思路 从示例上来说&#xff0c;输入"23"&#xff0c;最直接的想法就是两层for循环遍历了吧&#xff0c;正好把组合的情况都输出了。 如果输入"233"呢&#xff0c;那么就三层for循环&#xff0c;如果"2333"…

接口测试(Fiddler工具)

目录 1.Fiddler是什么&#xff1f; 2.Fiddler的原理 3.Fiddler安装 4.Fiddler界面 4.1.常用工具 4.2 会话列表 4.3 状态栏 4.4 内容显示区 1.Fiddler是什么&#xff1f; Fiddler是客户端与服务器之间的HTTP代理&#xff0c;是当前最常用的HTTP协议抓包工具。 主要功能&a…

NSDT可编程3D场景【兼容Three.js】

NSDT编辑器简化了WebGL 3D应用的开发&#xff0c;完全兼容Three.JS生态。本文介绍如何在自己的应用中嵌入使用NSDT编辑器搭建的3D场景&#xff0c;并通过JS API与场景进行交互。 在自己的应用中嵌入3D场景只需要三个步骤&#xff1a; 在NSDT编辑器中搭建3D场景在自己的前端应…

Nonebot2官网插件nonebot-plugin-chatgpt让自己的QQ聊天机器人不再呆头呆脑

前言 如果你会使用Nonebot2搭建QQ聊天机器人&#xff0c;那么你一定会使用Nonebot官网上插件商店发布的插件&#xff0c;今天这篇博客记录一下使用插件时遇到的错误&#xff0c;最终如何解决的错误。在开始之前先看一下效果图吧&#xff01; 瞬间我们的QQ机器人就高大上了起…

Java serialVersionUID 作用和自动生成设置

一、由来 最近在做一个军工的项目&#xff0c;代码提交后&#xff0c;军方用代码安全扫描工具&#xff0c;对代码进行全局扫描&#xff0c;提示一个漏洞&#xff0c;导致原因是实体类实现了Serializable接口&#xff0c;未对serialVersionUID手动赋值&#xff0c;java机制里&am…

Zero-shot(零次学习)简介

zero-shot基本概念 首先通过一个例子来引入zero-shot的概念。假设我们已知驴子和马的形态特征&#xff0c;又已知老虎和鬣狗都是又相间条纹的动物&#xff0c;熊猫和企鹅是黑白相间的动物&#xff0c;再次的基础上&#xff0c;我们定义斑马是黑白条纹相间的马科动物。不看任何斑…

枚举类的使用方法

一、理解枚举类型 枚举类型是Java 5中新增特性的一部分&#xff0c;它是一种特殊的数据类型&#xff0c;之所以特殊是因为它既是一种类(class)类型却又比类类型多了些特殊的约束&#xff0c;但是这些约束的存在也造就了枚举类型的简洁性、安全性以及便捷性。下面先来看看如何写…

如何用一句话感动测试工程师?产品和技术都这么说!

测试工程师在公司里的地位一言难尽&#xff0c;产品挥斥苍穹&#xff0c;指引产品前路&#xff1b;开发编写代码实现功能&#xff0c;给产品带来瞩目成就。两者&#xff0c;一个是领航员&#xff0c;一个是开拓者&#xff0c;都是聚光灯照耀的对象&#xff0c;唯独团队中的保障…

换脸方法大汇总:生成对抗网络GAN、扩散模型等

1、One-Shot Face Video Re-enactment using Hybrid Latent Spaces of StyleGAN2StyleGAN的高保真人像生成&#xff0c;已逐渐克服了单样本面部视频驱动重现的低分辨率限制&#xff0c;但这些方法至少依赖于以下其中之一&#xff1a;明确的2D/3D先验&#xff0c;基于光流作为运…

Android 基础知识4-2.5View与VIewGroup的概念、关系与区别

1.概念&#xff1a; Android里的图形界面都是由View和ViewGroup以及他们的子类构成的&#xff1a; View&#xff1a;所有可视化控件的父类,提供组件描绘和时间处理方法 ViewGroup&#xff1a; View类的子类&#xff0c;可以拥有子控件,可以看作是容器 Android UI中的控件都是…

Java【七大算法】算法详细图解,一篇文章吃透

文章目录一、排序相关概念二、七大排序1&#xff0c;直接插入排序2&#xff0c;希尔排序3&#xff0c;选择排序4&#xff0c;堆排序5&#xff0c;冒泡排序5.1冒泡排序的优化6&#xff0c;快速排序6.1 快速排序的优化7&#xff0c;归并排序三、排序算法总体分析对比总结提示&…