A Prototype Pattern defines the creation of a new object that is the same or similar to an existing instance created as a Prototype by copying that Prototype object

scenario

In League of Legends, there are a lot of minions. We define the minions:

// Minion. Java public class Minion{/** * MeleeMinion, CasterMinion, SiegeMinion, SuperMinion */ Private String type; /** * color */ private String color; /** * HP */ private int HP; /** Weapon */ private Weapon; Public Minion(String type, String color, int HP) {system.out.println (" start to construct "); this.type = type; this.color = color; this.hp = hp; }}Copy the code

Minion Minion = new Minion(“Melee”, “Blue”, 200);

In each wave of pawns, there are five of these pawns, so we need new five objects. If the constructor is a very complex or performance consuming operation, then we consume a lot of system resources while creating five objects.

In this scenario, the archetypal pattern is well suited: take an already created pawn as a prototype and copy that pawn to create other pawns that are the same or similar to that pawn.

implementation

The core of the prototype pattern is to create new instances by copying existing ones. In Java’s Object class, the clone() method gives us the ability to copy objects.

protected native Object clone() throws CloneNotSupportedException;
Copy the code

Concrete implementation way, only need to implement Cloneable interface in a class (if the class does not implement the Cloneable interface, call clone method will throw CloneNotSupportedException exception).

Public class implements Cloneable{/** * Implements Cloneable; /** * implements Cloneable; MeleeMinion, CasterMinion, SiegeMinion, SuperMinion */ Private String type; /** * color */ private String color; /** * HP */ private int HP; Public Minion(String type, String color, int HP) {system.out.println (" start to construct "); this.type = type; this.color = color; this.hp = hp; } @override public Minion clone() {try {Minion copy = (Minion) super.clone(); return copy; } catch (CloneNotSupportedException e) { e.printStackTrace(); return null; }} public void display() {system.out.println (" I am "+ type +" small "+ "; Color: "+ color + "; Health: "+ HP"; } public Weapon getWeapon() { return weapon; } public void setWeapon(Weapon weapon) { this.weapon = weapon; } public static void main(String[] args) throws Exception{ Minion minion = new Minion("Melee", "Blue", 200); Minion minionCopy = minion.clone(); Println ("minion == minionCopy: "+ (minion == minionCopy)); minionCopy.display(); }} minion == minionCopy: false I'm a Melee pawn; Color: Blue; HP: 200Copy the code

From the output we can see:

  • Call the Clone method to create an object that is not the same as the prototype object
  • The Clone method creates the object without calling the constructor of the class (as you can see by printing “Start building the minions” only once in the output).

Shallow copy & deep copy

The clone object can be divided into two types:

Shallow copy: Copies a reference to an object, not the object itself

Deep copy: Copies the object itself to create a new object

Java clone method, the implementation of shallow copy. We can actually verify:

Minion now adds Weapon to minions

Public class Weapon implements Cloneable{/** * Weapon type */ private String type; */ private Boolean isDamage; public Weapon(String type) { this.type = type; isDamage = false; } /** * Weapon damage */ public void destroy() {this.isdamage = true; } /** * Is the weapon damaged * @return */ public Boolean () {return isDamage; }} public class implements Cloneable{/** * implements Weapon */ private Weapon; public Weapon getWeapon() { return weapon; } public void setWeapon(Weapon weapon) { this.weapon = weapon; } public static void main(String[] args) throws Exception{ Minion minion = new Minion("Melee", "Blue", 200); Weapon = new Weapon; minion.setWeapon(weapon); Minion minionCopy = minion.clone(); System.out.println(" Minioncopy.getWeapon ().isdamage ()); Minion.getweapon ().destroy(); System.out.println(" Minioncopy.getWeapon ().isdamage ()); }} // The output starts to construct pawn copy Pawn 1 weapon damage: false Copy pawn 1 weapon damage: trueCopy the code

In the main method code, we only let the original pawn weapon break, but judging from the output, our replica pawn weapon also breaks. This confirms our conclusion above: The Clone method provided by Java implements shallow copy.

How to implement deep copy?

As simple as this, we can manually create objects that need deep copies

public Minion clone() { try { Minion copy = (Minion) super.clone(); Weapon Weapon = new Weapon(this.getWeapon().getType()); copy.setWeapon(weapon); return copy; } catch (CloneNotSupportedException e) { e.printStackTrace(); return null; }}Copy the code

This manual creation defeats the purpose of cloning objects. Instead, clone the member objects that need to be copied

Public class Weapon Clone () {@override public Weapon Clone () {try {return (Weapon); super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); return null; }}} @override public Minion clone() {try {Minion copy = (Minion) super.clone(); Weapon = this.weapon. Clone (); copy.setWeapon(weapon); return copy; } catch (CloneNotSupportedException e) { e.printStackTrace(); return null; }}Copy the code

This method of cloning objects can sometimes be very complicated to implement, such as having a large number of member objects of non-basic data types; References to member objects are so deeply nested that you need to implement a clone method for each layer.

A more general way to implement deep copy is to copy objects by serialization and deserialization

Java serialization is the process of converting a Java object into a sequence of bytes, and deserialization is the process of restoring a sequence of bytes to a Java object.

Specific code:

@override public Minion Clone () {ByteArrayOutputStream = new ByteArrayOutputStream(); try { ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream); objectOutputStream.writeObject(this); ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray()); ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream); return (Minion) objectInputStream.readObject(); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } return null; }Copy the code

!!!!!!!!! To be serialized objects and its member variables must implement the Serializable interface, otherwise when serialization will throw ava. IO. NotSerializableException anomalies.

The advantages of the prototype pattern

  • Hide the complexity of creating new objects
  • You can create objects of unknown type (call the Clone method directly, not caring about the object type)
  • You can quickly create an object by skipping the constructor (and sometimes, as a disadvantage, skipping the logic that the constructor executes)