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

 

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

    在原型模式中,原型对象实现一个克隆方法(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));}@Overridepublic 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();}}@Overridepublic 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. 原型模式:
    原型模式是一种创建型设计模式,它允许通过复制现有对象来创建新对象,而不是通过实现一个特定的工厂方法来创建对象。核心在于通过克隆已有的对象来创建新的对象,从而避免了通过构造函数创建对象时的复杂性。

  区别:

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

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

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

相关文章

python-自动篇-办公-用Excel画画

文章目录 代码所遇问题ModuleNotFoundError: No module named xlsxwriterFileNotFoundError: [Errno 2] No such file or directory: 111.jpg 效果附件图片excel 代码 # coding: utf-8from PIL import Image from xlsxwriter.workbook import Workbookclass ExcelPicture(obje…

使用双异步后,如何保证数据一致性?

目录 一、前情提要二、通过Future获取异步返回值1、FutureTask 是基于 AbstractQueuedSynchronizer实现的2、FutureTask执行流程3、get()方法执行流程 三、FutureTask源码具体分析1、FutureTask源码2、将异步方法的返回值改为Future<Integer>&#xff0c;将返回值放到new…

1.21 day6 IO网络编程

网络聊天室 服务端 #include <myhead.h> #define PORT 8888 #define IP "192.168.122.48" struct MSG {char tyep;char name[20];char buf[128]; }; typedef struct Node {struct sockaddr_in cin;struct Node*next; }*node;int main(int argc, const char *…

提升认知,推荐15个面向开发者的中文播客

前言 对于科技从业者而言&#xff0c;无论是自学成才的程序员&#xff0c;还是行业资深人士&#xff0c;终身学习是很有必要的&#xff0c;尤其是在这样一个技术快速迭代更新的时代。 作为一个摆脱了时间和空间限制的资讯分享平台&#xff0c;播客&#xff08;Podcast&#x…

42 漏洞发现-操作系统之漏洞探针类型利用修复

目录 系统漏洞发现意义?漏洞类型危害情况?如何做好未卜先知?相关名词解释:漏洞扫描工具演示-Goby&#xff0c;Nmap&#xff0c;Nessus(操作)漏洞类型区分讲解-权限提升,远程执行等(思路)漏洞利用框架演示-Metasploit,Searchsploit等(操作)漏洞修复方案讲解说明-补丁,防护软件…

go和swoole性能比较

开发效率 Go语言是本质上是静态语言&#xff0c;开发效率稍差&#xff0c;但性能更强&#xff0c;更适合底层软件的开发 Swoole使用PHP语言&#xff0c;动态脚本语言&#xff0c;开发效率最佳&#xff0c;更适合应用软件的开发 IO模型 go语言使用单线程eventloop处理IO事件&…

GO 的那些 IDE

文章目录 支持哪些功能快捷键代码高亮代码格式化代码提示导航跳转代码调试构建编译其他功能 GO有哪些IDEGolandVS CodeVim GOSublime TextAtomLiteIDEEclipse 总结 “程序员为什么要使用 IDE”&#xff0c;在一些社区论坛&#xff0c;经常可以看到这样的提问。关于是否应该使用…

Tomcat目录和文件

打开tomcat的解压之后的目录可以看到如下的目录结构&#xff1a; Bin bin目录主要是用来存放tomcat的命令&#xff0c;主要有两大类&#xff0c;一类是以.sh结尾的&#xff08;linux命令&#xff09;&#xff0c;另一类是以.bat结尾的&#xff08;windows命令&#xff09;。 …

sqlmap使用教程(2)-连接目标

目录 连接目标 1.1 设置认证信息 1.2 配置代理 1.3 Tor匿名网络 1.4 检测WAF/IPS 1.5 调整连接选项 1.6 处理连接错误 连接目标 场景1&#xff1a;通过代理网络上网&#xff0c;需要进行相应配置才可以成功访问目标主机 场景2&#xff1a;目标网站需要进行身份认证后才…

微信小程序-03

