探索设计模式的魅力:一次设计,多次利用,深入理解原型模式的设计艺术

 

    原型模式是一种设计模式,属于创建型模式的一种,它用于创建重复的对象,同时又能保持性能。在原型模式中,通过复制现有对象的原型来创建新对象,而不是通过实例化类来创建对象。这样做可以避免耗费过多的资源开销,特别是在对象的创建过程比较复杂或耗时的情况下。

    在原型模式中,原型对象实现一个克隆方法(Clone)用于复制自身,当需要创建新对象时,就可以通过克隆原型对象来得到一个新的对象副本。原型模式通常包括浅拷贝和深拷贝两种形式,浅拷贝只复制对象本身,而深拷贝则会连同对象引用的其他对象一起复制,因此能够得到完全独立的新对象。

    原型模式可以在需要大量创建相似对象的场景中发挥作用,它能够提高对象的创建效率,同时也能够减少对类的直接依赖,使系统结构更灵活。

  关键角色有两个:

  1. 原型(Prototype):定义用于复制现有对象的接口,通常包含一个克隆方法,用于返回一个克隆对象。
  2. 具体原型(Concrete Prototype):实现原型接口,实现克隆方法来复制自身。

  优点:

  1. 减少对象创建时间:原型模式通过复制现有对象来创建新对象,避免了昂贵的对象创建过程,特别是在需要频繁创建相似对象时,可以大大减少对象创建的时间和开销。
  2. 简化对象创建过程:原型模式封装了对象的创建过程,客户端无需关心具体的创建细节,使得对象创建变得更加简单,提高了系统的可维护性和扩展性。
  3. 动态增加或减少原型:原型模式允许动态地添加或删除原型,使得系统更加灵活,能够根据需求动态创建新的原型对象。

  缺点:

  1. 需要理解原型对象:在使用原型模式时,需要确保原型对象的克隆方法能够正确地复制对象的状态,有时需要深度复制而不是浅复制,这需要额外的处理和理解。
  2. 难以保持一致性:原型模式可能会造成系统中的一些对象克隆出来之后状态难以保持一致,特别是涉及到对象之间的引用关系时,需要特别小心处理。

  本质体现了两个关键点:

  1. 原型:具备复制能力的对象,它是创建新对象的模板。原型可以是一个接口、抽象类或具体类,关键是它需要提供克隆自身的方法。
  2. 克隆:根据原型对象复制出来的新对象。克隆过程可以是浅复制(只复制对象本身)或者深复制(复制对象和其引用的对象)。

    原型模式的本质是通过复制现有对象来创建新对象,从而封装了对象的创建过程,提供了一种灵活、高效的对象创建方式。

目录

一、案例

1.1 不用模式来实现

1.2 有何问题

1.3 原型模式重构代码

1.4 完美实现 

三、模式讲解

3.1 功能

3.2 原型模式的结构和说明

 3.3 几种工厂模式总结


一、案例

场景:前段时间有一个考试,发现在一个教室里考试的试卷有ABCDEFG卷,座位上前后左右人的卷子跟我的都不一样。考完后听老师说,ABCDEFG卷的题目是一样的,意思就是题目是一样的,题目的顺序是打乱的甚至同一个题的选项的顺序也是打乱的。

    下面我们从无到有来感受原型模式的设计艺术。

1.1 不用模式来实现

  选择题类:

@Data
public class ChoiceQuestion {

    /**
     * 选择题题目
     */
    private String title;
    /**
     * 选项
     */
    private Map<String, String> options;
    /**
     * 答案
     */
    private String key;

    public ChoiceQuestion(){}
    public ChoiceQuestion(String title, Map<String, String> options, String key) {
        this.title = title;
        this.options = options;
        this.key = key;
    }
}

  填空题类:

@Data
public class Completion {

    /**
     * 填空题题目
     */
    private String title;
    /**
     * 答案
     */
    private List<String> key;
    public Completion() {}

