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

零基础设计模式——第二部分:创建型模式 - 原型模式

第二部分:创建型模式 - 5. 原型模式 (Prototype Pattern)

我们已经探讨了单例、工厂方法、抽象工厂和生成器模式。现在,我们来看创建型模式的最后一个主要成员——原型模式。这种模式关注的是通过复制现有对象来创建新对象,而不是通过传统的构造函数实例化。

  • 核心思想:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。

原型模式 (Prototype Pattern)

“用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。”

想象一下细胞分裂:一个细胞(原型)可以通过分裂(克隆)产生一个新的、与自身几乎完全相同的细胞。或者,在绘图软件中,你画了一个复杂的图形(原型),然后可以通过“复制”和“粘贴”操作快速创建多个相同的图形副本,再对副本进行微调。

原型模式的核心就是“克隆”或“复制”。当创建一个对象的成本很高(例如,需要复杂的计算、数据库查询或网络请求)时,如果已经有一个相似的对象存在,通过复制这个现有对象来创建新对象可能会更高效。

1. 目的 (Intent)

原型模式的主要目的:

  1. 提高性能:当创建新对象的成本较大时(如初始化时间长、资源消耗多),通过复制已有的原型实例来创建新对象,可以避免重复执行这些昂贵的初始化操作。
  2. 简化对象创建:如果一个对象的创建过程比较复杂,或者需要依赖某些运行时的状态,通过克隆一个已配置好的原型对象可以简化新对象的创建。
  3. 动态添加或删除产品:可以在运行时通过注册原型实例来动态地增加或删除系统中可用的产品类型,而无需修改工厂类(如果与工厂模式结合使用)。
  4. 避免与产品具体类耦合:客户端可以只知道抽象的原型接口,通过克隆来获取新对象,而无需知道具体的实现类名。

2. 生活中的例子 (Real-world Analogy)

  • 复印文件

    • 原型 (Prototype):原始文件(比如一份合同模板)。
    • 克隆操作 (Clone):复印机复印的过程。
    • 新对象 (Cloned Object):复印出来的文件副本。
      你不需要重新打字排版来得到一份新的合同,只需要复印原件,然后在副本上修改少量信息(如签约方、日期)即可。
  • 生物克隆:如克隆羊多莉。多莉就是通过复制现有羊的细胞(原型)而创建的。

  • 制作模具和铸件

    • 原型:一个精心制作的模具。
    • 克隆操作:使用模具进行浇筑。
    • 新对象:通过模具生产出来的多个相同铸件。
  • 游戏中的敌人复制:在一个游戏中,当需要生成大量相同类型的敌人时,可以先创建一个敌人对象作为原型,并设置好其初始属性(如生命值、攻击力、模型等)。之后需要新的敌人时,直接克隆这个原型,而不是每次都从头加载资源和设置属性。

3. 结构 (Structure)

原型模式的结构相对简单,通常包含以下角色:

  1. Prototype (抽象原型):声明一个克隆自身的接口(通常是一个 clone() 方法)。
  2. ConcretePrototype (具体原型):实现 Prototype 接口,重写 clone() 方法来复制自身。这个类是实际被复制的对象。
  3. Client (客户端):让一个原型克隆自身从而创建一个新的对象。客户端不需要知道具体的原型类名,只需要通过抽象原型接口来操作。
    在这里插入图片描述
    克隆过程
  4. 客户端持有一个抽象原型对象的引用。
  5. 当客户端需要一个新的对象时,它调用原型对象的 clone() 方法。
  6. 具体原型类实现 clone() 方法,创建一个当前对象的副本并返回。
  7. 客户端得到一个新的对象,这个新对象与原型对象具有相同的初始状态(属性值)。

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)

优点:

  1. 性能提升:对于创建成本高的对象,克隆比重新创建更快。
  2. 简化对象创建:可以复制一个已经初始化好的复杂对象,避免重复的初始化逻辑。
  3. 灵活性高:可以在运行时动态地获取和复制原型对象。
  4. 对客户端隐藏具体类型:客户端只需要知道抽象原型接口即可创建对象。