小程序官方把 API 分为了如下 3 大类&#xff1a; 事件监听 API 特点&#xff1a;以 on 开头&#xff0c;用来监听某些事件的触发 举例&#xff1a;wx.onWindowResize(function callback) 监听窗口尺寸变化的事件 同步 API 特点1&#xff1a;以 Sync 结尾的 API 都是同步 API 特…

扫地机器人(二分算法+贪心算法)

1. if(robot[i]-len<sweep)这个代码的意思是——如果机器人向左移动len个长度后&#xff0c;比现在sweep的位置&#xff08;现在已经覆盖的范围&#xff09;还要靠左&#xff0c;就是覆盖连续不起来&#xff0c;呢么这个len就是有问题的&#xff0c;退出函数&#xff0c;再…

黑马axios案例之地区查询

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

嵌入式软件工程师面试题——2025校招社招通用(计算机网络篇)(三十二)

说明&#xff1a; 面试群&#xff0c;群号&#xff1a; 228447240面试题来源于网络书籍&#xff0c;公司题目以及博主原创或修改&#xff08;题目大部分来源于各种公司&#xff09;&#xff1b;文中很多题目&#xff0c;或许大家直接编译器写完&#xff0c;1分钟就出结果了。但…

[足式机器人]Part2 Dr. CAN学习笔记- 最优控制Optimal Control Ch07-4 轨迹追踪

本文仅供学习使用 本文参考&#xff1a; B站&#xff1a;DR_CAN Dr. CAN学习笔记 - 最优控制Optimal Control Ch07-4 轨迹追踪 1. 目标误差控制-误差的调节2. 稳态非零值控制3. 输入增量控制 1. 目标误差控制-误差的调节 2. 稳态非零值控制 3. 输入增量控制

fiddler流量捕获之winconfig

windows 使用了一种叫做“AppContainer”的隔离技术&#xff0c;使得一些流量无法正常捕获&#xff0c;在 fiddler中点击 WinConfig 按钮可以解除&#xff0c;这个与菜单栏 Tools→Win8 Loopback Exemptions 功能是相同的&#xff0c;流量捕获&#xff1a;指拦截和记录通过计算…

GPT-4 的决策在股市中进行量化投资

论文题目:Can Large Language Models Beat Wall Street? Unveiling the Potential of AI in Stock Selection 论文链接:https://arxiv.org/abs/2401.03737 博客地址:https://www.marketsense-ai.com/ 从本质上来说&#xff0c;股票选择是个价格发现机制&#xff0c;在股票投…

Web 开发 1: Flask 框架介绍和使用

在 Web 开发中&#xff0c;Flask 是一个流行且灵活的 Python Web 框架&#xff0c;用于构建 Web 应用程序。它简洁而易于上手&#xff0c;适用于小型到中型的项目。在本篇博客中&#xff0c;我将为你介绍 Flask 框架的基础知识和常用技巧&#xff0c;帮助你更好地掌握 Web 开发…

opencv#28 图像卷积

图像卷积 图像卷积是图像处理中最为基础的操作之一&#xff0c;其常用在图像的边缘检测&#xff0c;图像的去噪声以及图像压缩等领域。 图像卷积主要步骤: Step1:将卷积模板旋转180。 Step2:卷积模板移动到对应位置。 Step3:模板内求和&#xff0c;保存求和结果。 Step4:滑…

容器技术2-镜像与容器储存

目录 一、镜像制作 1、ddocker build 2、docker commit 二、镜像存储 1、公共仓库 2、私有仓库 三、镜像使用 四、容器存储 1、镜像元数据 2、存储驱动 3、数据卷 一、镜像制作 1、ddocker build 基于 Dockerfile 自动构建镜像 其机制为&#xff1a;每一行都会基于…

<C++>STL->string

string类的由来 这是string的定义&#xff1a; string类是模板实例化后的别名&#xff0c;basic_string是字符串类模板&#xff0c;常见的字符串类型有wchar_t char char16_t char32_t &#xff0c;basic_string类针对的是所有字符串类型设计出来的一个模板&#xff0c;而我…