    public Completion(String title, List<String> key) {
        this.title = title;
        this.key = key;
    }
}

  试卷类:

public class TestPaper {

    /**
     * 创建试卷
     * @param name 考生姓名
     * @param code 考生编号
     * @return
     */
    public String createPaper(String name, String code) {
        // 这里举例有两道选择题和两道填空题
        List<ChoiceQuestion> choiceQuestionList = new ArrayList<>(2);
        List<Completion> completionList = new ArrayList<>(2);

        // 初始化选择题
        Map<String, String> choiceMap1 = new HashMap<>();
        choiceMap1.put("A", "1");
        choiceMap1.put("B", "2");
        choiceMap1.put("C", "3");
        choiceMap1.put("D", "4");
        Map<String, String> choiceMap2 = new HashMap<>();
        choiceMap2.put("A", "1");
        choiceMap2.put("B", "2");
        choiceMap2.put("C", "3");
        choiceMap2.put("D", "4");
        choiceQuestionList.add(new ChoiceQuestion("1 + 1 = ", choiceMap1, "B"));
        choiceQuestionList.add(new ChoiceQuestion("3 + 1 = ", choiceMap2, "D"));

        // 初始化填空题
        List<String> comList1 = new ArrayList<>();
        comList1.add("富强");
        comList1.add("民主");
        List<String> comList2 = new ArrayList<>();
        comList2.add("51");
        comList2.add("21");

        completionList.add(new Completion("任意列举出两个社会主义核心价值观", comList1));
        completionList.add(new Completion("2008年中国获得金牌数___枚,银牌___枚", comList2));

        String newLine = "\n";
        // 组装试卷
        StringBuffer strBuff = new StringBuffer();
        strBuff.append("考生:").append(name).append(newLine)
                .append("考号:").append(code).append(newLine)
                .append("一、选择题").append(newLine);

        for (ChoiceQuestion choiceQuestion : choiceQuestionList) {
            strBuff.append(choiceQuestion.getTitle()).append(newLine);
            for (Map.Entry<String, String> entry : choiceQuestion.getOptions().entrySet()) {
                String key = entry.getKey();
                String value = entry.getValue();
                strBuff.append(key).append(":").append(value).append(newLine);
            }
            strBuff.append("答案:").append(choiceQuestion.getKey()).append(newLine);
        }
        for (Completion completion : completionList) {
            strBuff.append(completion.getTitle()).append(newLine);
            strBuff.append("答案:");
            for (String anster : completion.getKey()) {
                strBuff.append("  ").append(anster);
            }
        }

        return strBuff.toString();
    }
}

  测试客户端:

public class Client {

    public static void main(String[] args) {
        TestPaper testPaper = new TestPaper();
        System.out.println(testPaper.createPaper("张三", "ACCDF0001"));
        System.out.println(testPaper.createPaper("李四", "ACCDF0002"));
        System.out.println(testPaper.createPaper("王五", "ACCDF0003"));

    }
}

  运行结果如下:

考生:张三
考号:ACCDF0001
一、选择题
1 + 1 = 
A:1
B:2
C:3
D:4
答案:B
3 + 1 = 
A:1
B:2
C:3
D:4
答案:D
任意列举出两个社会主义核心价值观
答案:  富强  民主
2008年中国获得金牌数___枚,银牌___枚
答案:  51  21

考生:李四
考号:ACCDF0002
一、选择题
1 + 1 = 
A:1
B:2
C:3
D:4
答案:B
3 + 1 = 
A:1
B:2
C:3
D:4
答案:D
任意列举出两个社会主义核心价值观
答案:  富强  民主
2008年中国获得金牌数___枚,银牌___枚
答案:  51  21

考生:王五
考号:ACCDF0003
一、选择题
1 + 1 = 
A:1
B:2
C:3
D:4
答案:B
3 + 1 = 
A:1
B:2
C:3
D:4
答案:D
任意列举出两个社会主义核心价值观
答案:  富强  民主
2008年中国获得金牌数___枚,银牌___枚
答案:  51  21

  一坨坨代码很快就实现了,看上去很简单。 

        

