解锁原型模式:Java 中的高效对象创建之道
系列文章目录
后续补充~~~
文章目录
- 一、引言
- 1.1 软件开发中的对象创建困境
- 1.2 原型模式的登场
- 二、原型模式的核心概念
- 2.1 定义与概念
- 2.2 工作原理剖析
- 2.3 与其他创建型模式的差异
- 三、原型模式的结构与角色
- 3.1 抽象原型角色
- 3.2 具体原型角色
- 3.3 客户端角色
- 3.4 原型管理器角色
- 四、Java 中的原型模式实现
- 4.1 实现步骤
- 4.2 浅拷贝实现与示例
- 4.3 深拷贝实现与示例
- 五、原型模式的实际应用场景
- 5.1 游戏开发中的应用
- 5.2 文档编辑器中的应用
- 5.3 数据库记录缓存中的应用
- 5.4 网络连接池中的应用
- 六、原型模式的优缺点
- 6.1 优点
- 6.2 缺点
- 七、原型模式与其他模式的结合
- 7.1 与工厂模式结合
- 7.2 与装饰器模式结合
- 八、使用原型模式的注意事项
- 8.1 深浅拷贝的选择
- 8.2 克隆方法的正确实现
- 九、总结与展望
- 9.1 原型模式的回顾
- 9.2 未来应用的展望
一、引言
1.1 软件开发中的对象创建困境
在软件开发的广袤领域中,对象创建是一个基础且关键的环节,但同时也面临着诸多挑战。随着软件系统规模的不断扩大和复杂度的日益提升,对象的创建过程常常变得复杂繁琐。
从初始化的角度来看,许多对象在创建时需要进行大量的初始化操作。以一个电商系统中的商品对象为例,它可能需要初始化商品的基本信息,如名称、价格、库存数量、描述等,这些信息可能来自不同的数据源,甚至需要进行复杂的计算和转换。不仅如此,商品对象可能还关联着其他对象,如所属的类目对象、供应商对象等,在创建商品对象时,需要正确地建立这些关联关系,确保数据的完整性和一致性。如果是一个复杂的订单对象,其初始化过程可能涉及到多个商品对象、用户信息、配送地址、支付方式等诸多因素,每个因素都需要进行细致的处理和配置。
在性能方面,对象创建也可能带来瓶颈。当系统需要频繁创建大量相似的对象时,传统的通过构造函数创建对象的方式会消耗大量的时间和资源。比如在一个游戏开发场景中,游戏中可能存在大量的怪物对象,每个怪物对象都具有相似的属性和行为,如生命值、攻击力、防御力、移动速度等。如果每次创建怪物对象都通过构造函数进行全新的初始化,那么在游戏运行过程中,随着怪物数量的增加,创建对象的开销会逐渐增大,导致游戏的性能下降,出现卡顿、掉帧等现象,严重影响用户体验。在一些大数据处理场景中,需要处理海量的数据记录,将每条数据记录映射为一个对象,如果采用常规的对象创建方式,系统的内存和 CPU 资源会被迅速耗尽,导致系统崩溃。
1.2 原型模式的登场
原型模式作为一种巧妙的解决方案,应运而生,为软件开发中的对象创建困境提供了新的思路。其核心思想简洁而有力:通过复制现有对象来创建新对象,而不是每次都通过构造函数从头开始创建。这就好比在生产线上,有一个已经制作好的产品原型,当需要生产更多相同或相似的产品时,不需要重新设计和制作每个产品,只需要根据这个原型进行复制,然后再对复制出来的产品进行一些个性化的调整即可。
在实际应用中,原型模式展现出了独特的优势。回到前面电商系统的例子,当需要创建多个相似的商品对象时,可以先创建一个商品原型对象,设置好其通用的属性和关联关系,然后通过复制这个原型对象来快速创建新的商品对象。对于一些具有固定属性组合的商品系列,如某品牌的不同型号手机,它们具有相同的基本配置(如屏幕尺寸、处理器型号等),只是在某些细节上有所差异(如内存容量、颜色等),利用原型模式,只需创建一个该品牌手机的原型对象,然后通过复制并修改相应的细节属性,就能轻松创建出不同型号的手机对象,大大提高了开发效率,减少了初始化的工作量和出错的可能性。
在游戏开发中,对于那些具有相似属性和行为的怪物对象,同样可以利用原型模式。先创建一个怪物原型对象,定义好其基本的属性和行为逻辑,然后在游戏运行过程中,根据需要通过复制这个原型对象来快速创建新的怪物对象。这样,不仅可以减少对象创建的时间和资源消耗,还能使游戏的性能更加稳定,为玩家提供更加流畅的游戏体验。
通过以上对软件开发中对象创建困境的分析以及原型模式的介绍,我们可以看到原型模式在解决对象创建问题上具有显著的潜力。接下来,我们将深入探讨原型模式的原理、结构以及在 Java 中的实现方式,进一步领略其在软件开发中的魅力和价值。
二、原型模式的核心概念
2.1 定义与概念
原型模式是一种创建型设计模式,其核心思想是利用已有的原型对象来创建新对象,而无需了解具体的创建过程。简单来说,就是通过复制一个现有的对象来生成新的对象,这些新对象在创建时会继承原型对象的属性和状态 ,就像在模具制造中,有一个已经制作好的模具原型,当需要生产多个相同的模具时,只需要以这个原型模具为基础进行复制,就能快速得到新的模具,而不需要重新设计和制作每个模具。在软件开发中,这意味着当我们需要创建一个新对象时,如果这个对象与已有的某个对象具有相似的属性和行为,我们就可以直接复制这个已有的对象(即原型对象),然后根据需要对复制出来的新对象进行一些个性化的修改,从而避免了从头开始创建对象的繁琐过程。
2.2 工作原理剖析
原型模式的工作原理基于对象的复制机制。当我们需要创建一个新对象时,不是通过传统的调用构造函数来初始化一个全新的对象,而是找到一个合适的原型对象,然后调用该原型对象的克隆方法来创建一个与原型对象具有相同属性值的新对象。这就好比复印文件,我们将一份已有的文件(原型)放入复印机,通过复印操作得到一份与原文件内容完全相同的新文件(克隆对象)。在 Java 中,实现原型模式通常需要让具体的原型类实现 Cloneable 接口,并在类中重写 Object 类的 clone () 方法。Cloneable 接口是一个标记接口,它没有定义任何方法,只是用来标识实现该接口的类可以被克隆。当一个类实现了 Cloneable 接口并正确重写了 clone () 方法后,就可以通过调用该对象的 clone () 方法来创建一个新的对象,这个新对象是原对象的一个副本。
例如,假设有一个 “员工” 类 Employee,其中包含员工的姓名、年龄、职位等属性。如果我们已经创建了一个员工对象 emp1,现在需要创建一个具有相同属性的新员工对象 emp2,就可以利用原型模式。首先,让 Employee 类实现 Cloneable 接口,并重写 clone () 方法。在重写的 clone () 方法中,通过调用 super.clone () 来实现对象的浅克隆,即复制对象的基本数据类型属性和引用对象的内存地址。然后,在需要创建新员工对象时,调用 emp1 的 clone () 方法,就可以得到一个新的员工对象 emp2,emp2 的属性值与 emp1 相同。如果需要对 emp2 的属性进行修改,比如修改其职位,直接在 emp2 对象上进行操作即可,不会影响到 emp1 对象。
2.3 与其他创建型模式的差异
在创建型设计模式的大家族中,除了原型模式,工厂模式和建造者模式也是常用的成员,它们各自有着独特的设计理念和适用场景,与原型模式存在明显的差异。
工厂模式主要关注对象的创建逻辑,将对象的创建和使用分离。它通过一个工厂类来负责创建对象,客户端只需要向工厂类请求所需的对象,而不需要关心对象的具体创建过程。比如在一个汽车生产厂中,汽车工厂类负责创建不同型号的汽车对象,客户端只需要告诉工厂类自己需要哪种型号的汽车,工厂类就会根据请求创建出相应的汽车对象并返回给客户端。工厂模式适用于创建对象的逻辑比较复杂,或者需要根据不同的条件创建不同类型对象的场景。而原型模式则是基于已有对象的复制来创建新对象,重点在于对象的复制过程,它更适合创建对象的成本较高,或者需要创建大量相似对象的场景。例如,在一个游戏开发中,需要创建大量具有相似属性的怪物对象,如果使用工厂模式,每次创建怪物对象都需要执行复杂的初始化逻辑,而使用原型模式,只需要创建一个怪物原型对象,然后通过复制这个原型对象就可以快速创建出大量的怪物对象,大大提高了创建效率。
建造者模式则侧重于将复杂对象的构建过程和表示过程分离,通过一步步地构建对象的各个部分,最终创建出完整的对象。它通常包含一个指挥者(Director)和多个具体的建造者(ConcreteBuilder)。指挥者负责控制对象的构建步骤和顺序,具体的建造者负责实现对象各个部分的构建逻辑。比如建造一座房子,指挥者知道建造房子的步骤是先打地基、再砌墙、然后盖屋顶等,而具体的建造者则负责实际执行这些步骤,如地基建造者负责打地基,墙体建造者负责砌墙等。建造者模式适用于创建对象的过程非常复杂,涉及多个步骤和多个部分的场景。与原型模式相比,建造者模式更注重对象的构建过程和步骤的控制,而原型模式则更强调对象的复制和快速创建。
三、原型模式的结构与角色
3.1 抽象原型角色
抽象原型角色是原型模式中的一个关键抽象,它定义了克隆方法的接口,为所有具体原型类提供了统一的规范。在 Java 中,这个角色通常由一个接口或抽象类来实现。以 Java 的 Cloneable 接口为例,它是一个标记接口,本身并不包含任何方法定义,其主要作用是标识实现该接口的类可以被克隆 。当一个类实现了 Cloneable 接口后,就表明它具备了被克隆的能力,进而可以重写 Object 类中的 clone () 方法来实现具体的克隆逻辑。
假设我们正在开发一个图形绘制系统,其中有各种形状的图形对象,如圆形、矩形、三角形等。为了实现通过复制已有图形对象来创建新图形对象的功能,我们可以定义一个抽象的 Shape 接口作为抽象原型角色,该接口中声明一个 clone () 方法,用于克隆自身。
public interface Shape extends Cloneable {Shape clone();
}
在这个例子中,Shape 接口就是抽象原型角色,它为所有具体的形状类(如圆形、矩形等)提供了一个统一的克隆方法定义。任何实现了 Shape 接口的具体形状类都必须实现 clone () 方法,以满足原型模式的要求。通过这种方式,抽象原型角色在原型模式中起到了规范和约束的作用,确保了所有具体原型类都具备克隆的能力和统一的接口。
3.2 具体原型角色
具体原型角色是原型模式中真正被复制的对象,它实现了抽象原型角色所定义的接口,完成自身的复制操作。在实际应用中,具体原型类会根据自身的属性和业务逻辑,重写克隆方法,以确保克隆出来的新对象与原对象具有相同的状态和属性值。
继续以上述图形绘制系统为例,我们有一个具体的圆形类 Circle,它实现了 Shape 接口,成为具体原型角色。在 Circle 类中,我们需要重写 clone () 方法,以实现圆形对象的克隆。
public class Circle implements Shape {private int radius;private Point center;public Circle(int radius, Point center) {this.radius = radius;this.center = center;}public int getRadius() {return radius;}public Point getCenter() {return center;}@Overridepublic Shape clone() {try {// 调用父类的clone方法进行浅克隆Circle clonedCircle = (Circle) super.clone();// 对引用类型的成员变量进行深克隆(假设Point类也实现了Cloneable接口)clonedCircle.center = (Point) center.clone();return clonedCircle;} catch (CloneNotSupportedException e) {e.printStackTrace();return null;}}
}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 int getY() {return y;}@Overridepublic Point clone() {try {return (Point) super.clone();} catch (CloneNotSupportedException e) {e.printStackTrace();return null;}}
}
在这个例子中,Circle 类实现了 Shape 接口,并实现了 clone () 方法。在 clone () 方法中,首先调用 super.clone () 进行浅克隆,获取一个新的 Circle 对象,然后对其中的引用类型成员变量 center 进行深克隆,确保克隆出来的新圆形对象与原对象在属性和状态上完全一致,包括其内部的引用对象。这样,Circle 类就成功地扮演了具体原型角色,能够通过克隆自身来创建新的圆形对象。
3.3 客户端角色
客户端角色是原型模式的使用者,它通过调用原型对象的克隆方法来创建新的对象。在客户端代码中,首先需要获取到一个原型对象,然后根据需要多次调用该原型对象的克隆方法,从而快速创建出多个具有相同属性和状态的新对象。客户端不需要了解对象的具体创建过程,只需要关注如何使用克隆方法来满足自身的业务需求。
在图形绘制系统的示例中,客户端代码可能如下:
public class Client {public static void main(String[] args) {// 创建一个原型圆形对象Point center = new Point(100, 100);Circle prototypeCircle = new Circle(50, center);// 通过克隆创建新的圆形对象Circle clonedCircle1 = (Circle) prototypeCircle.clone();Circle clonedCircle2 = (Circle) prototypeCircle.clone();// 可以对克隆出来的对象进行属性修改clonedCircle1.getCenter().setX(150);clonedCircle2.getRadius() = 60;// 输出圆形对象的属性System.out.println("原型圆形:半径 = " + prototypeCircle.getRadius() + ",圆心 = (" + prototypeCircle.getCenter().getX() + ", " + prototypeCircle.getCenter().getY() + ")");System.out.println("克隆圆形1:半径 = " + clonedCircle1.getRadius() + ",圆心 = (" + clonedCircle1.getCenter().getX() + ", " + clonedCircle1.getCenter().getY() + ")");System.out.println("克隆圆形2:半径 = " + clonedCircle2.getRadius() + ",圆心 = (" + clonedCircle2.getCenter().getX() + ", " + clonedCircle2.getCenter().getY() + ")");}
}
在上述代码中,客户端首先创建了一个原型圆形对象 prototypeCircle,然后通过调用其 clone () 方法,创建了两个新的圆形对象 clonedCircle1 和 clonedCircle2。之后,客户端可以对克隆出来的对象进行属性修改,以满足不同的业务需求。通过这种方式,客户端利用原型模式,轻松地实现了对象的快速创建和定制,而无需关心复杂的对象创建过程。
3.4 原型管理器角色
原型管理器角色是原型模式中的一个可选组件,它主要负责管理原型对象的集合,为客户端提供便捷的原型对象获取和创建服务。原型管理器通常维护一个存储原型对象的容器,如 HashMap、List 等,客户端可以通过向原型管理器请求特定类型的原型对象,然后由原型管理器返回该原型对象的克隆,从而实现对象的创建。
在实际应用中,当系统中存在多个不同类型的原型对象,并且需要对这些原型对象进行统一管理和维护时,原型管理器就显得尤为重要。它可以将原型对象的创建和管理逻辑封装起来,使得客户端代码更加简洁和可维护。
以图形绘制系统为例,我们可以创建一个 ShapeManager 类作为原型管理器,代码如下:
import java.util.HashMap;
import java.util.Map;public class ShapeManager {private static ShapeManager instance;private Map<String, Shape> shapeMap;private ShapeManager() {shapeMap = new HashMap<>();}public static ShapeManager getInstance() {if (instance == null) {instance = new ShapeManager();}return instance;}public void registerShape(String shapeName, Shape shape) {shapeMap.put(shapeName, shape);}public Shape getShape(String shapeName) {Shape shape = shapeMap.get(shapeName);if (shape!= null) {return shape.clone();}return null;}
}
在这个 ShapeManager 类中,我们使用单例模式确保系统中只有一个原型管理器实例。shapeMap 用于存储不同类型的原型对象,通过 registerShape 方法可以将原型对象注册到管理器中,而 getShape 方法则根据传入的形状名称返回相应原型对象的克隆。
客户端代码可以这样使用原型管理器:
public class Client {public static void main(String[] args) {// 获取原型管理器实例ShapeManager shapeManager = ShapeManager.getInstance();// 创建原型圆形对象并注册到管理器Point center = new Point(100, 100);Circle prototypeCircle = new Circle(50, center);shapeManager.registerShape("Circle", prototypeCircle);// 从原型管理器获取克隆的圆形对象Circle clonedCircle = (Circle) shapeManager.getShape("Circle");// 输出克隆圆形对象的属性System.out.println("克隆圆形:半径 = " + clonedCircle.getRadius() + ",圆心 = (" + clonedCircle.getCenter().getX() + ", " + clonedCircle.getCenter().getY() + ")");}
}
通过使用原型管理器,客户端可以更加方便地获取和创建原型对象,无需直接管理原型对象的创建和存储,提高了代码的可维护性和可扩展性。
四、Java 中的原型模式实现
4.1 实现步骤
在 Java 中实现原型模式,主要涉及以下两个关键步骤:
- 实现 Cloneable 接口:Java 中的 Cloneable 接口是一个标记接口,它没有定义任何方法,其作用是标识一个类可以被克隆。当一个类实现了 Cloneable 接口,就表明这个类具备了被克隆的能力。例如,假设有一个类 Sheep,要使其能够被克隆,就
需要实现 Cloneable 接口:
public class Sheep implements Cloneable {// 类的属性和方法定义
}
- 重写 clone 方法:在实现了 Cloneable 接口的类中,需要重写 Object 类的 clone 方法。Object 类中的 clone 方法是受保护的,因此在子类中重写时需要将其访问修饰符改为 public,以确保外部可以调用。重写的 clone 方法负责创建并返回当前对象的一个副本。在 clone 方法中,通常会调用 super.clone () 来获取浅拷贝的对象,然后根据需要对对象中的引用类型属性进行进一步处理,以实现深拷贝。例如:
public class Sheep implements Cloneable {private String name;private int age;public Sheep(String name, int age) {this.name = name;this.age = age;}@Overridepublic Object clone() {try {Sheep clonedSheep = (Sheep) super.clone();// 这里可以对引用类型属性进行深拷贝处理(如果有)return clonedSheep;} catch (CloneNotSupportedException e) {e.printStackTrace();return null;}}
}
在上述代码中,Sheep 类实现了 Cloneable 接口并重写了 clone 方法。在 clone 方法中,首先通过 super.clone () 获取浅拷贝的 Sheep 对象,然后可以根据实际情况对该对象进行进一步的处理,如对引用类型属性进行深拷贝。如果在克隆过程中发生异常,如该类未实现 Cloneable 接口,会捕获并处理 CloneNotSupportedException 异常。
4.2 浅拷贝实现与示例
浅拷贝是指创建一个新对象,新对象的属性值与原对象相同,但对于引用类型的属性,新对象和原对象共享同一个引用,即指向同一个内存地址。在 Java 中,通过调用 super.clone () 方法可以实现浅拷贝。
以下是一个浅拷贝的示例代码:
class Sheep implements Cloneable {private String name;private int age;private String color;public Sheep(String name, int age, String color) {this.name = name;this.age = age;this.color = color;}@Overridepublic Object clone() {try {return super.clone();} catch (CloneNotSupportedException e) {e.printStackTrace();return null;}}
}public class PrototypePatternDemo {public static void main(String[] args) {Sheep originalSheep = new Sheep("Tom", 2, "white");Sheep clonedSheep = (Sheep) originalSheep.clone();System.out.println("Original Sheep: " + originalSheep.hashCode());System.out.println("Cloned Sheep: " + clonedSheep.hashCode());System.out.println("Original Sheep Name: " + originalSheep.name);System.out.println("Cloned Sheep Name: " + clonedSheep.name);// 修改克隆对象的属性clonedSheep.name = "Jerry";System.out.println("After modification:");System.out.println("Original Sheep Name: " + originalSheep.name);System.out.println("Cloned Sheep Name: " + clonedSheep.name);}
}
在上述代码中,Sheep 类实现了 Cloneable 接口并重写了 clone 方法,通过调用 super.clone () 实现浅拷贝。在 main 方法中,创建了一个原始的 Sheep 对象 originalSheep,然后通过调用 clone 方法克隆出一个新的 Sheep 对象 clonedSheep。通过输出两个对象的哈希码可以看出,它们是不同的对象。当修改克隆对象 clonedSheep 的 name 属性时,原始对象 originalSheep 的 name 属性不受影响,因为基本数据类型和 String 类型在浅拷贝中是值传递。然而,如果 Sheep 类中有引用类型的属性,那么在浅拷贝中,原始对象和克隆对象的该引用类型属性将指向同一个对象,对其中一个对象的引用类型属性的修改会影响到另一个对象。
4.3 深拷贝实现与示例
深拷贝是指创建一个新对象,新对象的所有属性值与原对象相同,并且对于引用类型的属性,新对象和原对象各自拥有独立的副本,即不共享引用,修改一个对象的引用类型属性不会影响到另一个对象。在 Java 中,实现深拷贝有以下两种常见方式:
方式一:手动递归复制引用类型属性
通过在 clone 方法中手动创建引用类型属性的新实例,并将原对象引用类型属性的值复制到新实例中,从而实现深拷贝。例如:
class Address implements Cloneable {private String city;private String street;public Address(String city, String street) {this.city = city;this.street = street;}@Overridepublic Object clone() {try {return super.clone();} catch (CloneNotSupportedException e) {e.printStackTrace();return null;}}
}class Person implements Cloneable {private String name;private int age;private Address address;public Person(String name, int age, Address address) {this.name = name;this.age = age;this.address = address;}@Overridepublic Object clone() {try {Person clonedPerson = (Person) super.clone();// 手动深拷贝引用类型属性clonedPerson.address = (Address) address.clone();return clonedPerson;} catch (CloneNotSupportedException e) {e.printStackTrace();return null;}}
}public class DeepCopyDemo {public static void main(String[] args) {Address originalAddress = new Address("Beijing", "Wangfujing Street");Person originalPerson = new Person("Alice", 30, originalAddress);Person clonedPerson = (Person) originalPerson.clone();System.out.println("Original Person Address: " + originalPerson.address.hashCode());System.out.println("Cloned Person Address: " + clonedPerson.address.hashCode());// 修改克隆对象的地址属性clonedPerson.address.city = "Shanghai";System.out.println("After modification:");System.out.println("Original Person City: " + originalPerson.address.city);System.out.println("Cloned Person City: " + clonedPerson.address.city);}
}
在上述代码中,Person 类包含一个引用类型属性 Address。在 Person 类的 clone 方法中,首先调用 super.clone () 获取浅拷贝的 Person 对象,然后手动调用 address.clone () 方法,为克隆对象创建一个独立的 Address 副本,从而实现深拷贝。通过输出原始对象和克隆对象的地址属性的哈希码可以看出,它们是不同的对象,修改克隆对象的地址属性不会影响到原始对象。
方式二:使用序列化和反序列化
将对象写入字节流(序列化),然后再从字节流中读取对象(反序列化),这样得到的新对象与原对象完全独立,实现了深拷贝。这种方式要求对象及其引用类型的属性都必须实现 Serializable 接口。例如:
import java.io.*;class Address implements Serializable {private String city;private String street;public Address(String city, String street) {this.city = city;this.street = street;}
}class Person implements Serializable {private String name;private int age;private Address address;public Person(String name, int age, Address address) {this.name = name;this.age = age;this.address = address;}public Object deepClone() {try {// 序列化ByteArrayOutputStream bos = new ByteArrayOutputStream();ObjectOutputStream oos = new ObjectOutputStream(bos);oos.writeObject(this);oos.close();// 反序列化ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());ObjectInputStream ois = new ObjectInputStream(bis);Object clonedObject = ois.readObject();ois.close();return clonedObject;} catch (IOException | ClassNotFoundException e) {e.printStackTrace();return null;}}
}public class SerializationDeepCopyDemo {public static void main(String[] args) {Address originalAddress = new Address("Beijing", "Wangfujing Street");Person originalPerson = new Person("Bob", 25, originalAddress);Person clonedPerson = (Person) originalPerson.deepClone();System.out.println("Original Person Address: " + originalPerson.address.hashCode());System.out.println("Cloned Person Address: " + clonedPerson.address.hashCode());// 修改克隆对象的地址属性clonedPerson.address.city = "Guangzhou";System.out.println("After modification:");System.out.println("Original Person City: " + originalPerson.address.city);System.out.println("Cloned Person City: " + clonedPerson.address.city);}
}
在上述代码中,Person 类和 Address 类都实现了 Serializable 接口。Person 类中定义了一个 deepClone 方法,通过将当前对象序列化到字节流,再从字节流中反序列化得到一个新的对象,实现了深拷贝。在 main 方法中,创建了原始对象 originalPerson,然后调用 deepClone 方法得到克隆对象 clonedPerson。通过输出原始对象和克隆对象的地址属性的哈希码以及修改克隆对象的地址属性后的结果,可以验证深拷贝的效果,即两个对象的地址属性相互独立,修改一个不会影响另一个。
五、原型模式的实际应用场景
5.1 游戏开发中的应用
在游戏开发领域,原型模式有着广泛而重要的应用,尤其是在克隆游戏角色方面,其优势表现得淋漓尽致。以一款热门的多人在线角色扮演游戏(MMORPG)为例,游戏中存在着各种各样的角色,每个角色都拥有独特的属性和技能,如生命值、攻击力、防御力、魔法值、各种主动和被动技能等。这些属性和技能的初始化和配置往往较为复杂,如果在游戏运行过程中,每次需要创建新的角色时都通过常规的构造函数进行全新的初始化,将会消耗大量的时间和系统资源,严重影响游戏的性能和玩家的体验。
利用原型模式,游戏开发者可以先创建一个基础的角色原型对象,精心配置好其各项属性和技能。当玩家在游戏中进行一些操作需要创建新的角色时,比如玩家创建新的游戏角色、在副本中生成怪物角色或者在团队战斗中召唤支援角色等场景,系统只需通过复制这个原型对象来快速创建新的角色。这样,不仅大大减少了对象创建的时间开销,还能确保新创建的角色具有一致的初始状态和属性配置,提高了游戏的稳定性和流畅性。
例如,在游戏中,有一种常见的怪物角色 “哥布林”,它具有固定的生命值、攻击力、防御力以及一些简单的攻击技能。开发者可以创建一个 “哥布林原型” 对象,将其属性和技能进行初始化和设置。当玩家进入某个地图区域,需要生成多个哥布林怪物时,系统直接克隆 “哥布林原型” 对象,快速生成多个哥布林怪物角色,这些克隆出来的哥布林角色在属性和技能上与原型保持一致,并且可以根据游戏的具体需求,对每个克隆角色进行一些个性化的微调,比如调整其在地图中的位置、赋予其不同的 AI 行为等。通过这种方式,原型模式在游戏开发中有效地提高了开发效率,降低了资源消耗,为玩家提供了更加流畅和丰富的游戏体验。
5.2 文档编辑器中的应用
在文档编辑器的开发中,原型模式同样发挥着关键作用,特别是在复制图形和内容方面。以一款专业的图形文档编辑器为例,用户在编辑文档时,常常需要复制已有的图形元素(如矩形、圆形、线条等)或者一段包含格式和内容的文本段落,以提高编辑效率和文档的一致性。
当用户选择一个图形元素并执行复制操作时,文档编辑器实际上是利用原型模式来实现这一功能。编辑器中存在着各种图形类,每个图形类都实现了原型模式的克隆方法。例如,对于一个矩形图形类 Rectangle,它实现了 Cloneable 接口并重写了 clone () 方法。当用户复制一个矩形时,系统会调用该矩形对象的 clone () 方法,创建一个与原矩形具有相同属性(如位置、大小、颜色、填充样式等)的新矩形对象。然后,新创建的矩形对象会被放置在用户指定的位置,或者根据默认的复制规则进行定位,从而实现了图形的复制。
对于文档中的文本内容,同样可以利用原型模式。假设文档中的文本段落具有丰富的格式设置,如字体、字号、颜色、加粗、倾斜、下划线等,以及具体的文本内容。当用户复制一段文本时,系统会将这段文本及其格式信息视为一个整体的原型对象进行克隆。通过克隆操作,创建出一个与原文本段落格式和内容完全相同的新文本段落,然后用户可以将其粘贴到文档的其他位置,并根据需要对新段落的内容进行修改,而格式信息则保持不变。这种基于原型模式的复制机制,使得用户在文档编辑过程中能够快速、准确地复制和重用图形和内容,极大地提高了文档编辑的效率和便捷性,为用户提供了更加流畅和高效的文档编辑体验。
5.3 数据库记录缓存中的应用
在数据库记录缓存的场景中,原型模式能够显著提高缓存的效率和数据处理的性能。以一个大型的电子商务系统为例,该系统需要频繁地从数据库中读取商品信息,如商品的名称、价格、库存数量、描述、图片等,以展示在用户界面上。由于数据库查询操作通常涉及到磁盘 I/O 等相对较慢的操作,如果每次用户请求商品信息时都直接从数据库中查询,将会导致系统响应时间变长,性能下降。
为了提高系统性能,系统会使用缓存机制来存储常用的商品信息。利用原型模式,系统可以在第一次从数据库中读取商品信息时,创建一个商品对象的原型,并将其存储在缓存中。当后续有相同的商品信息请求时,系统直接从缓存中获取该商品原型对象,然后通过克隆方法创建一个新的商品对象副本返回给请求者。这样,避免了频繁地从数据库中读取数据,大大减少了数据库的负载和查询时间,提高了系统的响应速度。
例如,当用户在电商平台上浏览某类商品列表时,系统会从缓存中获取该类商品的原型对象,然后根据用户的具体请求,克隆出相应数量的商品对象,并根据用户的个性化设置(如排序方式、筛选条件等)对克隆对象进行一些属性调整,最后将这些克隆对象展示给用户。通过这种方式,原型模式在数据库记录缓存中实现了数据的高效复用和快速响应,提升了系统的整体性能和用户体验。
5.4 网络连接池中的应用
在高并发 Web 应用程序的网络连接池管理中,原型模式作用关键。若每次网络通信都新建并销毁连接,会大量消耗系统资源,降低响应速度,无法满足高并发需求。
利用原型模式,系统先创建网络连接原型对象并放入连接池。有通信请求时,从池里获取原型对象,克隆出新连接副本用于通信,结束后将副本放回,而非销毁。
比如电商平台订单处理模块,用户提交订单时,系统从连接池获取网络连接克隆对象与支付网关通信,完成支付后放回,供下次使用。如此,原型模式实现网络连接高效复用与管理,提升系统并发处理能力和响应速度,保障高并发环境下系统稳定运行。
六、原型模式的优缺点
6.1 优点
-
性能提升显著:创建对象时通过复制已有对象,避免复杂初始化。如大型游戏开发中创建怪物对象,无需每次都重新加载模型、设置属性,减少时间与资源消耗,在电商、物流等频繁创建对象的系统中优势明显。
-
灵活性高:运行时可动态创建和修改对象。像图形绘制软件,用户能基于原型克隆并修改属性创建各种图形,满足动态需求,适用于按用户输入或运行条件创建对象的场景。
-
简化对象创建流程:对于复杂对象,将创建过程简化为克隆操作。如 ERP 系统创建订单对象,克隆原型对象并按需修改,降低代码复杂性,提高开发与维护效率。
6.2 缺点
-
深浅拷贝的复杂性:需考虑浅拷贝与深拷贝。浅拷贝可能致数据不一致,深拷贝实现复杂,处理复杂对象结构时代码冗长难维护,如包含多层嵌套对象的文档对象。
-
代码复杂度增加:实现原型模式需实现 Cloneable 接口并重写 clone 方法,类结构变化时需同步更新,管理多个原型对象还需额外代码,大型项目中维护难度加大。
七、原型模式与其他模式的结合
7.1 与工厂模式结合
在实际的软件开发中,原型模式常常与工厂模式结合使用,以发挥两者的优势,实现更加灵活和高效的对象创建机制。以 SpoonFactory 为例,假设我们正在开发一个餐饮管理系统,其中涉及到各种餐具的管理和使用。在这个系统中,有不同类型的勺子,如汤勺(SoupSpoon)、沙拉勺(SaladSpoon)等,每种勺子都有其独特的属性和功能。
首先,我们定义一个抽象的勺子类 AbstractSpoon,它实现了 Cloneable 接口,具备克隆的能力,作为抽象原型角色:
public abstract class AbstractSpoon implements Cloneable {private String spoonName;public String getSpoonName() {return spoonName;}public void setSpoonName(String spoonName) {this.spoonName = spoonName;}@Overridepublic Object clone() {Object object = null;try {object = super.clone();} catch (CloneNotSupportedException exception) {System.err.println("AbstractSpoon is not Cloneable");}return object;}
}
然后,定义具体的汤勺类 SoupSpoon 和沙拉勺类 SaladSpoon,它们继承自 AbstractSpoon,成为具体原型角色:
public class SoupSpoon extends AbstractSpoon {public SoupSpoon() {setSpoonName("Soup Spoon");}
}public class SaladSpoon extends AbstractSpoon {public SaladSpoon() {setSpoonName("Salad Spoon");}
}
接着,创建一个勺子工厂类 SpoonFactory,作为工厂模式中的工厂角色。在这个工厂类中,我们使用一个 Map 来存储不同类型勺子的原型对象,并提供一个方法来获取特定类型勺子的克隆对象:
import java.util.HashMap;
import java.util.Map;public class SpoonFactory {private static final Map<String, AbstractSpoon> spoonPrototypeMap = new HashMap<>();static {spoonPrototypeMap.put("SoupSpoon", new SoupSpoon());spoonPrototypeMap.put("SaladSpoon", new SaladSpoon());}public static AbstractSpoon getSpoon(String spoonType) {AbstractSpoon prototype = spoonPrototypeMap.get(spoonType);if (prototype!= null) {return (AbstractSpoon) prototype.clone();}return null;}
}
在上述代码中,SpoonFactory 类通过静态代码块初始化了汤勺和沙拉勺的原型对象,并将它们存储在 spoonPrototypeMap 中。当客户端需要获取某种类型的勺子时,只需调用 SpoonFactory 的 getSpoon 方法,传入勺子类型的名称,工厂会从 Map 中获取对应的原型对象,并返回其克隆对象。
这种结合方式的优势在于:一方面,利用工厂模式的集中化管理,将不同类型勺子的创建逻辑封装在工厂类中,客户端无需了解具体的创建过程,只需要通过工厂类获取所需的勺子对象,降低了客户端与具体勺子类之间的耦合度;另一方面,借助原型模式的复制机制,避免了每次创建新勺子对象时都进行复杂的初始化操作,提高了对象创建的效率。当系统中需要添加新类型的勺子时,只需要在 SpoonFactory 中注册新的原型对象,而无需修改大量的客户端代码,增强了系统的可扩展性和维护性。
7.2 与装饰器模式结合
原型模式与装饰器模式的结合,为对象的创建和功能扩展提供了一种强大而灵活的方式。这种结合的核心原理在于,先通过原型模式克隆出一个对象,然后利用装饰器模式对克隆后的对象进行动态的功能添加和修改。
以一个图形绘制系统为例,假设我们有一个基本的图形类 Shape,它实现了 Cloneable 接口,具备克隆的能力,作为原型模式中的具体原型角色:
public class Shape implements Cloneable {private String color;public Shape(String color) {this.color = color;}public String getColor() {return color;}public void setColor(String color) {this.color = color;}@Overridepublic Object clone() {try {return super.clone();} catch (CloneNotSupportedException e) {e.printStackTrace();return null;}}
}
然后,定义一个装饰器抽象类 ShapeDecorator,它实现了与 Shape 相同的接口,作为装饰器模式中的抽象装饰器角色:
public abstract class ShapeDecorator implements Shape {protected Shape decoratedShape;public ShapeDecorator(Shape decoratedShape) {this.decoratedShape = decoratedShape;}@Overridepublic String getColor() {return decoratedShape.getColor();}@Overridepublic void setColor(String color) {decoratedShape.setColor(color);}@Overridepublic Object clone() {// 这里可以根据需要实现装饰器的克隆逻辑return decoratedShape.clone();}
}
接着,定义具体的装饰器类,如 BorderDecorator,用于为图形添加边框装饰,作为装饰器模式中的具体装饰器角色:
public class BorderDecorator extends ShapeDecorator {private int borderWidth;public BorderDecorator(Shape decoratedShape, int borderWidth) {super(decoratedShape);this.borderWidth = borderWidth;}public void drawWithBorder() {System.out.println("Drawing shape with border width: " + borderWidth);decoratedShape.draw();}@Overridepublic void draw() {drawWithBorder();}
}
在客户端代码中,我们可以先通过原型模式克隆出一个 Shape 对象,然后使用装饰器模式为克隆对象添加装饰:
public class Client {public static void main(String[] args) {Shape originalShape = new Shape("Red");Shape clonedShape = (Shape) originalShape.clone();ShapeDecorator borderedShape = new BorderDecorator(clonedShape, 5);borderedShape.draw();}
}
在上述代码中,首先创建了一个红色的原始 Shape 对象 originalShape,然后通过克隆得到 clonedShape。接着,使用 BorderDecorator 装饰器为 clonedShape 添加了宽度为 5 的边框装饰,得到 borderedShape。最后调用 borderedShape 的 draw 方法,会先绘制带有边框的图形,再调用被装饰的 Shape 对象的 draw 方法,实现了动态添加功能的效果。
通过这种结合方式,我们可以在运行时根据实际需求,灵活地克隆对象并为其添加各种装饰,从而满足不同的业务场景和功能需求。这种方式避免了在原型类中硬编码各种功能,提高了代码的可维护性和可扩展性,使得系统更加灵活和易于定制。
八、使用原型模式的注意事项
8.1 深浅拷贝的选择
运用原型模式时,深浅拷贝的选择关乎程序的正确性与稳定性。
-
浅拷贝适用场景:对象属性仅含基本数据类型(如 int、double 等)和 String 类型时,浅拷贝通常可行。因基本数据类型直接复制值,String 类型不可变,浅拷贝后对象属性相互独立。如简单员工信息类,含工号、姓名、年龄等属性,用浅拷贝可快速创建新对象且保证数据独立。
-
深拷贝需求场景:对象属性含引用类型(如自定义类对象、数组)时需谨慎。浅拷贝会使原对象与克隆对象共享引用类型属性地址,修改克隆对象会影响原对象。例如员工信息类含地址信息(自定义 Address 类对象),浅拷贝下修改克隆对象地址,原对象地址也会改变。此时需深拷贝,递归复制对象及其引用类型属性,确保对象间相互独立。但深拷贝实现复杂,可能影响性能,尤其对象结构复杂或引用类型属性多,需权衡使用。
8.2 克隆方法的正确实现
-
遵循克隆机制规范:实现克隆方法要遵循 Java 克隆机制规范。克隆方法访问修饰符需为 public,因 Object 类中 clone 方法是 protected,子类重写需提升为 public 以便外部调用。同时,要处理克隆过程可能抛出的 CloneNotSupportedException 异常,合理捕获避免程序异常终止。
-
确保业务逻辑复制正确:对于含复杂逻辑和业务规则的对象,克隆方法要保证所有业务逻辑和状态正确复制到克隆对象。如复杂订单对象,涉及商品项、促销活动、支付状态等信息且相互关联。克隆时不仅复制基本属性,引用类型属性也要正确克隆,保证业务状态准确反映,且不破坏内部逻辑和约束关系,确保克隆对象与原对象功能和逻辑一致。
九、总结与展望
9.1 原型模式的回顾
原型模式作为一种创建型设计模式,其核心在于通过复制已有对象来创建新对象,避免了复杂的初始化过程。在实际应用中,我们看到它在多个领域都发挥了重要作用。在游戏开发中,它能够快速克隆游戏角色,提高游戏的运行效率;在文档编辑器中,方便地实现图形和内容的复制,提升用户编辑体验;在数据库记录缓存和网络连接池管理中,有效减少资源消耗,提升系统性能。
然而,原型模式并非完美无缺。深浅拷贝的复杂性要求开发者在使用时必须谨慎抉择,确保数据的独立性和一致性。同时,实现克隆方法也增加了代码的复杂度,需要开发者仔细处理,以保证克隆的正确性和完整性。
9.2 未来应用的展望
随着技术的不断发展,原型模式有望在更多领域展现其价值。在人工智能和机器学习领域,数据的预处理和模型的初始化是关键步骤。原型模式可以用于快速复制和调整数据集,为模型训练提供高效的数据准备。在大数据处理中,面对海量的数据,通过原型模式可以快速创建具有相似结构的数据对象,提高数据处理的效率。在分布式系统中,原型模式也可以用于快速创建和复制分布式对象,简化系统的部署和维护。
此外,随着硬件技术的不断进步,计算机的性能和存储能力得到了极大提升,这为原型模式的应用提供了更广阔的空间。在未来的软件开发中,我们可以期待原型模式与其他先进技术的深度融合,进一步发挥其优势,为解决复杂的软件系统问题提供更加高效、灵活的解决方案。
相关文章:
解锁原型模式:Java 中的高效对象创建之道
系列文章目录 后续补充~~~ 文章目录 一、引言1.1 软件开发中的对象创建困境1.2 原型模式的登场 二、原型模式的核心概念2.1 定义与概念2.2 工作原理剖析2.3 与其他创建型模式的差异 三、原型模式的结构与角色3.1 抽象原型角色3.2 具体原型角色3.3 客户端角色3.4 原型管理器角色…...
DeepSeek从入门到精通:揭秘 AI 提示语设计误区与 AI 幻觉(新手避坑指南)
文章目录 引言常见陷阱与应对策略:新手必知的提示词设计误区缺乏迭代陷阱:期待一次性完美结果过度指令与模糊指令陷阱:当细节缺乏重点或意图不明确假设偏见陷阱:当前 AI 只听你想听的幻觉生成陷阱:当AI自信地胡说八道忽…...
Jenkins同一个项目不同分支指定不同JAVA环境
背景 一些系统应用,会为了适配不同的平台,导致不同的分支下用的是不同的gradle,导致需要不同的JAVA环境来编译,比如a分支需要使用JAVA11, b分支使用JAVA17。 但是jenkins上,一般都是Global Tool Configuration 全局所有环境公用一个JAVA_HOME。 尝试过用 Build 的Execut…...
从入门到精通:Postman 实用指南
Postman 是一款超棒的 API 开发工具,能用来测试、调试和管理 API,大大提升开发效率。下面就给大家详细讲讲它的安装、使用方法,再分享些实用技巧。 一、安装 Postman 你能在 Postman 官网(https://www.postman.com )下…...
win32汇编环境,对话框中使用月历控件示例二
;运行效果 ;win32汇编环境,对话框中使用月历控件示例二 ;以下示例有2个操作,即将每周的开始日进行改变,将默认的周日开始改为周一开始,同时实现点击哪个日期,则设定为哪个日期 ;直接抄进RadAsm可编译运行。重要部分加备注。 ;下面为asm文件 ;>>>>>>>&…...
gsoap实现webservice服务
gsoap实现webservice服务 在实现Web服务时,使用gSOAP是一个很好的选择,因为它提供了强大的工具和库来创建SOAP和RESTful服务。gSOAP是一个C和C语言开发的库,它支持SOAP协议的各种版本,包括SOAP 1.1和SOAP 1.2。下面是如何使用gSO…...
容联云联络中心AICC:深度整合DeepSeek,业务验证结果公开
容联云重磅推出AICC3.2版本,实现了智能化的升级与外呼效率的突破——深度整合DeepSeek-R1大模型、预测式外呼在数据分析侧的增强、全渠道路由能力、一键多呼效率的强化。 同时,全面接入DeepSeek-R1的容联云 AICC3.2 ,目前已与某知名汽车金融企…...
腿足机器人之七- 逆运动学
腿足机器人之七- 逆运动学 基本概念腿部运动的数学表示坐标系定义以及自由度说明正运动学模型 逆运动学求解几何解法数值迭代法雅可比矩阵法基础双足机器人步态规划中的雅可比法应用 工程挑战与解决方案实际应用中的工具和算法多解问题高自由度机器人(如Atlas的28自…...
快速点位排查问题的方法
一、核心思路:缩小问题范围 1. 分治法(Divide and Conquer) 原理:将复杂系统拆分为独立模块,逐层验证。示例: 网络问题:检查客户端 → 本地网络 → 服务器 → 数据库。代码问题:注…...
【前端】Vue组件库之Element: 一个现代化的 UI 组件库
文章目录 前言一、官网1、官网主页2、设计原则3、导航4、组件 二、核心功能:开箱即用的组件生态1、丰富的组件体系2、特色功能亮点 三、快速上手:三步开启组件化开发1、安装(使用Vue 3)2、全局引入3、按需导入(推荐&am…...
一文搞懂Android应用元素查看器(Appium+Appium-inspector)——定位微信布局元素
Appium和Appium Inspector是怎么协作的呢?Appium 与 Appium Inspector 的版本匹配Appium安装启动appium服务安装Appium inspector客户端查看安卓真机指定app布局元素(这里以微信为例,需要保持与模拟器或真机一直连接)【QA】解决顶部工具栏上Refresh Source & Screensho…...
matlab质子磁力仪传感器线圈参数绘图
1、内容简介 matlab134-质子磁力仪传感器线圈参数绘图 可以交流、咨询、答疑 2、内容说明 略 线圈是质子磁力仪传感器的核心,其品质直接影响着仪器的测量精度 。 结合反向串联圆柱体线圈模型,对约束设计 的因素进行分析; 建立约束参数与设计参数之间…...
WPF快速创建DeepSeek本地自己的客户端-基础思路版本
开发工具:VS 2015 开发环境:.Net 4.0 使用技术:WPF 本篇文章内容: 本地部署DeepSeek以后一般使用网页工具(如Chatbox)或者DOS窗口与其对话。本篇文章使用WPF创建一个基础版的对话工具。 一、搭建本地DeepS…...
FreeRTOS第12篇:系统的“绿色通道”——中断管理与临界区
文/指尖动听知识库-星愿 文章为付费内容,商业行为,禁止私自转载及抄袭,违者必究!!! 文章专栏:深入FreeRTOS内核:从原理到实战的嵌入式开发指南 引言:嵌入式系统的“紧急电话” 想象你正在主持一场重要会议:大部分时间按议程推进(任务执行),但偶尔会有紧急来电(硬…...
SpringBoot+Vue+数据可视化的动漫妆造服务平台(程序+论文+讲解+安装+调试+售后等)
感兴趣的可以先收藏起来,还有大家在毕设选题,项目以及论文编写等相关问题都可以给我留言咨询,我会一一回复,希望帮助更多的人。 系统介绍 在当今数字化高速发展的时代,动漫产业迎来了前所未有的繁荣,动漫…...
CentOS 7超详细安装教程(含镜像)
1. 安装前准备 1.1 CentOS简介 CentOS(Community Enterprise Operating System,中文意思是:社区企业操作系统)是一种基于 Red Hat Enterprise Linux(RHEL)源代码构建的免费开源操作系统。它在稳定性、安全…...
一种棋牌网游的玩法
起因 俺是个记性不好的人,经常记不住牌,所以很少能赢。于是俺就写了个程序来记录出过的牌。 开始 因为是网游,所以就开始监听网络包。因为不需要改网络包,所以俺就选择了cap_ip。cap_ip是一个通过设置网卡混乱模式来监听网络包…...
9.综合调试|输入不能存在空格|desc存在None|输出权值和ID|函数重名|修改文件名|权值和实际关键词出现次数(C++)
输入不能存在空格 目前输入的关键词时每隔一空格内容分别进行搜索,大部分时候我们都是将一串包含空格的内容直接进行搜索,需要将代码改进。 将cin换为fgets #include "searcher.hpp" #include <iostream> #include <cstdio> #in…...
使用SHOW PROCESSLIST和SHOW ENGINE INNODB STATUS排查mysql锁等待问题
现象: mysql 查某表一直不能结束,查别的表没有问题。已知之前刚刚alter此表想把它的一个字段长度增长,但是这个操作一直没有结束。现在应该怎么办? 方案: 使用 SHOW PROCESSLIST; 查看当前所有活动的SQL线程,找出是否有长时间…...
ElasticSearch映射分词
目录 弃用Type why 映射 查询 mapping of index 创建 index with mapping 添加 field with mapping 数据迁移 1.新建 一个 index with correct mapping 2.数据迁移 reindex data into that index 分词 POST _analyze 自定义词库 ik分词器 circuit_breaking_excep…...
《通信之道——从微积分到 5G》读书总结
第1章 绪 论 1.1 这是一本什么样的书 通信技术,说到底就是数学。 那些最基础、最本质的部分。 1.2 什么是通信 通信 发送方 接收方 承载信息的信号 解调出其中承载的信息 信息在发送方那里被加工成信号(调制) 把信息从信号中抽取出来&am…...
html css js网页制作成品——HTML+CSS榴莲商城网页设计(4页)附源码
目录 一、👨🎓网站题目 二、✍️网站描述 三、📚网站介绍 四、🌐网站效果 五、🪓 代码实现 🧱HTML 六、🥇 如何让学习不再盲目 七、🎁更多干货 一、👨…...
Redis的发布订阅模式与专业的 MQ(如 Kafka, RabbitMQ)相比,优缺点是什么?适用于哪些场景?
Redis 的发布订阅(Pub/Sub)模式与专业的 MQ(Message Queue)如 Kafka、RabbitMQ 进行比较,核心的权衡点在于:简单与速度 vs. 可靠与功能。 下面我们详细展开对比。 Redis Pub/Sub 的核心特点 它是一个发后…...
通过 Ansible 在 Windows 2022 上安装 IIS Web 服务器
拓扑结构 这是一个用于通过 Ansible 部署 IIS Web 服务器的实验室拓扑。 前提条件: 在被管理的节点上安装WinRm 准备一张自签名的证书 开放防火墙入站tcp 5985 5986端口 准备自签名证书 PS C:\Users\azureuser> $cert New-SelfSignedCertificate -DnsName &…...
Oracle11g安装包
Oracle 11g安装包 适用于windows系统,64位 下载路径 oracle 11g 安装包...
go 里面的指针
指针 在 Go 中,指针(pointer)是一个变量的内存地址,就像 C 语言那样: a : 10 p : &a // p 是一个指向 a 的指针 fmt.Println(*p) // 输出 10,通过指针解引用• &a 表示获取变量 a 的地址 p 表示…...
小智AI+MCP
什么是小智AI和MCP 如果还不清楚的先看往期文章 手搓小智AI聊天机器人 MCP 深度解析:AI 的USB接口 如何使用小智MCP 1.刷支持mcp的小智固件 2.下载官方MCP的示例代码 Github:https://github.com/78/mcp-calculator 安这个步骤执行 其中MCP_ENDPOI…...
深入理解 React 样式方案
React 的样式方案较多,在应用开发初期,开发者需要根据项目业务具体情况选择对应样式方案。React 样式方案主要有: 1. 内联样式 2. module css 3. css in js 4. tailwind css 这些方案中,均有各自的优势和缺点。 1. 方案优劣势 1. 内联样式: 简单直观,适合动态样式和…...
shell脚本质数判断
shell脚本质数判断 shell输入一个正整数,判断是否为质数(素数)shell求1-100内的质数shell求给定数组输出其中的质数 shell输入一个正整数,判断是否为质数(素数) 思路: 1:1 2:1 2 3:1 2 3 4:1 2 3 4 5:1 2 3 4 5-------> 3:2 4:2 3 5:2 3…...
Python第七周作业
Python第七周作业 文章目录 Python第七周作业 1.使用open以只读模式打开文件data.txt,并逐行打印内容 2.使用pathlib模块获取当前脚本的绝对路径,并创建logs目录(若不存在) 3.递归遍历目录data,输出所有.csv文件的路径…...