缺点:

  1. 需要为每个类实现克隆方法:每个需要作为原型的类都必须实现 clone() 方法,这可能比较繁琐,特别是当类层次结构很深或包含许多字段时。
  2. 深拷贝实现复杂:正确实现深拷贝可能比较复杂,需要仔细处理所有引用类型的成员,以避免意外共享或循环引用问题。
  3. 可能违反开闭原则(如果克隆逻辑复杂):如果克隆逻辑非常复杂且依赖于具体类的内部结构,当具体类修改时,克隆方法可能也需要修改。

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() 方法,并正确处理深拷贝与浅拷贝的问题。当与原型管理器结合使用时,还可以实现运行时的动态产品配置。

记住它的核心:克隆现有对象,高效创建

相关文章:

零基础设计模式——第二部分:创建型模式 - 原型模式

第二部分&#xff1a;创建型模式 - 5. 原型模式 (Prototype Pattern) 我们已经探讨了单例、工厂方法、抽象工厂和生成器模式。现在&#xff0c;我们来看创建型模式的最后一个主要成员——原型模式。这种模式关注的是通过复制现有对象来创建新对象&#xff0c;而不是通过传统的…...

完整改进RIME算法,基于修正多项式微分学习算子Rime-ice增长优化器,完整MATLAB代码获取

1 简介 为了有效地利用雾状冰生长的物理现象&#xff0c;最近开发了一种优化算法——雾状优化算法&#xff08;RIME&#xff09;。它模拟硬雾状和软雾状过程&#xff0c;构建硬雾状穿刺和软雾状搜索机制。在本研究中&#xff0c;引入了一种增强版本&#xff0c;称为修改的RIME…...

【1——Android端添加隐私协议(unity)1/3】

前言&#xff1a;这篇仅对于unity 发布Android端上架国内应用商店添加隐私协议&#xff0c;隐私协议是很重要的东西&#xff0c;没有这个东西&#xff0c;是不上了应用商店的。 对于仅仅添加隐私协议&#xff0c;我知道有三种方式,第一种和第二种基本一样 1.直接在unity里面新…...

笔记本6GB本地可跑的图生视频项目(FramePack)

文章目录 &#xff08;一&#xff09;简介&#xff08;二&#xff09;本地执行&#xff08;2.1&#xff09;下载&#xff08;2.2&#xff09;更新&#xff08;2.3&#xff09;运行&#xff08;2.4&#xff09;生成 &#xff08;三&#xff09;注意&#xff08;3.1&#xff09;效…...

Android View的事件分发机制

ViewGroup的事件分发逻辑 从Activity传递给Window&#xff0c;再传递给ViewGroup&#xff0c;ViewGroup的dispatchTouchEvent()会被调用&#xff0c;如果onInterceptTouchEvent()返回true 转交自身onTouchEvent()处理,如果返回false继续向子View传递,子View的dispatchTouchEve…...

Python字符串格式化(二): f-string的进化

文章目录 一、Python 3.6&#xff1a;重新发明字符串格式化&#xff08;2016&#xff09;1. 语法糖的诞生&#xff1a;表达式直嵌技术2. 性能与可读性的双重提升3. 奠定现代格式化的基础架构 二、Python 3.7&#xff1a;解锁异步编程新场景&#xff08;2018&#xff09;1. 异步…...

力扣HOT100之二叉树:124. 二叉树中的最大路径和

这道题是困难题&#xff0c;靠自己想还是挺难想的&#xff0c;还是去看的灵神的题解&#xff0c;感觉还是要多复习一下这道题。这道题的思路和之前做的543. 二叉树的直径很像&#xff0c;可以参考之前的这篇博客。这里我们还是用递归来做&#xff0c;定义一个lambda函数来实现递…...

【C++】位图+布隆过滤器

1.位图 概念 所谓位图&#xff0c;就是用每一位来存放某种状态&#xff0c;适用于海量数据&#xff0c;数据无重复的场景。通常是用来判断某个数据存不存在的或是否被标记。 1.二进制位表示 &#xff1a; 位图中的每一位&#xff08;bit&#xff09;代表一个元素的状态。通常&…...

Google Agent Development Kit与MCP初试

Google Agent Development Kit与MCP初试 一、背景知识二、搭建智能大脑 - Ollama服务器2.1 为什么要先搭建Ollama&#xff1f;2.2 搭建ollama服务器2.2.1 安装2.2.2 试着用curl命令"问"AI一个问题&#xff1a; 三、构建智能体工坊 - ADK环境3.1 创建容器3.2 安装核心…...