1.2 有何问题

  • 以上呢就是三位考试的试卷; 线三 、李四和王五 ,每个⼈人的试卷内容是⼀一样的这没问题,但是三个人的题⽬以及选项顺序和答案都是一样的,但是没有达到我们的场景中“ABCDEFG卷题目顺序和选项顺序都不一样”的目的。
  • 以上代码⾮非常难扩展,随着题⽬目的不不断的增加以及乱序功能的补充,都会让这段代码变 得越来越混乱。 

         

1.3 原型模式重构代码

原型模式解决的痛点:创建⼤量重复的类,⽽我们的场景就需要给不不同的用户都创建相同的试卷,但这些试卷的题目不便于每次都从库中获取,甚至有时候需要从远程的RPC中获取。这样都是⾮常耗时的,⽽且随着创建对象的增多将严重影响效率。

        

 用原型模式注意要点:

  • 原型对象的接口:在原型模式中,原型对象需要实现一个克隆方法(Clone),该方法用于复制自身并返回一个新的对象副本。这需要确保原型对象具有适当的接口和克隆能力。

  • 浅拷贝和深拷贝:在实现克隆方法时,需要考虑对象中是否存在引用类型的成员变量。浅拷贝只会复制对象本身,而不会复制对象包含的引用类型成员变量,这意味着原型对象和克隆对象会引用同一个引用类型的成员变量。深拷贝则会复制对象本身以及其引用类型的成员变量,使得原型对象和克隆对象完全独立。选择浅拷贝还是深拷贝取决于实际需求和设计考虑。

  • 创建新对象的方式:在使用原型模式创建新对象时,可以通过调用原型对象的克隆方法,或者使用原型管理器(Prototype Manager)来获取新对象。原型管理器可以维护一组原型对象,并根据需要返回相应的克隆对象。

  • 修改克隆对象:当获得克隆对象后,可以根据需要对克隆对象做进一步的修改。这使得每个克隆对象都可以根据需求进行个性化的定制。

         

  代码改造

  添加选项答案类:

@Data
public class Option {

    /**
     * 选项
     */
    private Map<String, String> options;
    /**
     * 答案
     */
    private String key;

    public Option(Map<String, String> options, String key) {
        this.options = options;
        this.key = key;
    }
}

  添加选项乱序工具类:

public class RandomUtil {

    /**
     * 乱序Map元素,记录对应答案key
     * @param option 题⽬选项 
     * @param key 答案
     * @return Option 乱序后 {A=c., B=d., C=a., D=b.} */
    static public Option random(Map<String, String> option, String key) {
        Set<String> keySet = option.keySet();
        ArrayList<String> keyList = new ArrayList<>(keySet);
        Collections.shuffle(keyList);
        HashMap<String, String> optionNew = new HashMap<>();
        int idx = 0;
        String keyNew = "";
        for (String next : keySet) {
            String randomKey = keyList.get(idx++);
            if (key.equals(next)) {
                keyNew = randomKey;
            }
            optionNew.put(randomKey, option.get(next));
        }
        return new Option(optionNew, keyNew);
    }
}

  添加题库类

@Data
public class QuestionBank implements Cloneable {

    /**
     * 试卷序号
     */
    private String seq;
    /**
     * 考生姓名
     */
    private String name;
    /**
     * 考生考号
     */
    private String code;

    private ArrayList<ChoiceQuestion> choiceQuestionList = new ArrayList<>(2);
    private ArrayList<Completion> completionList = new ArrayList<>(2);

