The Problem: We could solve the Prototype problem in an easy way, but we would have to write several setters and create a converter that passes all the information from getter to setter. For example:
newContract.setId(contract.getId()); newContract.setName(contract.getName());
We can’t assign the object reference because both variables will be pointing to the same object. If I change one of the references both references will be identical.
contract = newContract; contract.setName("It's the same object reference!");
Both references will be the same value. The reference variable newContract.getName() will return “It’s the same object reference!”.
What would be a real-world situation in which the Prototype Pattern could be used? We can use it for logging, for example. In order to keep the old state and get the new state of the object, we must use the Prototype Pattern. We can’t copy the reference and use the same object. Another situation would be when we must copy information from an entity and save the same information using JPA.
So, how do you prefer doing the object copy? By manual setters? By using Reflection methods? Or using the clone method from Java API? I will show you the possible ways to solve this problem. I prefer the clone() method!
The diagram:
Get the source code:
https://github.com/rafadelnero/design-patterns-saga.git
1 – The Contract POJO: The first thing we have to realize is that this class is implementing Cloneable. The second thing is that we are overriding the clone() method. We are also changing clone() visibility to “public“.
On the clone() method, we reference the superclass cloning the object like this:
clonedContract = (Contract) super.clone();
Unfortunately, the interface Cloneable was implemented in Java 1.0 where generics didn’t exist yet. As a consequence, we need to use class casting.
Shallow Copy vs. Deep Copy: On the below example, we are using Deep Copy. This means we are making a copy with all the related objects inside the Contract class. See that we are doing it manually:
clonedContract.contractComplement = (ContractComplement) contractComplement.clone();
We are copying also the ContractComplement class. An example of Shallow Copy is pretty simple. We just have to copy the Contract class.
public class Contract implements Cloneable { private long id; private String name; private ContractComplement contractComplement; private BigDecimal price; public Contract(long id, String name, ContractComplement contractComplement, BigDecimal price) { this.id = id; this.name = name; this.contractComplement = contractComplement; this.price = price; } public Contract() { super(); } @Override protected Object clone() { Contract clonedContract; try { clonedContract = (Contract) super.clone(); clonedContract.contractComplement = (ContractComplement) contractComplement.clone(); return clonedContract; } catch (CloneNotSupportedException e) { throw new RuntimeException(e); } } // Getters and Setters omitted }
2 – The ContractComplement class: There is no new concept here. We just implemented the Cloneable interface and overrode the clone() method using the super.clone() method to clone this object.
public class ContractComplement implements Cloneable { private String complementName; private Integer specificProductPrice; public ContractComplement() { super(); } public ContractComplement(String complementName, Integer specificProductPrice) { this.complementName = complementName; this.specificProductPrice = specificProductPrice; } @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } // Getters and Setters omitted }
3 – The Unitary Test: Now we are going to check if the objects are really different and if the values are equal.
checkIfTheObjectWasClonedTest() method: Here we are testing the Prototype Pattern with the clone() method from the Java API. We are checking if the object was successfully cloned.
checkIfTheObjectWasCopiedByBeanUtilsTest: This is an alternative way to solve this problem. We can use the utility class BeanUtils from the Spring dependency. We have to add it to the pom.xml:
org.springframework
spring-beans
4.3.9.RELEASE
There are some cons to using this class:
1 – We must create getters and setters. We can’t encapsulate them.
2 – The copy is done by Reflection. It’s very slow.
3 – We must copy the objects manually.
public class PrototypeTest { // Constants omitted. @Test public void checkIfTheObjectWasClonedTest() { Contract contract = mockContract(); Contract contractPrototype = (Contract) contract.clone(); checkIfTheObjectsAreCloned(contract, contractPrototype); } @Test public void checkIfTheObjectWasCopiedByBeanUtilsTest() throws IllegalAccessException, InvocationTargetException { Contract contract = mockContract(); Contract contractToBeCopied = new Contract(); ContractComplement complementContractToBeCopied = new ContractComplement(); BeanUtils.copyProperties(contract, contractToBeCopied); BeanUtils.copyProperties(contract.getContractComplement(), complementContractToBeCopied); contractToBeCopied.setContractComplement(complementContractToBeCopied); checkIfTheObjectsAreCloned(contract, contractToBeCopied); } private Contract mockContract() { ContractComplement complement = new ContractComplement(COMPLEMENT_NAME, QTD_CUSTOMERS); Contract contract = new Contract(ID_CONTRACT, CONTRACT_NAME, complement, CONTRACT_VALUE); return contract; } private void checkIfTheObjectsAreCloned(Contract contract, Contract contractToBeCopied) { Assert.assertTrue(contract.getId() == contractToBeCopied.getId()); Assert.assertTrue(contract.getName().equals(contractToBeCopied.getName())); Assert.assertTrue(contract.getPrice().equals(contractToBeCopied.getPrice())); ContractComplement complement = contract.getContractComplement(); ContractComplement copiedComplement = contractToBeCopied.getContractComplement(); Assert.assertTrue(complement.getComplementName() .equals(copiedComplement.getComplementName())); Assert.assertTrue(complement.getSpecificProductPrice() .equals(copiedComplement.getSpecificProductPrice())); Assert.assertFalse(contract.equals(contractToBeCopied)); Assert.assertFalse(contract.getContractComplement() .equals(contractToBeCopied.getContractComplement())); } }
Summary of actions:
1 – Implemented the Cloneable interface.
2 – Overrode the clone() method.
3 – Changed the visibility of the clone() method to public.
4 – Invoked the super.clone() method.
5 – Made a copy of ContractComplement in the clone() method explicitly.
To practice the Prototype Pattern you can create another POJO, implement the Cloneable interface and make the Deep Copy! Try to use TDD (Test Driven Development). Start the development from the test.
Copy constructor is another way one may consider in the situation.
Hi, with few fields in the class certainly it would work well. If the situation that you are thinking is different, share here so we can discuss it.