云原生+大数据

虚拟化&#xff1a; 虚拟化&#xff0c;是指通过虚拟化技术将一台计算机虚拟为多台逻辑计算机。在一台计算机上同时运行多个逻辑计算机&#xff0c;每个逻辑计算机可运行不同的操作系统&#xff0c;并且应用程序都可以在相互独立的空间内运行而互不影响&#xff0c;从而显著提…...

基于cornerstone3D的dicom影像浏览器 第二十一章 显示DICOM TAGS

系列文章目录 第一章 下载源码 运行cornerstone3D example 第二章 修改示例crosshairs的图像源 第三章 vitevue3cornerstonejs项目创建 第四章 加载本地文件夹中的dicom文件并归档 第五章 dicom文件生成png&#xff0c;显示检查栏&#xff0c;序列栏 第六章 stack viewport 显…...

【记录】PPT|PPT打开开发工具并支持Quicker VBA运行

文章目录 打开开发者工具支持Quicker VBA运行 打开开发者工具 参考文章&#xff0c;微软文档&#xff1a;显示“开发工具”选项卡&#xff0c;以下直接复制&#xff0c;如侵私删。 适用对象&#xff1a;Microsoft 365 专属 Excel Microsoft 365 专属 Outlook Microsoft 365 专属…...

西门子 S1500 博途软件舞台威亚 3D 控制方案

西门子 S1500 PLC 是工业自动化领域的主流控制器&#xff0c;适合高精度、高可靠性的舞台威亚控制。下面为你提供基于博途 (TIA Portal) 软件的 3D 控制方案设计。 系统架构设计 舞台威亚 3D 控制系统通常包含以下组件&#xff1a; 硬件层&#xff1a; S1500 PLC 主机伺服驱动…...

第三十二天打卡

import pandas as pd from sklearn.datasets import load_iris from sklearn.model_selection import train_test_split from sklearn.ensemble import RandomForestClassifier # 加载鸢尾花数据集 iris load_iris() df pd.DataFrame(iris.data, columnsiris.feature_names) …...

同步/异步电路;同步/异步复位

同步/异步电路&#xff1b;同步/异步复位 在 FPGA 设计中&#xff0c;同步电路、异步电路、同步复位和异步复位是基础且关键的概念&#xff0c;它们的特性直接影响电路的可靠性、时序性能和设计复杂度。 一、同步电路&#xff08;Synchronous Circuit&#xff09; 定义 同步电…...

spring boot 实现resp视频推流

1、搭建resp服务&#xff08;docker方式&#xff09; docker pull aler9/rtsp-simple-serverdocker run -d --restartalways \--name rtsp-server \-p 8554:8554 \aler9/rtsp-simple-server2、maven依赖 <dependency><groupId>org.bytedeco</groupId><a…...

python、R、shell兼容1

一&#xff0c;兼容方式 1&#xff0c;shell中用R、python&#xff1a; &#xff08;1&#xff09;python3、R/r&#xff08;radian&#xff09;进入 &#xff08;2&#xff09;脚本封装&#xff1a;命令行或者封装到sh脚本中 python xxx.py 自定义参数 Rscript xxx.r 自…...

Oracle 11G RAC重启系统异常

vmware安装centos7环境部署Oracle RAC (11.2.0.4) 部署时所有资源情况都是正常的&#xff0c;关机重启虚拟机后集群资源状态异常&#xff0c;请教CSDN大佬 – 部署规划 域名地址备注rac16192.168.31.16rac17192.168.31.17rac16vip192.168.31.26viprac17vip192.168.31.27vip…...

便捷的电脑自动关机辅助工具

软件介绍 本文介绍的软件是一款电脑上实用的倒计时和关机助手。 软件特性 这款关机助手十分贴心&#xff0c;它是一款无需安装的小软件&#xff0c;体积仅60KB&#xff0c;不用担心占用电脑空间&#xff0c;打开即可直接使用。 操作方法 你只需设置好对应的关机时间&#x…...

巧用 FFmpeg 命令行合并多个视频为一个视频文件教程