    /**
     * 实现中题目可能来自数据库中,这里示例便直接写死
     */
    public QuestionBank() {

        // 初始化选择题
        Map<String, String> choiceMap1 = new HashMap<>();
        choiceMap1.put("A", "1");
        choiceMap1.put("B", "2");
        choiceMap1.put("C", "3");
        choiceMap1.put("D", "4");
        Map<String, String> choiceMap2 = new HashMap<>();
        choiceMap2.put("A", "1");
        choiceMap2.put("B", "2");
        choiceMap2.put("C", "3");
        choiceMap2.put("D", "4");
        this.choiceQuestionList.add(new ChoiceQuestion("1 + 1 = ", choiceMap1, "B"));
        this.choiceQuestionList.add(new ChoiceQuestion("3 + 1 = ", choiceMap2, "D"));

        // 初始化填空题
        List<String> comList1 = new ArrayList<>();
        comList1.add("富强");
        comList1.add("民主");
        List<String> comList2 = new ArrayList<>();
        comList2.add("51");
        comList2.add("21");

        this.completionList.add(new Completion("任意列举出两个社会主义核心价值观", comList1));
        this.completionList.add(new Completion("2008年中国获得金牌数___枚,银牌___枚", comList2));
    }

    @Override
    public QuestionBank clone() {
        try {
            QuestionBank questionBank = (QuestionBank) super.clone();
            questionBank.setChoiceQuestionList((ArrayList<ChoiceQuestion>) this.getChoiceQuestionList().clone());
            questionBank.setCompletionList((ArrayList<Completion>) this.getCompletionList().clone());

            // 打乱选择题
            Collections.shuffle(questionBank.getChoiceQuestionList());
            // 选择题的选项打乱
            for (ChoiceQuestion choiceQuestion : questionBank.getChoiceQuestionList()) {
                Option option = RandomUtil.random(choiceQuestion.getOptions(), choiceQuestion.getKey());
                choiceQuestion.setOptions(option.getOptions());
                choiceQuestion.setKey(option.getKey());
            }
            // 打乱填空题
            Collections.shuffle(questionBank.getCompletionList());

            return questionBank;
        } catch (Exception e) {
            throw new AssertionError();
        }
    }

    @Override
    public String toString() {
        String newLine = "\r\n";
        StringBuffer strBuff = new StringBuffer();
        strBuff.append("考卷:").append(getSeq()).append(newLine)
                .append("考生:").append(getName()).append(newLine)
                .append("考号:").append(getCode()).append(newLine);
        strBuff.append("一、选择题").append(newLine);

        for (ChoiceQuestion choiceQuestion : getChoiceQuestionList()) {
            strBuff.append(choiceQuestion.getTitle()).append(newLine);
            for (Map.Entry<String, String> entry : choiceQuestion.getOptions().entrySet()) {
                String key = entry.getKey();
                String value = entry.getValue();
                strBuff.append(key).append(":").append(value).append(newLine);
            }
            strBuff.append("答案:").append(choiceQuestion.getKey()).append(newLine);
        }

        strBuff.append("一、填空题").append(newLine);
        for (Completion completion : getCompletionList()) {
            strBuff.append(completion.getTitle()).append(newLine);
            strBuff.append("答案:");
            for (String anster : completion.getKey()) {
                strBuff.append("  ").append(anster);
            }
        }

        return strBuff.toString();
    }
}

  试卷类改造:

@Data
public class TestPaper {

    /**
     * 题库
     */
    QuestionBank questionBank = new QuestionBank();

    /**
     * 创建试卷
     * @param req 试卷序号
     * @param name 考生姓名
     * @param code 考生编号
     * @return
     */
    public String createPaper(String req, String name, String code) {
        QuestionBank questionBankClone = this.getQuestionBank().clone();
        questionBankClone.setSeq(req);
        questionBankClone.setName(name);
        questionBankClone.setCode(code);

        return questionBankClone.toString();
    }
}

  改造测试客户端:

public class Client {

    public static void main(String[] args) {
        // 创建试卷工具类并初始化题库
        TestPaper testPaper = new TestPaper();
        // 创建张三
        System.out.println(testPaper.createPaper("A", "张三", "A0001"));
        System.out.println(testPaper.createPaper("B", "李四", "A0002"));
        System.out.println(testPaper.createPaper("C", "王五", "A0003"));
    }
}

  运行结果如下:

  (考卷A)

