The prototype design pattern is a creational design pattern that involves creating objects by cloning them from existing ones. Its purpose is related to performance as creating an object from scratch may be more expensive then copying an existing object and modifying it.
There are three approaches in scala to clone an object the first approach is using the scala cloneable interface,the second approach is using copy constructor and the third one is using the copy method of case class. The recommended approach to object copying is to create a case class and use the copy method that comes with it.
The downside of case class is it produces lot of boilerplate code which may not be required in all the scenarios so in those case we can either use the copy constructor or the scala cloneable interface. Like java scala also does a shallow copy when we use the copy method of the case class or the cloneable interface so we need to make sure we take care of those scenarios. In the example below we have a list as part of alert object which we are trying to clone i have taken care that we copy the list as a new list and not just the reference.
Lets say we have an alert object which we need to clone for further analyses without effecting the actual alert object. As the alert object is composed of list in order for the clone method on Alert to work properly, it must copy the internals of the List and we are doing the same in below code.
Using the case class copy method
case class Alert(subElementId: Int, metricMetadataId: Int, metricMetadataHourlyId: Int, metricValue: Int, elementsEffected: ArrayBuffer[Int]) { }
Driver Code
object Test extends App{ var list:ArrayBuffer[Int] = ArrayBuffer[Int](100,200,300,400) var alert1:Alert = Alert(1111,2222,3333,4444,list); var alert2:Alert =alert1.copy(elementsEffected=alert1.elementsEffected.clone()); alert2.elementsEffected +=800; println(alert1) println(alert2) }
Using the copy constructor and cloneable approach
import scala.collection.mutable.ArrayBuffer class Alert2() extends Cloneable { var subElementId: Int = 0 var metricMetadataId: Int = 0 var metricMetadataHourlyId: Int = 0 var metricValue: Int = 0; var elementsEffected: ArrayBuffer[Int] = ArrayBuffer[Int](); def this(subElementId: Int, metricMetadataId: Int, metricMetadataHourlyId: Int, metricValue: Int, elementsEffected: ArrayBuffer[Int]) { this; this.subElementId = subElementId; this.metricMetadataId = metricMetadataId; this.metricMetadataHourlyId = metricMetadataHourlyId; this.metricValue = metricValue; this.elementsEffected=elementsEffected; } //Copy Constructor Approach def this(copyAlert: Alert2) { this(copyAlert.subElementId, copyAlert.metricMetadataId, copyAlert.metricMetadataHourlyId, copyAlert.metricValue, copyAlert.elementsEffected.clone()); } //cloneable approach def myclone: Alert2 = { println("clone called"); var newAlert:Alert2 = this.clone.asInstanceOf[Alert2] newAlert.elementsEffected = this.elementsEffected.clone(); newAlert; } }
Driver Code
object Test extends App{ var list:ArrayBuffer[Int] = ArrayBuffer[Int](100,200,300,400) var alert3:Alert2 = new Alert2(1111,2222,3333,4444,list); var alert4:Alert2 = new Alert2(alert3); alert4.elementsEffected +=666; alert3.elementsEffected.foreach(print(_)) println(); alert4.elementsEffected.foreach(print(_)) var alert5:Alert2 = new Alert2(1111,2222,3333,4444,list); var alert6:Alert2 = alert5.myclone; alert6.elementsEffected +=666; alert5.elementsEffected.foreach(print(_)) println(); alert6.elementsEffected.foreach(print(_)) }