你是否曾经遇到过需要将多个视频片段合并成一个连续视频的情况&#xff1f;比如&#xff0c;你拍摄了一段旅行的精彩瞬间&#xff0c;想把它们合成一部短片&#xff1b;或者你在制作教学视频时&#xff0c;希望将不同的部分整合在一起。这时候&#xff0c;FFmpeg 就是你的得力助…...

平时使用电脑,如何去维护

在这个数字化的时代&#xff0c;电脑已经成为我们生活和工作中不可或缺的一部分。然而&#xff0c;你是否知道如何正确地维护它&#xff0c;让它始终保持良好的运行状态呢&#xff1f;今天&#xff0c;就让我来为大家揭晓这个谜底。定期清理电脑内部和外部的灰尘是至关重要的。…...

(视觉)分类、检测与分割在不同网络中的设计体现

分类、检测与分割在不同网络中的设计体现 概述 在计算机视觉领域&#xff0c;不同的网络结构在功能和结构上差异显著&#xff0c;同时也共享一些基础设计元素。 卷积神经网络是基石&#xff1a; 卷积层通过特定的卷积核与图像进行卷积运算提取图像中的局部特征&#xff0c;比…...

技术分享 | MySQL大事务导致数据库卡顿

本文为墨天轮数据库管理服务团队第66期技术分享&#xff0c;内容原创&#xff0c;作者为技术顾问孙文龙&#xff0c;如需转载请联系小墨&#xff08;VX&#xff1a;modb666&#xff09;并注明来源。 一、现 象 业务侧反馈连接数据库异常&#xff0c;报错 connection is not av…...

C#在 .NET 9.0 中启用二进制序列化:配置、风险与替代方案

在 .NET 9.0 中启用二进制序列化&#xff1a;配置、风险与替代方案 引言一、启用二进制序列化的步骤二、实现序列化与反序列化三、安全风险与缓解措施四、推荐替代方案五、总结 引言 在 .NET 生态中&#xff0c;二进制序列化&#xff08;Binary Serialization&#xff09;曾是…...

每日Prompt:像素风格插画

提示词 像素风格插画&#xff0c;日式漫画脸&#xff0c;画面主体为一位站在路边的男孩&#xff0c;人物穿着黑色冲锋衣&#xff0c;手里拿着手机&#xff0c;男孩靠坐在机车旁边&#xff0c;脚边依偎着一只带着小摩托车头盔的小小猫&#xff0c;背景是雨中&#xff0c;身旁停…...

Rust 学习笔记:生命周期

Rust 学习笔记&#xff1a;生命周期 Rust 学习笔记&#xff1a;生命周期使用生命周期防止悬空引用借用检查器函数中的泛型生命周期生命周期注释语法函数签名中的生命周期注解从生命周期的角度思考结构定义中的生命周期注解省略生命周期方法定义中的生命周期注释静态生命周期泛型…...

科学标注法:数据治理的未来之路

在数据治理领域,科学标注法是一种系统化、标准化的数据标注方法论,其核心是通过规范化的流程、技术工具和质量控制机制,将原始数据转化为具有语义和结构特征的可用数据资源。以下从定义、技术特征、应用场景、与传统标注方法的区别以及遵循的标准框架等方面展开详细解析: 一…...

小白刷题 之 如何高效计算二进制数组中最大连续 1 的个数

前言 学习如何快速找出二进制数组中最长的连续 1 序列。 这个问题在数据处理、网络传输和算法面试中经常出现&#xff0c;掌握它不仅能提升编程能力&#xff0c;还能加深对数组操作和循环控制的理解。 &#x1f31f; 问题背景 想象你是一位网络工程师&#xff0c;正在分析服…...

中科方德鸳鸯火锅平台使用教程:轻松运行Windows应用!

原文链接&#xff1a;中科方德鸳鸯火锅平台使用教程&#xff1a;轻松运行Windows应用&#xff01; Hello&#xff0c;大家好啊&#xff0c;今天给大家带来一篇中科方德鸳鸯火锅平台使用的文章&#xff0c;欢迎大家分享点赞&#xff0c;点个在看和关注吧&#xff01;在信创环境…...

完全禁用 Actuator 功能