考卷:A
考生:张三
考号:A0001
一、选择题
1 + 1 = 
A:3
B:1
C:2
D:4
答案:C
3 + 1 = 
A:3
B:1
C:4
D:2
答案:C
一、填空题
2008年中国获得金牌数___枚,银牌___枚
答案:  51  21
任意列举出两个社会主义核心价值观
答案:  富强  民主

  (考卷B)

考卷:B
考生:李四
考号:A0002
一、选择题
3 + 1 = 
A:4
B:1
C:2
D:3
答案:A
1 + 1 = 
A:2
B:4
C:3
D:1
答案:A
一、填空题
2008年中国获得金牌数___枚,银牌___枚
答案:  51  21
任意列举出两个社会主义核心价值观
答案:  富强  民主

  (考卷C)

考卷:C
考生:王五
考号:A0003
一、选择题
1 + 1 = 
A:1
B:4
C:3
D:2
答案:D
3 + 1 = 
A:4
B:3
C:1
D:2
答案:A
一、填空题
任意列举出两个社会主义核心价值观
答案:  富强  民主
2008年中国获得金牌数___枚,银牌___枚
答案:  51  21

         

1.4 完美实现 

    从上面结果看出,A卷、B卷和C卷的题目和选项都被打乱了。再添加DEF等卷,就完美实现了案例场景下我座位中前后左右人的卷子与我不同的情况

        

三、模式讲解

本质:通过复制现有对象来创建新对象,从而封装了对象的创建过程,提供了一种灵活、高效的对象创建方式。

3.1 功能

    原型模式 的功能 实际上包含两个方面:

  • 一个是通过克隆来创建新的对象实例;
  • 另一个是为克隆出来的对象实例复制原型实例属性的值。

3.2 原型模式的结构和说明

  • Prototype:声明一个克隆自身的接口,用来约束想要克隆自己的类,要求它们都要实现这里定义的克隆方法。
  • ConcretePrototype:实现Prototype接口的类,这些类真正实现了克隆自身的功能。
  • Client:使用原型的客户端,首先获取到原型对象,然后通过原型实例克隆自身来创建新的对象实例。

        

 3.3 几种工厂模式总结

    工厂方法、抽象工厂、建造者模式和原型模式是常见的软件设计模式,它们各自解决了不同类型的设计问题。

  1. 工厂方法模式:
    工厂方法模式是一种创建型设计模式,它提供了一种将对象的创建和使用分离的方式。核心在于定义一个创建对象的接口,但将具体的创建过程延迟到子类中进行实现。

  2. 抽象工厂模式:
    抽象工厂模式也是一种创建型设计模式,它提供了一种创建一系列相关或相互依赖对象的接口,而无需指定具体的类。核心在于提供一个能够创建一系列产品的工厂接口,而具体的工厂类负责实现这个接口并创建具体的产品。

  3. 建造者模式:
    建造者模式是一种创建型设计模式,它将一个复杂对象的构建过程与其表示相分离,并允许使用相同的构建过程来创建不同的表示。核心在于将对象的构建过程分解成多个步骤,并提供一个统一的接口来组合这些步骤,从而创建复杂对象。

  4. 原型模式:
    原型模式是一种创建型设计模式,它允许通过复制现有对象来创建新对象,而不是通过实现一个特定的工厂方法来创建对象。核心在于通过克隆已有的对象来创建新的对象,从而避免了通过构造函数创建对象时的复杂性。

  区别:

  • 工厂方法模式和抽象工厂模式都是用于对象创建,但工厂方法模式关注于单个对象的创建,而抽象工厂模式关注于一系列相关对象的创建。
  • 建造者模式主要用于创建复杂对象,并且可以根据需要定制对象的不同表示,而工厂方法和抽象工厂模式更多地关注于对象的创建过程和对象的组合。
  • 原型模式主要用于通过复制来创建新对象,避免了直接通过构造函数创建对象时的复杂性,与工厂方法、抽象工厂和建造者模式的区别在于其实现方式和目的不同。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

