Java实现原型模式
介绍: 原型模式(Prototype Pattern)是一种创建型设计模式,允许通过复制已有对象来创建新对象,而不需要依赖其具体类。这种模式的关键在于克隆现有对象,而不是通过直接实例化新对象,从而避免重复的复杂对象构建过程。
🎯 案例场景:
假设我们在开发一个游戏角色系统,每个角色都有不同的属性,比如力量、敏捷和智力。创建一个新角色需要耗费大量的计算资源(如生成外貌、装备等)。如果我们想要快速创建一个与现有角色相似的新角色,只需要在原有角色基础上进行微调,而不重新生成所有属性。
我们可以使用原型模式,通过复制已有角色对象,再对其进行调整,来生成新的角色。
原型模式的核心步骤:
- Prototype 接口 :定义
clone()
方法,所有需要被复制的对象实现该接口。 - 具体原型类 :实现
clone()
方法,用于创建对象的副本。 - 客户端代码 :通过调用
clone()
方法来复制已有对象。
🧑💻 Java代码实现:
java
import java.util.HashMap;
import java.util.Map;
// 1. 定义原型接口 Prototype
interface GameCharacter extends Cloneable {
GameCharacter clone();
void display();
}
// 2. 具体的角色类实现原型接口
class Warrior implements GameCharacter {
private String name;
private int strength;
private int agility;
private int intelligence;
public Warrior(String name, int strength, int agility, int intelligence) {
this.name = name;
this.strength = strength;
this.agility = agility;
this.intelligence = intelligence;
}
// 实现 clone 方法
@Override
public Warrior clone() {
try {
return (Warrior) super.clone(); // 调用 Object 类的 clone 方法
} catch (CloneNotSupportedException e) {
throw new RuntimeException("Clone not supported", e);
}
}
// 展示角色信息
@Override
public void display() {
System.out.println("Warrior [Name: " + name + ", Strength: " + strength +
", Agility: " + agility + ", Intelligence: " + intelligence + "]");
}
}
// 3. 客户端代码,使用原型模式
public class PrototypePatternDemo {
public static void main(String[] args) {
// 创建一个原始的角色
Warrior originalWarrior = new Warrior("Arthas", 80, 50, 30);
originalWarrior.display();
// 使用克隆方法复制角色
Warrior clonedWarrior = originalWarrior.clone();
// 修改克隆对象的某些属性
clonedWarrior.display();
// 现在,原始角色和克隆角色的内存地址不同,但属性相同
System.out.println("Original Warrior HashCode: " + originalWarrior.hashCode());
System.out.println("Cloned Warrior HashCode: " + clonedWarrior.hashCode());
}
}
📋 解释:
GameCharacter
接口 :定义了clone()
方法,表示所有实现这个接口的类都可以被克隆。Warrior
类 :实现了GameCharacter
接口,并通过super.clone()
进行浅克隆。这个方法通过复制对象的当前状态来创建一个新实例。- 客户端代码 :首先创建了一个
Warrior
对象originalWarrior
,并使用其clone()
方法创建了一个副本clonedWarrior
。然后可以自由修改克隆对象的属性,而不影响原始对象。
🌟 应用场景:
- 对象创建开销大,不希望每次都通过构造函数创建。
- 需要保存对象的初始状态,以便在不同地方进行修改和操作。
- 游戏开发中,需要频繁创建相似但稍有不同的角色或物品。
深拷贝和浅拷贝
浅拷贝 :复制对象时,只复制对象的基本属性 和引用类型的引用。即如果对象的属性是引用类型(如数组、对象),浅拷贝只会复制这些引用,而不是引用所指向的实际对象。因此,拷贝后的对象和原始对象共享这些引用类型的数据,修改引用类型的内容会影响原始对象。
深拷贝 :不仅会复制对象的基本属性,还会递归地复制引用类型所指向的对象 ,即引用对象也会完全复制成一个新的副本。这样,拷贝后的对象与原始对象完全独立,修改拷贝对象的引用类型内容,不会影响原始对象。
🌟 案例解释:
假设我们有一个 Person
类,里面包含基本类型属性 name
和引用类型 address
。
1. 浅拷贝示例:
浅拷贝只复制基本属性和引用对象的引用,不复制引用对象本身。
java
class Address {
String city;
public Address(String city) {
this.city = city;
}
}
class Person implements Cloneable {
String name;
Address address;
public Person(String name, Address address) {
this.name = name;
this.address = address;
}
// 浅拷贝
@Override
public Person clone() {
try {
return (Person) super.clone(); // 调用 Object 的 clone 方法
} catch (CloneNotSupportedException e) {
throw new RuntimeException("Clone not supported", e);
}
}
public void display() {
System.out.println("Name: " + name + ", City: " + address.city);
}
}
public class ShallowCopyDemo {
public static void main(String[] args) {
Address address = new Address("New York");
Person person1 = new Person("John", address);
Person person2 = person1.clone(); // 浅拷贝
// 显示原始和克隆对象
person1.display(); // Name: John, City: New York
person2.display(); // Name: John, City: New York
// 修改克隆对象的 address
person2.address.city = "San Francisco";
// 浅拷贝后,原始对象的 address 也被改变
person1.display(); // Name: John, City: San Francisco
person2.display(); // Name: John, City: San Francisco
}
}
结果解释:
person2
是person1
的浅拷贝。- 当
person2
的address.city
修改为"San Francisco"
后,person1
的address.city
也变成了"San Francisco"
。这是因为浅拷贝只复制了引用,两个对象共享同一个Address
对象。
2. 深拷贝示例:
深拷贝需要复制所有对象,包括引用对象中的数据。
java
class Address implements Cloneable {
String city;
public Address(String city) {
this.city = city;
}
@Override
public Address clone() {
try {
return (Address) super.clone(); // 克隆 Address 对象
} catch (CloneNotSupportedException e) {
throw new RuntimeException("Clone not supported", e);
}
}
}
class Person implements Cloneable {
String name;
Address address;
public Person(String name, Address address) {
this.name = name;
this.address = address;
}
// 深拷贝
@Override
public Person clone() {
try {
Person clonedPerson = (Person) super.clone();
clonedPerson.address = address.clone(); // 深拷贝 Address
return clonedPerson;
} catch (CloneNotSupportedException e) {
throw new RuntimeException("Clone not supported", e);
}
}
public void display() {
System.out.println("Name: " + name + ", City: " + address.city);
}
}
public class DeepCopyDemo {
public static void main(String[] args) {
Address address = new Address("New York");
Person person1 = new Person("John", address);
Person person2 = person1.clone(); // 深拷贝
// 显示原始和克隆对象
person1.display(); // Name: John, City: New York
person2.display(); // Name: John, City: New York
// 修改克隆对象的 address
person2.address.city = "San Francisco";
// 深拷贝后,原始对象的 address 未被改变
person1.display(); // Name: John, City: New York
person2.display(); // Name: John, City: San Francisco
}
}
结果解释:
person2
是person1
的深拷贝。- 当
person2
的address.city
修改为"San Francisco"
后,person1
的address.city
仍然是"New York"
。这是因为深拷贝创建了Address
对象的独立副本,两个对象不再共享同一个引用。
🔍 关键区别总结:
- 浅拷贝
- 只复制对象的基本类型属性 和引用的地址,不复制引用的实际内容。
- 拷贝后的对象与原对象共享引用类型的对象。
- 如果修改了引用类型的数据,原始对象也会受到影响。
- 深拷贝
- 复制对象的所有属性,包括递归复制引用类型对象。
- 拷贝后的对象和原始对象完全独立,互不影响。
- 修改其中一个对象的引用类型内容,不会影响另一个对象。