问题描述&#xff1a; springboot 关闭Actuator无效&#xff0c;原本设置 management:endpoints:enabled-by-default: false # 禁用所有端点屏蔽了/actuator/info和/actuator/health&#xff0c;但/actuator还可以访问。 拉满配置如下&#xff0c;成功屏蔽 # application.y…...

Netty学习专栏(二):Netty快速入门及重要组件详解(EventLoop、Channel、ChannelPipeline)

文章目录 前言一、快速入门&#xff1a;5分钟搭建Echo服务器二、核心组件深度解析2.1 EventLoop&#xff1a;颠覆性的线程模型EventLoop 设计原理核心 API 详解代码实践&#xff1a;完整使用示例 2.2 Channel&#xff1a;统一的网络抽象层Channel 核心架构核心 API 详解代码实践…...

27-FreeRTOS的任务管理

一、FreeRTOS的任务概念 在FreeRTOS中&#xff0c;任务&#xff08;Task&#xff09;是操作系统调度的基本单位。每个任务都是一个无限循环的函数&#xff0c;它执行特定的功能。任务可以被看作是一个轻量级的线程&#xff0c;具有自己的堆栈和优先级。下面是如何定义一个任务函…...

upload-labs靶场通关详解:第14关

一、分析源代码 这一关的任务说明已经相当于给出了答案&#xff0c;就是让我们上传一个图片木马&#xff0c;可以理解为图片中包含了一段木马代码。 function getReailFileType($filename){$file fopen($filename, "rb");$bin fread($file, 2); //只读2字节fclose…...

supervisor的进程监控+prometheus+alertmanager实现告警

supervisor服务进程监控实现告警 前提&#xff1a;部署了prometheus(配置了rules文件夹),alertmanager,webhook,python3环境 [roottest supervisor_prometheus]# pwd /opt/supervisor_prometheus [roottest supervisor_prometheus]# ls supervisor_exporter.py supervisor_int…...

HarmonyOS 鸿蒙应用开发基础:父组件调用子组件方法的几种实现方案对比

在ArkUI声明式UI框架中&#xff0c;父组件无法直接调用子组件的方法。本文介绍几种优雅的解决方案&#xff0c;并作出对比分析&#xff0c;分析其适用于不同场景和版本需求。帮助开发者在开发中合理的选择和使用。 方案一&#xff1a;Watch装饰器&#xff08;V1版本适用&#x…...

Enhancing Relation Extractionvia Supervised Rationale Verifcation and Feedback

Enhancing Relation Extraction via Supervised Rationale Verification and Feedback| Proceedings of the AAAI Conference on Artificial Intelligencehttps://ojs.aaai.org/index.php/AAAI/article/view/34631 1. 概述 关系抽取(RE)任务旨在抽取文本中实体之间的语义关...

等离子体隐身技术和小型等离子体防御装置设计

相信大家前不久都看到了关于国防科大团队关于等离子体防御的相关文章&#xff0c;恰好也在做相关的研究&#xff0c;所以想向对这个问题感兴趣的朋友聊一聊这里面的一些基本原理和研究现状。 等离子体与电磁波的相互作用 等离子体会对电磁波产生吸收和反射作用&#xff0c;通常…...

PCB设计教程【入门篇】——电路分析基础-电路定理

前言 本教程基于B站Expert电子实验室的PCB设计教学的整理&#xff0c;为个人学习记录&#xff0c;旨在帮助PCB设计新手入门。所有内容仅作学习交流使用&#xff0c;无任何商业目的。若涉及侵权&#xff0c;请随时联系&#xff0c;将会立即处理 一、电路基本概念 连接线与节点 …...

C++-继承

1.继承的概念及定义 1.1继承的概念 继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要的手段&#xff0c;它允许程序员在保持原有类特性的基础上进行扩展&#xff0c;增加功能&#xff0c;这样产生新的类&#xff0c;称派生类。继承呈现了面向对象 程序设计的层…...

25.5.22学习总结

ST表&#xff08;Sparse Table&#xff0c;稀疏表&#xff09;是一种用于高效解决静态区间最值查询&#xff08;RMQ&#xff09;问题的数据结构。其核心思想是通过预处理每个长度为2^j的区间的最值&#xff0c;使得查询时只需合并两个子区间的最值即可得到结果&#xff0c;从而…...