第二节 K8S 的架构

第二节 K8S 的架构 K8S 架构图如下: 官方文档: https://kubernetes.io/docs/concepts/architecture/ kube-api-server 是集群的核心&#xff0c; 是k8s中最重要的组件&#xff0c; 因为它是实现声明式api的关键, 整个集群的入口,所有请求都要经过它, api接口服务. kubernetes…

Navicat使用HTTP通道连接远程服务器的SQLite文件

拷贝ntunnel_sqlite.php文件到Linux机器中 ntunnel_sqlite.php文件位置&#xff1a; 在Navicat安装位置中可以找到ntunnel_sqlite.php文件&#xff0c;其他两个类似文件是支持MySQL和pgsql的

【开源】基于JAVA+Vue+SpringBoot的高校宿舍调配管理系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能需求2.1 学生端2.2 宿管2.3 老师端 三、系统展示四、核心代码4.1 查询单条个人习惯4.2 查询我的室友4.3 查询宿舍4.4 查询指定性别全部宿舍4.5 初次分配宿舍 五、免责说明 一、摘要 1.1 项目介绍 基于JAVAVueSpringBootMySQL的…

k8s之包管理器Helm

helm的作用就是通过打包的方式&#xff0c;把deployment service ingress这些打包在一块&#xff0c;一键式的部署服务。类似yum官方提供的一个类似与安装仓库的功能&#xff0c;可以实现一键化部署应用。 Helm的三个重要概念 ●Chart&#xff1a;Helm 的软件包&#xff0c;采…

从物联网看智慧文旅的未来:技术与实践的完美结合,重塑旅游体验的新篇章

一、物联网技术&#xff1a;智慧文旅的基石 随着科技的飞速发展&#xff0c;物联网技术已经深入到我们生活的方方面面&#xff0c;尤其在智慧文旅领域&#xff0c;物联网技术更是起到了不可或缺的作用。它如同智慧文旅的基石&#xff0c;为旅游行业带来了前所未有的创新和变革…

node.js(express.js)+mysql实现新增文章分类功能

表单验证 // 导入定义验证规则的包 // const joi require("hapi/joi"); const joi require("joi"); /*** string()值必须是字符串* alphanum()值只能包含a-zA-ZO-9的字符串* min(length) 最小长度* max(length) 大长度* required() 值是必填项&#xff0…

VSCode插件 —— Cody AI (免费AI助手!)

之前介绍过一款 阿里云免费的AI开发工具——通义灵码 TONGYI Lingma 本文再推荐一个可以极大提高开发前端开发效率的工具 —— Cody AI &#xff08;Sourcegraph&#xff09;&#xff0c;同样是免费的&#xff01; 不过&#xff0c;使用Cody AI需要有github 或 Google 、 git…

cadence中统计高电平波形的两种方法(transient measurement和value cross函数)

cadence中统计高电平波形的两种方法&#xff08;transient measurement和value cross函数&#xff09; 一、measurement——transient measurement 如图&#xff0c;为比较器的输出 选择想要查看的波形&#xff0c;右侧会出现对此波形上升沿下降沿的统计结果&#xff0c;如图…

基于时空模型的视频异常检测

假设存在一个运动区域&#xff0c;规则要求只能进行特定的运动项目。 出于安全原因或因为业主不喜欢而禁止进行任何其他活动:)。 我们要解决的问题是&#xff1a;如果我们知道正确行为的列表&#xff0c;我们是否可以创建一个视频监控系统&#xff0c;在出现不常见的行为发出通…

使用WAF防御网络上的隐蔽威胁之目录穿越

目录穿越&#xff08;Directory Traversal&#xff09;是一种网络安全攻击手段&#xff0c;也被称为路径穿越。 这种攻击允许攻击者访问存储在Web服务器文件系统上的文件和目录&#xff0c;这些文件和目录原本不应该对用户可见或可访问。 通过利用安全漏洞&#xff0c;攻击者…

