【设计模式系列笔记】原型模式

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
云数据库 Tair(兼容Redis),内存型 2GB
简介: 原型模式(Prototype Pattern)是一种创建型设计模式,其主要目的是通过复制现有对象来创建新对象,而无需知道其具体类型。这种模式属于对象创建型模式,通过克隆来避免使用new关键字创建对象,提高性能和降低系统的耦合度。

1、原型模式介绍

原型模式(Prototype Pattern)是一种创建型设计模式,其主要目的是通过复制现有对象来创建新对象,而无需知道其具体类型。这种模式属于对象创建型模式,通过克隆来避免使用new关键字创建对象,提高性能和降低系统的耦合度。

在Java中,原型模式的实现通常依赖于Cloneable接口和clone()方法。Cloneable接口标识一个类可以通过克隆创建对象。原型模式的关键是要实现对象的克隆,这可以通过浅克隆或深克隆来完成,具体取决于对象内部是否包含了引用类型。

2、关键思想

  1. 对象复制: 原型模式通过复制现有对象的数据来创建新对象,而不是通过使用 new 关键字直接实例化一个新对象。这种复制可以是浅复制(shallow copy)或深复制(deep copy),具体取决于对象内部是否包含引用类型
  1. 浅复制(Shallow Copy):
  • 浅复制仅复制对象本身的所有字段,而不会复制引用类型的对象。
  • 如果原型对象内部包含引用类型的字段,复制的结果是原始对象和副本对象引用相同的引用类型对象,而不是复制一份引用类型对象。
  • 原始对象和副本对象之间共享引用类型对象,因此对引用类型对象的修改会在两者之间产生影响。
  1. 深复制(Deep Copy):
  • 深复制不仅复制对象本身的所有字段,还会递归复制引用类型的对象,使得原始对象和副本对象引用不同的引用类型对象。
  • 原始对象和副本对象之间不共享引用类型对象,因此对引用类型对象的修改不会影响另一个对象。
  1. Cloneable 接口: 在Java中,被复制的对象通常需要实现 Cloneable 接口,该接口标识了对象是可复制的。这个接口是一个标记接口,表示该类的对象可以通过 clone() 方法进行复制。
  2. clone() 方法: 被复制的对象需要重写 clone() 方法,该方法负责实现对象的复制逻辑。在复制过程中,可以选择是进行浅复制还是深复制,以满足具体需求。
  3. 克隆和原型的独立性: 原型模式创建的新对象和原始对象是相互独立的。对新对象的修改不会影响到原始对象,反之亦然。这是通过复制对象数据而不是共享引用来实现的。
  4. 动态配置对象: 原型模式允许动态配置对象,因为在运行时可以创建对象的克隆,并根据实际需求进行调整。

原型模式的优势在于它能够提高系统性能,避免了重复创建相似对象的开销,同时减少了对具体类的依赖,降低了系统的耦合度。该模式常用于需要大量相似对象的场景,特别是当对象的创建过程较为复杂时。

3、实现方式:

假设我们有一个游戏角色类 GameCharacter,它包含一些基本属性,如角色名称和武器列表。我们将使用原型模式创建角色的克隆,以便在游戏中动态生成相似的角色。以下是一个的实现例子:

示例代码

import java.util.ArrayList;
import java.util.List;
// 步骤1:定义可克隆的原型类
class GameCharacter implements Cloneable {
    private String name; // 角色名称
    private List<String> weapons; // 武器列表
    // 构造方法
    public GameCharacter(String name) {
        this.name = name;
        this.weapons = new ArrayList<>();
    }
    // 添加武器
    public void addWeapon(String weapon) {
        weapons.add(weapon);
    }
    // 打印角色信息
    public void displayInfo() {
        System.out.println("Character: " + name);
        System.out.println("Weapons: " + weapons);
    }
    // 步骤2:重写clone()方法
    @Override
    public GameCharacter clone() throws CloneNotSupportedException {
        // 浅复制
        GameCharacter clonedCharacter = (GameCharacter) super.clone();
        // 深复制武器列表
        clonedCharacter.weapons = new ArrayList<>(this.weapons);
        return clonedCharacter;
    }
}
// 步骤3:使用原型创建对象
public class PrototypeExample {
    public static void main(String[] args) {
        // 创建原型角色
        GameCharacter originalCharacter = new GameCharacter("Hero");
        originalCharacter.addWeapon("Sword");
        originalCharacter.addWeapon("Shield");
        // 打印原始角色信息
        System.out.println("Original Character:");
        originalCharacter.displayInfo();
        try {
            // 克隆角色
            GameCharacter clonedCharacter = originalCharacter.clone();
            // 添加新武器到克隆角色
            clonedCharacter.addWeapon("Bow");
            // 打印克隆角色信息
            System.out.println("\nCloned Character:");
            clonedCharacter.displayInfo();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
    }
}

在这个例子中,我们创建了一个 GameCharacter 类,它具有名称和武器列表属性。通过实现 Cloneable 接口和重写 clone() 方法,我们使该类成为可克隆的原型。在 main 方法中,我们创建了一个原始角色对象,然后通过克隆创建了一个相似的角色对象。这两个角色对象共享相同的名称,但武器列表是相互独立的,即实现了深复制。

要点:

  1. 克隆方法: 原型模式的核心是在原型类中实现 Cloneable 接口,并重写 clone() 方法,确保对象可以被复制。
  2. 浅复制和深复制: 根据实际需求选择浅复制或深复制。浅复制只复制对象本身,而深复制会递归复制对象的所有引用类型成员。
  3. 性能提升: 原型模式可以在一定程度上提升系统性能,因为通过复制对象避免了对象的重新创建,尤其在对象的创建过程较为复杂时更为显著。
  4. 动态配置对象: 原型模式允许动态配置对象,因为可以在运行时通过克隆创建新对象,而无需知道具体类型。
  5. 适用于大对象: 当对象的创建过程较为复杂、耗时或者对象的复制是一种常见操作时,原型模式是一个很好的选择。

注意事项:

  1. 深复制的实现: 如果对象内部包含引用类型成员,确保进行深复制,以防止克隆对象和原始对象共享引用类型对象。
  2. 克隆方法的调用: 在使用原型模式时,要注意正确调用对象的 clone() 方法,最好通过捕获 CloneNotSupportedException 来处理可能的异常。
  3. 构造方法的调用: 在克隆时,对象的构造方法不会被调用。如果需要执行特定的初始化操作,可以在 clone() 方法中手动进行。
  4. 序列化替代方案: 在某些情况下,使用序列化(Serializable)和反序列化来实现对象的复制可能是更合适的替代方案。
  5. 克隆和单例模式: 原型模式和单例模式可能存在一些冲突,因为单例模式通常要求只有一个实例,而原型模式则是通过克隆创建新实例。在这种情况下,可以考虑使用其他创建型设计模式,如工厂模式。

优点:

  1. 性能提升: 原型模式通过复制现有对象避免了对象的重新创建,可以提高系统性能,特别是在对象的创建过程较为复杂或者耗时的情况下。
  2. 动态配置对象: 克隆操作可以在运行时动态配置对象,而不是在编译时确定对象的类型。这使得系统更加灵活,能够根据实际需要动态创建对象。
  3. 简化对象创建: 原型模式使得对象的创建变得简单,不需要知道具体对象的类型,只需复制一个现有对象即可得到新对象。
  4. 减少对具体类的依赖: 客户端可以通过克隆操作而不是直接实例化对象,从而减少对具体类的依赖,提高系统的灵活性。

缺点:

  1. 深复制实现复杂: 如果对象内部包含引用类型成员,实现深复制可能相对复杂,需要递归复制所有引用类型对象及其内部对象。
  2. 需要实现Cloneable接口: 被克隆的类需要实现Cloneable接口,并重写clone()方法,这违反了依赖倒置原则(Dependency Inversion Principle)。
  3. 克隆方法的调用: 在使用原型模式时,要注意正确调用对象的clone()方法,需要处理可能的CloneNotSupportedException异常。

应用场景:

  1. 大量相似对象的创建: 当需要创建大量相似对象,而且这些对象的区别仅在于其属性值时,原型模式是一个很好的选择,可以通过克隆来创建新对象,避免重复的初始化工作。
  2. 动态配置对象: 当需要动态配置对象而不知道具体类型时,原型模式提供了一种灵活的创建对象的方式。
  3. 对象的创建过程复杂或耗时: 如果对象的创建过程包含了复杂的初始化步骤或者是耗时的操作,使用原型模式可以提高系统性能。
  4. 避免构造函数的约束: 在某些情况下,构造函数可能有一些限制,而通过克隆可以避免这些约束,使得对象的创建更加灵活。

总的来说,原型模式在某些场景下是非常有用的,特别是当需要创建大量相似对象或者对象创建过程较为复杂的情况。在使用时需要注意深复制的实现和克隆方法的调用。

目录
相关文章
|
9天前
|
设计模式 Java API
Kotlin教程笔记(50) - 改良设计模式 - 工厂模式
Kotlin教程笔记(50) - 改良设计模式 - 工厂模式
|
9天前
|
设计模式 监控 Java
Kotlin教程笔记(52) - 改良设计模式 - 观察者模式
Kotlin教程笔记(52) - 改良设计模式 - 观察者模式
|
9天前
|
设计模式 安全 Java
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
|
24天前
|
设计模式 安全 Java
Kotlin教程笔记(57) - 改良设计模式 - 单例模式
Kotlin教程笔记(57) - 改良设计模式 - 单例模式
27 2
|
5天前
|
设计模式 安全 Java
Kotlin教程笔记(57) - 改良设计模式 - 单例模式
Kotlin教程笔记(57) - 改良设计模式 - 单例模式
|
5天前
|
设计模式 Java Kotlin
Kotlin教程笔记(56) - 改良设计模式 - 装饰者模式
Kotlin教程笔记(56) - 改良设计模式 - 装饰者模式
|
5天前
|
设计模式 监控 Java
Kotlin教程笔记(52) - 改良设计模式 - 观察者模式
Kotlin教程笔记(52) - 改良设计模式 - 观察者模式
16 3
|
5天前
|
设计模式 算法 Kotlin
Kotlin教程笔记(53) - 改良设计模式 - 策略模式
Kotlin教程笔记(53) - 改良设计模式 - 策略模式
20 2
|
5天前
|
设计模式 安全 Java
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
16 1
|
5天前
|
设计模式 Java API
Kotlin教程笔记(50) - 改良设计模式 - 工厂模式
Kotlin教程笔记(50) - 改良设计模式 - 工厂模式
12 1