接口自动化测试框架(pytest+allure+aiohttp+ 用例自动生成)

&#x1f345; 点击文末小卡片&#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 近期准备优先做接口测试的覆盖&#xff0c;为此需要开发一个测试框架&#xff0c;经过思考&#xff0c;这次依然想做点儿不一样的东西。 接口测试是比较讲究效率的…...

FastAPI在 Nginx 和 Docker 环境中的部署

目录 实现示例1. 项目结构2. FastAPI 应用 (app/main.py)3. 依赖文件 (app/requirements.txt)4. Dockerfile5. Nginx 配置 (nginx/nginx.conf)6. Docker Compose 配置 (docker-compose.yml) 使用方法修改代码后更新 实现示例 接下来创建一个简单的示例项目&#xff0c;展示如何…...

08 接口自动化-用例管理框架pytest之fixtrue,conftest.py,allure报告以及logo定制

文章目录 一、使用fixture实现部分前后置1.function级别:在每个函数的前后执行2.class级别&#xff1a;在每个类的前后执行一次3.module级别&#xff1a;在每个模块的前后执行一次4.package、session级别&#xff0c;一般是和connftest.py文件一起使用 二、当fixture的级别为pa…...

Appium+python自动化(二)- 环境搭建—下

简介 我这里已经将android的测试开发环境已经搭建准备完毕。上一篇android测试开发环境已经准备好&#xff0c; 那么接下来就是appium的环境安装和搭建了。 搭建环境安装过程中切勿浮躁&#xff0c;静下心来一个一个慢慢地按照步骤一个个来。 环境装好后&#xff0c;可以用真机…...

浅谈测试驱动开发TDD

目录 1.什么是TDD 2.TDD步骤 3.TDD 的核心原则 4.TDD 与传统开发的对比 5.TDD中的单元测试和集成测试区别 6.总结 1.什么是TDD 测试驱动开发&#xff08;Test-Driven Development&#xff0c;简称 TDD&#xff09; 是一种软件开发方法论&#xff0c;核心思想是 “先写测试…...

MVC和MVVM架构的区别

MVC和MVVM都是前端开发中常用的设计模式&#xff0c;都是为了解决前端开发中的复杂性而设计的&#xff0c;而MVVM模式则是一种基于MVC模式的新模式。 MVC(Model-View-Controller)的三个核心部分&#xff1a;模型、视图、控制器相较于MVVM(Model-View-ViewModel)的三个核心部分…...

网络安全-等级保护(等保) 3-1-1 GB/T 28448-2019 附录A (资料性附录)测评力度附录C(规范性附录)测评单元编号说明

附录A (资料性附录)测评力度 A.1 概述 测评力度是在等级测评过程中实施测评工作的力度&#xff0c;体现为测评工作的实际投入程度&#xff0c;具体由测评的广度和深度来反映。测评广度越大&#xff0c;测评实施的范围越大&#xff0c;测评实施包含的测评对象就越多。测评深度…...

MySQL 可观测性最佳实践

MySQL 简介 MySQL 是一个广泛使用的开源关系型数据库管理系统&#xff08;RDBMS&#xff09;&#xff0c;以其高性能、可靠性和易用性而闻名&#xff0c;适用于各种规模的应用&#xff0c;从小型网站到大型企业级系统。 监控 MySQL 指标是维护数据库健康、优化性能和确保数据…...

深入解析Spring Boot与Redis集成:高效缓存与性能优化

深入解析Spring Boot与Redis集成&#xff1a;高效缓存与性能优化 引言 在现代Web应用中&#xff0c;缓存技术是提升系统性能的重要手段之一。Redis作为一种高性能的内存数据库&#xff0c;广泛应用于缓存、会话管理和消息队列等场景。本文将详细介绍如何在Spring Boot项目中集…...

《C 语言字符串操作从入门到实战(下篇):strncpy/strncat/strstr 等函数原理与实现》

目录 七. strncpy函数的使用与模拟实现 7.1 strncpy函数理解 7.2 strncpy函数使用示例 7.3 strncpy函数模拟实现 八. strncat函数的使用与模拟实现 8.1 strncat函数理解 8.2 strncat函数使用示例 8.3 strncat函数模拟实现 九. strncmp函数的使用 9.1 strncmp函数理…...