黑马axios案例之地区查询

查询某个省内某个城市的所有地区 接口&#xff1a;http://hmajax.itheima.net/api/area 参数名: pname:省份名字或直辖市名字&#xff0c;比如北京、福建省、辽宁省… cname:城市名字&#xff0c;比如北京市、厦门市、大连市… <!DOCTYPE html> <html lang"en&q…

白话Kubernetes网络

1 Kubernetes 网络介绍 Kubernetes 网络是一个系统&#xff0c;能够使不同集群内外的组件相互通信。这个系统会处理许多情况&#xff0c;其中重要的情况包括 Pod 之间的通信、Service 的通信以及集群如何处理来自外部的流量。 由于 Kubernetes 是分布式系统&#xff0c;因此它…

Git 版本控制 常用操作和项目应用

一、前言 1、何为版本控制&#xff1f; 版本控制是一种记录一个或若干文件内容变化&#xff0c;以便将来查阅特定版本修订情况的系统。 Git是目前最先进的分布式版本控制系统。 maven&#xff1a;jar包管理工具 版本管理工具&#xff1a;Git、Svn 2、Git & SVN对比 …

VUE项目目录与运行流程(VScode)

各目录对应名称含义 main.js&#xff08;导入App.vue&#xff0c;基于App.vue创建结构渲染index.html&#xff09; //核心作用&#xff1a;导入App.vue&#xff0c;基于App.vue创建结构渲染index.html//1.导入Vue核心包 import Vue from vue//2.导入App.vue根组件 import App f…

QT开发低功耗蓝牙BLE连接ECB02模块进行数据收发

时间记录&#xff1a;2024/1/22 一、注意点 &#xff08;1&#xff09;pro文件中引入bluetooth模块 &#xff08;2&#xff09;安卓端运行时需要同步打开定位功能才能扫描到蓝牙设备 &#xff08;3&#xff09;mingw套件不能在Windows上运行&#xff0c;需要使用MSVC套件编译…

UE5 C++学习笔记 FString FName FText相互转换

1.FString 是UE里的String。最接近std::string, 唯一可以修改的字符串类型。性能更低 TEXT(string) TEXT宏&#xff0c;作用是将字符串转换成Unicode&#xff0c;切记UE中使用字符串输出要使用该宏 2. FName 是UE里特有的类型。它更注重于表示名称不区分大小写&#xff0c;不…

c++学习第十一讲---文件操作

文件操作&#xff1a; c中对文件操作需要包含头文件 < fstream > 文本文件&#xff1a;以ASCII码形式储存 二进制文件&#xff1a;以二进制文件储存&#xff08;读不懂&#xff09; 操作文件三大类&#xff1a; 读&#xff1a;ifstream ; 写&#xff1a;ofstream ; 读…

旅游网站day13

1. 完善首页 1.1 首页banner查询接口 1.2 搜索服务 集成ES 1. 方式1&#xff1a;数据独立存储与独立搜索 2. 方式2&#xff1a;条件搜索与主键查询为搜索模块搭建一个服务 为什么需要api? 因为搜索也需要模型对象。 导入es依赖&#xff1a; 搜索api&#xff1a; ES工具类…

《WebKit 技术内幕》学习之十(1): 插件与JavaScript扩展

虽然目前的浏览器的功能很强 &#xff0c;但仍然有其局限性。早期的浏览器能力十分有限&#xff0c;Web前端开发者希望能够通过一定的机制来扩展浏览器的能力。早期的方法就是插件机制&#xff0c;现在流行次啊用混合编程&#xff08;Hybird Programming&#xff09;模式。插件…

transformer优化(二)-DETR 学习笔记

论文地址&#xff1a;https://arxiv.org/pdf/2005.12872.pdf 代码地址&#xff1a;https://github.com/bubbliiiing/detr-pytorch https://github.com/facebookresearch/detr 1.是什么&#xff1f; DETR&#xff08;Detection Transformer&#xff09;是一种基于Transforme…