零基础设计模式——第二部分:创建型模式 - 原型模式
第二部分:创建型模式 - 5. 原型模式 (Prototype Pattern)
我们已经探讨了单例、工厂方法、抽象工厂和生成器模式。现在,我们来看创建型模式的最后一个主要成员——原型模式。这种模式关注的是通过复制现有对象来创建新对象,而不是通过传统的构造函数实例化。
- 核心思想:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
原型模式 (Prototype Pattern)
“用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。”
想象一下细胞分裂:一个细胞(原型)可以通过分裂(克隆)产生一个新的、与自身几乎完全相同的细胞。或者,在绘图软件中,你画了一个复杂的图形(原型),然后可以通过“复制”和“粘贴”操作快速创建多个相同的图形副本,再对副本进行微调。
原型模式的核心就是“克隆”或“复制”。当创建一个对象的成本很高(例如,需要复杂的计算、数据库查询或网络请求)时,如果已经有一个相似的对象存在,通过复制这个现有对象来创建新对象可能会更高效。
1. 目的 (Intent)
原型模式的主要目的:
- 提高性能:当创建新对象的成本较大时(如初始化时间长、资源消耗多),通过复制已有的原型实例来创建新对象,可以避免重复执行这些昂贵的初始化操作。
- 简化对象创建:如果一个对象的创建过程比较复杂,或者需要依赖某些运行时的状态,通过克隆一个已配置好的原型对象可以简化新对象的创建。
- 动态添加或删除产品:可以在运行时通过注册原型实例来动态地增加或删除系统中可用的产品类型,而无需修改工厂类(如果与工厂模式结合使用)。
- 避免与产品具体类耦合:客户端可以只知道抽象的原型接口,通过克隆来获取新对象,而无需知道具体的实现类名。
2. 生活中的例子 (Real-world Analogy)
-
复印文件:
- 原型 (Prototype):原始文件(比如一份合同模板)。
- 克隆操作 (Clone):复印机复印的过程。
- 新对象 (Cloned Object):复印出来的文件副本。
你不需要重新打字排版来得到一份新的合同,只需要复印原件,然后在副本上修改少量信息(如签约方、日期)即可。
-
生物克隆:如克隆羊多莉。多莉就是通过复制现有羊的细胞(原型)而创建的。
-
制作模具和铸件:
- 原型:一个精心制作的模具。
- 克隆操作:使用模具进行浇筑。
- 新对象:通过模具生产出来的多个相同铸件。
-
游戏中的敌人复制:在一个游戏中,当需要生成大量相同类型的敌人时,可以先创建一个敌人对象作为原型,并设置好其初始属性(如生命值、攻击力、模型等)。之后需要新的敌人时,直接克隆这个原型,而不是每次都从头加载资源和设置属性。
3. 结构 (Structure)
原型模式的结构相对简单,通常包含以下角色:
- Prototype (抽象原型):声明一个克隆自身的接口(通常是一个
clone()
方法)。 - ConcretePrototype (具体原型):实现 Prototype 接口,重写
clone()
方法来复制自身。这个类是实际被复制的对象。 - Client (客户端):让一个原型克隆自身从而创建一个新的对象。客户端不需要知道具体的原型类名,只需要通过抽象原型接口来操作。
克隆过程: - 客户端持有一个抽象原型对象的引用。
- 当客户端需要一个新的对象时,它调用原型对象的
clone()
方法。 - 具体原型类实现
clone()
方法,创建一个当前对象的副本并返回。 - 客户端得到一个新的对象,这个新对象与原型对象具有相同的初始状态(属性值)。
4. 深拷贝 vs. 浅拷贝 (Deep Copy vs. Shallow Copy)
这是原型模式中一个非常重要的概念。
-
浅拷贝 (Shallow Copy):
- 当复制一个对象时,只复制对象本身和其中的基本数据类型成员的值。
- 如果对象包含对其他对象的引用(引用类型成员),则只复制这些引用,而不复制引用所指向的对象。因此,原对象和副本中的引用类型成员将指向内存中的同一个对象。
- 修改副本中的引用类型成员所指向的对象,会影响到原对象中对应的成员(因为它们指向同一个东西)。
-
深拷贝 (Deep Copy):
- 当复制一个对象时,不仅复制对象本身和基本数据类型成员,还会递归地复制所有引用类型成员所指向的对象。
- 原对象和副本中的引用类型成员将指向不同的、内容相同的对象。
- 修改副本中的引用类型成员所指向的对象,不会影响到原对象。
选择深拷贝还是浅拷贝取决于具体需求。如果希望副本的修改不影响原型,或者原型和副本需要独立地管理其引用的对象,那么应该使用深拷贝。如果共享引用的对象是不可变的,或者业务逻辑允许共享,那么浅拷贝可能就足够了,并且性能通常更高。
在Java中,Object
类的 clone()
方法默认执行的是浅拷贝。要实现深拷贝,需要在 clone()
方法中对引用类型的字段也进行递归克隆。
在Go中,没有内建的 clone()
方法。复制通常通过创建一个新实例并手动复制字段值来完成。对于引用类型(如切片、映射、指针),需要特别注意是复制引用还是复制底层数据。
5. 适用场景 (When to Use)
- 当一个系统应该独立于它的产品创建、构成和表示时,并且你想要在运行时指定实例化的类。
- 当要实例化的类是在运行时指定时,例如,通过动态装载。
- 为了避免创建一个与产品类层次平行的工厂类层次时(即不想为了创建不同产品而创建一堆工厂类)。
- 当一个类的实例只能有几种不同状态组合中的一种时。建立相应数目的原型并克隆它们可能比每次用合适的状态手工实例化该类更方便一些。
- 创建对象的成本很高:例如,对象初始化需要大量计算、I/O操作或网络通信。
- 需要创建大量相似对象:只有少量属性不同的对象。
- 系统需要在运行时动态添加或修改可创建的对象类型。
6. 优缺点 (Pros and Cons)
优点:
- 性能提升:对于创建成本高的对象,克隆比重新创建更快。
- 简化对象创建:可以复制一个已经初始化好的复杂对象,避免重复的初始化逻辑。
- 灵活性高:可以在运行时动态地获取和复制原型对象。
- 对客户端隐藏具体类型:客户端只需要知道抽象原型接口即可创建对象。
缺点:
- 需要为每个类实现克隆方法:每个需要作为原型的类都必须实现
clone()
方法,这可能比较繁琐,特别是当类层次结构很深或包含许多字段时。 - 深拷贝实现复杂:正确实现深拷贝可能比较复杂,需要仔细处理所有引用类型的成员,以避免意外共享或循环引用问题。
- 可能违反开闭原则(如果克隆逻辑复杂):如果克隆逻辑非常复杂且依赖于具体类的内部结构,当具体类修改时,克隆方法可能也需要修改。
7. 实现方式 (Implementations)
让我们通过一个图形绘制的例子来看看原型模式的实现。假设我们有不同形状(圆形、矩形)的对象,它们可以被克隆。
抽象原型 (Shape)
// shape.go
package shapeimport "fmt"// Shape 抽象原型接口
type Shape interface {Clone() ShapeDraw()SetID(id string)GetID() string
}
// Shape.java
package com.example.shape;// 抽象原型接口
// Java 中通常让原型类实现 Cloneable 接口并重写 clone() 方法
public interface Shape extends Cloneable { // Cloneable 是一个标记接口Shape cloneShape(); // 自定义一个更明确的克隆方法名void draw();void setId(String id);String getId();
}
具体原型 (Circle, Rectangle)
// circle.go
package shapeimport "fmt"// Circle 具体原型
type Circle struct {ID stringRadius intX, Y int // 圆心坐标
}func NewCircle(id string, radius, x, y int) *Circle {return &Circle{ID: id, Radius: radius, X: x, Y: y}
}func (c *Circle) SetID(id string) { c.ID = id }
func (c *Circle) GetID() string { return c.ID }// Clone 实现浅拷贝,因为 Circle 的字段都是值类型或string (string在Go中是值类型行为)
func (c *Circle) Clone() Shape {return &Circle{ID: c.ID + "_clone", // 给克隆体一个新IDRadius: c.Radius,X: c.X,Y: c.Y,}
}func (c *Circle) Draw() {fmt.Printf("Drawing Circle [ID: %s, Radius: %d, Center: (%d,%d)]\n", c.ID, c.Radius, c.X, c.Y)
}// rectangle.go
package shapeimport "fmt"// Rectangle 具体原型
type Rectangle struct {ID stringWidth, Height intX, Y int // 左上角坐标
}func NewRectangle(id string, width, height, x, y int) *Rectangle {return &Rectangle{ID: id, Width: width, Height: height, X: x, Y: y}
}func (r *Rectangle) SetID(id string) { r.ID = id }
func (r *Rectangle) GetID() string { return r.ID }// Clone 实现浅拷贝
func (r *Rectangle) Clone() Shape {return &Rectangle{ID: r.ID + "_clone",Width: r.Width,Height: r.Height,X: r.X,Y: r.Y,}
}func (r *Rectangle) Draw() {fmt.Printf("Drawing Rectangle [ID: %s, Width: %d, Height: %d, TopLeft: (%d,%d)]\n", r.ID, r.Width, r.Height, r.X, r.Y)
}
// Circle.java
package com.example.shape;// 具体原型
public class Circle implements Shape {private String id;private int radius;private Point center; // 假设 Point 是一个自定义的可变类public Circle(String id, int radius, int x, int y) {this.id = id;this.radius = radius;this.center = new Point(x, y);System.out.println("Circle created with ID: " + id);}// 私有构造,用于克隆private Circle(String id, int radius, Point center) {this.id = id;this.radius = radius;this.center = center; // 注意这里,如果是浅拷贝,center会被共享}@Overridepublic void setId(String id) { this.id = id; }@Overridepublic String getId() { return this.id; }public int getRadius() { return radius; }public Point getCenter() { return center; }public void setCenter(int x, int y) { this.center.setX(x); this.center.setY(y); }@Overridepublic Shape cloneShape() {System.out.println("Cloning Circle with ID: " + this.id);Circle clonedCircle = null;try {// 默认的 Object.clone() 是浅拷贝clonedCircle = (Circle) super.clone(); // 为了实现深拷贝,需要对可变引用类型字段进行单独克隆clonedCircle.id = this.id + "_clone"; // 通常给克隆体新IDclonedCircle.center = (Point) this.center.clone(); // 假设 Point 也实现了 Cloneable 和 clone()} catch (CloneNotSupportedException e) {// This should not happen if we implement Cloneablee.printStackTrace();}return clonedCircle;}@Overridepublic void draw() {System.out.printf("Drawing Circle [ID: %s, Radius: %d, Center: %s]%n", id, radius, center);}
}// Rectangle.java
package com.example.shape;public class Rectangle implements Shape {private String id;private int width;private int height;private Point topLeft; // 可变引用类型public Rectangle(String id, int width, int height, int x, int y) {this.id = id;this.width = width;this.height = height;this.topLeft = new Point(x,y);System.out.println("Rectangle created with ID: " + id);}@Overridepublic void setId(String id) { this.id = id; }@Overridepublic String getId() { return this.id; }public Point getTopLeft() { return topLeft; }public void setTopLeft(int x, int y) { this.topLeft.setX(x); this.topLeft.setY(y); }@Overridepublic Shape cloneShape() {System.out.println("Cloning Rectangle with ID: " + this.id);Rectangle clonedRectangle = null;try {clonedRectangle = (Rectangle) super.clone();clonedRectangle.id = this.id + "_clone";clonedRectangle.topLeft = (Point) this.topLeft.clone(); // 深拷贝 Point} catch (CloneNotSupportedException e) {e.printStackTrace();}return clonedRectangle;}@Overridepublic void draw() {System.out.printf("Drawing Rectangle [ID: %s, Width: %d, Height: %d, TopLeft: %s]%n", id, width, height, topLeft);}
}// Point.java (辅助类,用于演示深拷贝)
package com.example.shape;public class Point implements Cloneable {private int x;private int y;public Point(int x, int y) { this.x = x; this.y = y; }public int getX() { return x; }public void setX(int x) { this.x = x; }public int getY() { return y; }public void setY(int y) { this.y = y; }@Overridepublic String toString() { return "(" + x + "," + y + ")"; }@Overrideprotected Object clone() throws CloneNotSupportedException {// Point 只包含基本类型,所以 super.clone() 已经是深拷贝效果了// 如果 Point 内部还有其他引用类型,则需要进一步处理return super.clone();}
}
原型管理器 (可选, PrototypeManager / ShapeCache)
有时会引入一个原型管理器类,用于存储和检索原型实例。客户端向管理器请求一个特定类型的原型,然后克隆它。
// shape_cache.go
package shapeimport "fmt"// ShapeCache 原型管理器
type ShapeCache struct {prototypes map[string]Shape
}func NewShapeCache() *ShapeCache {cache := &ShapeCache{prototypes: make(map[string]Shape)}cache.loadCache()return cache
}// loadCache 初始化原型实例并存储
func (sc *ShapeCache) loadCache() {circle := NewCircle("circle1", 10, 0, 0)rectangle := NewRectangle("rect1", 20, 10, 0, 0)sc.prototypes[circle.GetID()] = circlesc.prototypes[rectangle.GetID()] = rectanglefmt.Println("ShapeCache: Prototypes loaded.")
}// GetShape 克隆并返回指定ID的原型
func (sc *ShapeCache) GetShape(id string) (Shape, error) {prototype, found := sc.prototypes[id]if !found {return nil, fmt.Errorf("prototype with id '%s' not found", id)}return prototype.Clone(), nil
}// AddShape 允许运行时添加新的原型
func (sc *ShapeCache) AddShape(id string, shape Shape) {sc.prototypes[id] = shapefmt.Printf("ShapeCache: Prototype '%s' added.\n", id)
}
// ShapeCache.java
package com.example.shape;import java.util.Hashtable;// 原型管理器
public class ShapeCache {private static Hashtable<String, Shape> shapeMap = new Hashtable<>();public static Shape getShape(String shapeId) throws CloneNotSupportedException {Shape cachedShape = shapeMap.get(shapeId);if (cachedShape == null) {System.err.println("ShapeCache: Prototype with ID '" + shapeId + "' not found.");return null;}System.out.println("ShapeCache: Returning clone of prototype with ID: " + shapeId);return cachedShape.cloneShape(); // 调用我们自定义的克隆方法}// loadCache 会加载每种形状的实例,并将它们存储在 Hashtable 中public static void loadCache() {System.out.println("ShapeCache: Loading initial prototypes...");Circle circle = new Circle("circle-proto", 10, 0, 0);shapeMap.put(circle.getId(), circle);Rectangle rectangle = new Rectangle("rect-proto", 20, 10, 5, 5);shapeMap.put(rectangle.getId(), rectangle);System.out.println("ShapeCache: Prototypes loaded.");}// 允许运行时添加新的原型public static void addPrototype(String id, Shape shape) {shapeMap.put(id, shape);System.out.println("ShapeCache: Prototype '" + id + "' added.");}
}
客户端使用
// main.go (示例用法)
/*
package mainimport ("fmt""./shape"
)func main() {cache := shape.NewShapeCache()// 从缓存获取原型并克隆circle1, err := cache.GetShape("circle1")if err != nil {fmt.Println("Error:", err)return}circle1.Draw() // ID: circle1_clonerect1, err := cache.GetShape("rect1")if err != nil {fmt.Println("Error:", err)return}rect1.Draw() // ID: rect1_clone// 修改克隆体的属性circle1.SetID("myCustomCircle")// 如果是 Circle 类型,可以进行类型断言来访问特定属性if c, ok := circle1.(*shape.Circle); ok {c.Radius = 100c.X = 50}circle1.Draw() // ID: myCustomCircle, Radius: 100, Center: (50,0)// 原始原型不受影响originalCircle, _ := cache.GetShape("circle1") // 再次获取会重新克隆originalCircleProto := cache.prototypes["circle1"] // 直接访问原型 (不推荐直接修改原型)fmt.Println("--- Original prototype vs new clone from cache ---")originalCircleProto.Draw() // ID: circle1, Radius: 10originalCircle.Draw() // ID: circle1_clone, Radius: 10 (新克隆的)// 运行时添加新原型trianglePrototype := shape.NewTriangle("triangle-proto", 5, 10) // 假设有 Triangle 类型cache.AddShape(trianglePrototype.GetID(), trianglePrototype)clonedTriangle, _ := cache.GetShape("triangle-proto")if clonedTriangle != nil {clonedTriangle.Draw()}
}// 假设添加一个 Triangle 类型 (triangle.go)
/*
package shape
import "fmt"
type Triangle struct { ID string; Base, Height int }
func NewTriangle(id string, base, height int) *Triangle { return &Triangle{id, base, height} }
func (t *Triangle) SetID(id string) { t.ID = id }
func (t *Triangle) GetID() string { return t.ID }
func (t *Triangle) Clone() Shape { return &Triangle{t.ID + "_clone", t.Base, t.Height} }
func (t *Triangle) Draw() { fmt.Printf("Drawing Triangle [ID: %s, Base: %d, Height: %d]\n", t.ID, t.Base, t.Height) }
*/
// Main.java (示例用法)
/*
package com.example;import com.example.shape.Circle;
import com.example.shape.Rectangle;
import com.example.shape.Shape;
import com.example.shape.ShapeCache;public class Main {public static void main(String[] args) {ShapeCache.loadCache(); // 加载原型try {System.out.println("--- Cloning and using shapes ---");Shape clonedCircle1 = ShapeCache.getShape("circle-proto");if (clonedCircle1 != null) {clonedCircle1.draw(); // ID: circle-proto_clone}Shape clonedRectangle1 = ShapeCache.getShape("rect-proto");if (clonedRectangle1 != null) {clonedRectangle1.draw(); // ID: rect-proto_clone}System.out.println("\n--- Modifying a cloned shape ---");// 修改克隆体的属性if (clonedCircle1 != null) {clonedCircle1.setId("myCustomCircle");if (clonedCircle1 instanceof Circle) {Circle customCircle = (Circle) clonedCircle1;customCircle.setCenter(100, 100); // 修改 Point 对象}clonedCircle1.draw(); // ID: myCustomCircle, Center: (100,100)}System.out.println("\n--- Verifying original prototype is unchanged ---");// 原始原型不受影响 (因为我们实现了深拷贝 Point)Shape originalCircleProto = ShapeCache.shapeMap.get("circle-proto"); // 直接访问原型 (不推荐)if (originalCircleProto != null) {System.out.print("Original Prototype in Cache: ");originalCircleProto.draw(); // ID: circle-proto, Center: (0,0)}Shape newlyClonedCircle = ShapeCache.getShape("circle-proto");if (newlyClonedCircle != null) {System.out.print("Newly Cloned from Cache: ");newlyClonedCircle.draw(); // ID: circle-proto_clone, Center: (0,0)}// 演示如果 Point 是浅拷贝会发生什么// 如果 Circle.cloneShape() 中对 center 只是 clonedCircle.center = this.center;// 那么修改 customCircle.setCenter(100,100) 会同时修改 originalCircleProto 的 centerSystem.out.println("\n--- Adding a new prototype at runtime ---");Circle newProto = new Circle("circle-large-proto", 50, 10, 10);ShapeCache.addPrototype(newProto.getId(), newProto);Shape clonedLargeCircle = ShapeCache.getShape("circle-large-proto");if(clonedLargeCircle != null) {clonedLargeCircle.draw();}} catch (CloneNotSupportedException e) {e.printStackTrace();}}
}
*/
8. 总结
原型模式通过复制(克隆)现有对象来创建新对象,从而在特定场景下(如对象创建成本高、需要大量相似对象)提供了一种高效且灵活的对象创建方式。核心在于实现 clone()
方法,并正确处理深拷贝与浅拷贝的问题。当与原型管理器结合使用时,还可以实现运行时的动态产品配置。
记住它的核心:克隆现有对象,高效创建。
相关文章:

零基础设计模式——第二部分:创建型模式 - 原型模式
第二部分:创建型模式 - 5. 原型模式 (Prototype Pattern) 我们已经探讨了单例、工厂方法、抽象工厂和生成器模式。现在,我们来看创建型模式的最后一个主要成员——原型模式。这种模式关注的是通过复制现有对象来创建新对象,而不是通过传统的…...
Honeywell TK-PRS021 C200
Honeywell C200/C200E 是一款高性能的集成控制与安全系统(ICSS),采用紧凑型 A 系列机箱 设计,适用于工业自动化、过程控制和批处理管理。C200 控制器最初随 PlantScape R200 发布,而 C200E 则与 Experion PKS R400 兼容…...

java 进阶 1.0.3
Thread API说明 自己滚去看文档 CPU线程调度 每一个线程的优先使用权都是系统随机分配的,人人平等 谁先分配到就谁先用 也可以耍赖,就是赋予某一个线程拥有之高使用权:优先级 这样的操作就叫做线程调度 最基本的是系统轮流获得 java的做法是抢…...

从 Docker 到 runC
从 Docker 到 runC:容器底层原理详解 目录 1. Docker 与 runC 的关系 2. Docker 的核心组件 3. runC 的核心功能 4. 实战示例:从 Docker 到 runC 4.1 示例场景:运行一个简单容器 4.2 Docker 底层调用 runC 的流程 4.3 查看 runC 的调用 4.4 直接调用 runC 创建容器 …...

PET,Prompt Tuning,P Tuning,Lora,Qlora 大模型微调的简介
概览 到2025年,虽然PET(Pattern-Exploiting Training)和Prompt Tuning在学术界仍有探讨,但在工业和生产环境中它们已基本被LoRA/QLoRA等参数高效微调(PEFT)方法取代 。LoRA因其实现简单、推理零开销&#…...

02-jenkins学习之旅-基础配置
0 配置主路径 jenkins安装目录下找到jenkins.xml文件,C:\ProgramData\Jenkins\.jenkins目录下会存放jenkins相关的配置信息。 1 jdk配置 jenkins是java开发开源的项目,进而服务器需要jdk环境 1.1 服务器安装jdk 1.2 jenkins jdk配置 2 git配置 在je…...
互联网大厂Java求职面试:云原生架构与AI应用集成解决方案
互联网大厂Java求职面试:云原生架构与AI应用集成解决方案 场景一:短视频与直播平台的高并发架构设计 面试官提问 面试官(技术总监): 郑薪苦,你有处理过千万级用户同时在线的直播系统吗?如何设…...
Python爬虫实战:研究Crawley 框架相关技术
1. Crawley 框架相关定义 1.1 网络爬虫定义 网络爬虫是一种按照一定的规则,自动地抓取万维网信息的程序或者脚本。它通过 HTTP 协议与 Web 服务器进行交互,获取网页内容并进行解析处理,是数据采集和信息检索的重要工具。 1.2 Crawley 框架定义 Crawley 是一个基于 Pytho…...
C#实现List导出CSV:深入解析完整方案
C#实现List导出CSV:深入解析完整方案 在数据交互场景中,CSV文件凭借其跨平台兼容性和简洁性,成为数据交换的重要载体。本文将基于C#反射机制实现的通用CSV导出方案,结合实际开发中的痛点,从基础实现、深度优化到生产级…...

Appium+python自动化(三)- SDK Manager
简介 一开始打算用真机做的,所以在前边搭建环境时候就没有下载SDK,但是考虑到绝大多数人都没有真机,所以顺应民意整理一下模拟器。SDK顾名思义,Android SDK Manager就是一个Android软件开发工具包管理器,就像一个桥梁&…...

3D Gaussian Splatting for Real-Time Radiance Field Rendering——文章方法精解
SfM → Point-NeRF → 3D Gaussian Splatting 🟦SfM Structure-from-Motion(运动恢复结构,简称 SfM)是一种计算机视觉技术,可以: 利用多张从不同角度拍摄的图像,恢复出场景的三维结构和相机的…...
主成分分析基本概念及python代码使用
目录 1. 前言 2. 主成分分析的基本概念 3. PCA的适应场景 4. PCA算法的理论基础 4.1 标准化数据 4.2 计算协方差矩阵 4.3 求解特征值和特征向量 4.4 选择主成分 4.5 投影到新坐标系 5. 完整的PCA示例 5.1 使用手写数字数据集 5.2 可视化降维后的数据 6. PCA的优缺…...
MCP如何助力智能交通系统?从数据融合到精准决策
MCP如何助力智能交通系统?从数据融合到精准决策 近年来,智能交通系统(ITS)正在全球范围内快速发展,它结合人工智能(AI)、物联网(IoT)和数据分析,致力于提高交通效率、减少拥堵、增强安全性。而MCP(Multi-Constraint Pathfinding,多约束路径寻优)技术作为一种复杂…...
什么是抽象类?是所有函数都是纯虚函数吗?
什么是抽象类? 抽象类(Abstract Class)是一种特殊的类,它不能被直接实例化,但可以作为基类被其他类继承。抽象类的主要用途是定义一组接口规范,这些规范由派生类实现。 在C中,抽象类是通过包含…...
计算机视觉与深度学习 | Python实现ARIMA-WOA-CNN-LSTM时间序列预测(完整源码和数据
以下是一个结合ARIMA、鲸鱼优化算法(WOA)、CNN和LSTM进行时间序列预测的Python实现框架。由于完整代码和数据量较大,此处提供核心代码结构和示例数据集,您可根据需求扩展。 1. 数据准备(示例数据) 使用airline-passengers.csv(航空乘客数据集): import pandas as pd…...

【Unity实战笔记】第二十四 · 使用 SMB+Animator 实现基础战斗系统
转载请注明出处:🔗https://blog.csdn.net/weixin_44013533/article/details/146409453 作者:CSDN|Ringleader| 1 结构 1.1 状态机 1.2 SMB 2 代码实现 2.1 核心控制 Player_Base_SMB 继承 StateMachineBehaviour ,控制变量初始…...
C/C++的OpenCV 进行图像梯度提取
使用 C/OpenCV 进行图像梯度提取 图像梯度表示图像中像素强度的变化率和方向。它是图像分析中的一个基本概念,广泛应用于边缘检测、特征提取和物体识别等任务。OpenCV 提供了多种计算图像梯度的函数。本文将介绍几种常用的梯度算子及其在 C/OpenCV 中的实现。 预备…...
Redis 缓存使用的BigKey问题
一、什么是 BigKey? BigKey 指在 Redis 中存储的 单个 Key 对应的 Value 过大,通常表现为: String 类型:Value 长度 > 10KB。Hash/List/Set/ZSet:元素数量 > 5,000 或总大小 > 10MB。 二、BigKey 的危害 问…...

【Java高阶面经:消息队列篇】22、消息队列核心应用:高并发场景下的解耦、异步与削峰
一、消息队列:分布式系统的核心枢纽 在分布式架构日益普及的今天,消息队列(Message Queue, MQ)已成为解决系统复杂性的核心组件。它通过异步通信、系统解耦和流量控制等能力,有效应对高并发场景下的数据流动挑战。 1.1 核心特性:异步、解耦与弹性 1.1.1 异步通信:释放…...

软媒魔方——一款集合多种系统辅助组件的软件
停更4年,但依旧吊炸天! 亲们,是不是觉得电脑用久了就像老牛拉车,慢得让人着急?别急,我今天要给大家安利一个超好用的电脑优化神器——软媒魔方! 软件介绍 首先,这货真心是免费的&a…...
Unity场景的加载与卸载
Unity场景的加载与卸载 使用方法:把SceneLoader 脚本代码挂在场景中 使用示例: SceneLoader.Instance.LoadAdditiveScene(8);//通过场景索引加载SceneLoader.Instance.UnloadScene("ShiWaiScene");//通过场景名字卸载脚本代码如下࿱…...

多路径可靠传输协议(比如 MPTCP)为什么低效
可靠就不能多路径,多路径求可靠必然要多费劲。这不难理解,多路径必异步,这无疑增加了可靠性判断的难度。 前文 多路径传输(比如 MPTCP)对性能的意义 阐述了作为单连接的多子流 MPTCP 对传输性能的意义是无意义,本文接着阐述作为隧…...

塔能高温冰蓄冷技术:工厂能耗精准节能的创新之路
在工厂的能耗构成中,制冷系统是重要的耗能环节。传统的水蓄冷和冰蓄冷技术在实际应用中存在一些局限性,难以满足工厂对节能和成本控制的更高要求。塔能科技的高温冰蓄冷技术,凭借其独特的优势,为工厂能耗精准节能提供了创新的解决…...

内存优化笔记1
欢迎关注更多精彩 关注我,学习常用算法与数据结构,一题多解,降维打击。 问题提出 在很多工业软件中,需要对对象进行微分细化,这样会产生很多(几百万到几千万)对象。随着业务的发展,…...

人脸识别,使用 deepface + api + flask, 改写 + 调试
1. 起因, 目的, 感受: github deepface 这个项目写的很好, 继续研究使用这个项目,改写 api。增加一个前端 flask app 2. 先看效果 3. 过程: 大力改写原始项目中 api 这部分的代码, 原始项目的文件结构太繁杂了: 我把…...

代码管理平台Gitlab如何通过快解析实现远程访问?
一、Gitlab功能介绍 Gitlab是被广泛使用的基于git的开源代码管理平台,用于管理、存储开发人员代码,同时可以协同开发 二、外网试用Gitlab遇到的问题 运维人员将Gitlab服务器部署在总部机房,而分公司开发人员和出差运维人员就无法访问Gitlab…...

基于SpringBoot+Vue的足球青训俱乐部管理后台系统的设计与开发
项目背景与概述 随着足球青训行业的快速发展,如何高效、规范地管理学员、教练以及课程等日常工作,成为了青训俱乐部运营的重要课题。为了提升俱乐部的管理效率与用户体验,基于 Spring Boot 和 Vue.js 开发了一个 足球青训俱乐部管理后台系统…...
Redis 是否适合像 MySQL 一样当数据库使用?
Redis 可以在特定场景下作为数据库使用,但与 MySQL 等关系型数据库还是有很大的差异。Redis 确实有持久化功能,开启 AOF 并把 appendfsync 设置为 always 后,它会把每一次数据操作都立刻记录到文件里,相当于每发生一件事就马上记下…...
AI是否会取代人类?浔川问答①
提问者:浔川社团官方联合会 回答者:deepseek 关于AI是否会取代人类的问题,目前科技界和社会学界的主流观点认为:AI会在许多领域显著改变人类的工作和生活方式,但“完全取代人类”的可能性极低。更可能的是人机协作的深…...
JDBC-java操作数据库
1.基本结构: package com.atguigu.servlets;import java.sql.Connection; import java.sql.DriverManager; import java.sql.Statement;public class JDBCemo {public static void main(String[] args) throws Exception{String url "jdbc:mysql:///mysql&qu…...