Prototype mode
Prototype pattern solves the problem of copying a large number of attributes.
This paper will introduce several depth attribute replication methods and several shallow attribute replication methods.
The source code address of this paper will be given at the end of the text.
Code before prototype pattern is not used
public static void testCloneStupid() { PrototypePerson person = new PrototypePerson() .setId("10001") .setAge(18) .setName("First sight") .setHobbies(CollUtil.newArrayList("Suckling", "sleep")); // Manually copy attributes PrototypePerson clonePerson = new PrototypePerson(); clonePerson.setName(person.getName()); clonePerson.setAge(person.getAge()); clonePerson.setId(person.getId()); // .... There are many more attributes to copy clonePerson.setHobbies(person.getHobbies()); clonePerson.getHobbies().add("Beat beans"); System.out.println("person:" + person.toString()); System.out.println("clonePerson:" + clonePerson.toString()); }
The code is standardized and neat. What's the problem? Imagine, what if there are 100 + attributes that need to be assigned? The prototype pattern solves the problem caused by such a large number of attribute replication.
Code after prototype mode transformation
public class PrototypePerson implements Cloneable, Serializable { private String id; private String name; private Integer age; private List<String> hobbies; @Override public PrototypePerson clone() { try { // The clone of jdk must implement Cloneable and serializable interfaces return (PrototypePerson) super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); return null; } } }
Caller usage code
public static void testJdkClone() { PrototypePerson person = new PrototypePerson() .setId("10001") .setAge(18) .setName("First sight") .setHobbies(CollUtil.newArrayList("Suckling", "sleep")); // jdk's own clone PrototypePerson clonePerson = person.clone(); clonePerson.getHobbies().add("Beat beans"); System.out.println("person:" + person.toString()); System.out.println("clonePerson:" + clonePerson.toString()); }
The output is as follows:
Using the prototype pattern greatly simplifies the caller's code. Write the implementation details into the entity class.
However, the problem is that when copying the attributes of types, references are copied, so when modifying person's hobbies, the attributes of clonePerson will be modified synchronously.
Deep and shallow replication
Shallow copy: when copying the attributes of an object type, the reference of the object is copied (for example, the clone of jdk is a shallow copy).
Deep copy: completely copy the properties of the source object, including the properties of the object type.
Byte stream serialization method of deep replication
public static <T> T deepClone(T obj) { if (null == obj) return null; try { ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(obj); ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bis); Object o = ois.readObject(); return (T) o; } catch (Exception e) { throw new RuntimeException(e); } }
Calling code:
public static void testDeepClone() { PrototypePerson person = new PrototypePerson() .setId("10001") .setAge(18) .setName("First sight") .setHobbies(CollUtil.newArrayList("Suckling", "sleep")); PrototypePerson clonePerson = PrototypeUtils.deepClone(person); clonePerson.getHobbies().add("Beat beans"); System.out.println("person:" + person.toString()); System.out.println("clonePerson:" + clonePerson.toString()); }
Output result:
Byte stream serialization and deep replication are commonly used with high performance.
Json serialization method of deep replication
public static <T> T deepCloneByJson(T obj) { String jsonString = JSON.toJSONString(obj); Object object = JSON.parseObject(jsonString, obj.getClass()); return (T) object; }
Json serialization deep replication method is simple to code, and Xiaobai can remember it~
The test code is not given here, and the output result is the same as above.
Shallow replication of BeanUtils
// Spring's BeanUtils is used here // import org.springframework.beans.BeanUtils; public static void testBeanUtilsClone() { PrototypePerson person = new PrototypePerson() .setId("10001") .setAge(18) .setName("First sight") .setHobbies(CollUtil.newArrayList("Suckling", "sleep")); PrototypePerson clonePerson = new PrototypePerson(); // With the help of BeanUtils auxiliary properties BeanUtils.copyProperties(person, clonePerson); clonePerson.getHobbies().add("Beat beans"); System.out.println("person:" + person.toString()); System.out.println("clonePerson:" + clonePerson.toString()); }
Shallow copy JDK object Clone() mode
public class PrototypePerson implements Cloneable, Serializable { private String id; // .... @Override public PrototypePerson clone() { try { // Shallow replication using Jdk return (PrototypePerson) super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); return null; } } }
Special account
- Not all prototype patterns need to be deeply copied. Look at the actual business scenario.
- The original intention of prototype pattern is to reduce the stupid operation of manually copying a large number of attributes.
- The standard prototype pattern requires objects to implement Cloneable and serializable interfaces.
- Suggested method: the object implements the Serializable interface (instead of the clonable interface), and provides the cloning tool class: PrototypeUtils. The caller uses PrototypeUtils for attribute replication, which determines whether to copy shallow or deep.
PrototypeUtils tool class code
import com.alibaba.fastjson.JSON; import org.springframework.beans.BeanUtils; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; /** * @description: Prototype pattern tool class * @author: chujian * @since: 2022-02-19 16:23 **/ @SuppressWarnings("unchecked") public class PrototypeUtils { /** * Shallow cloning * * @param obj * @param <T> * @return */ public static <T> T clone(T obj) { if (null == obj) return null; Object newInstance = null; try { newInstance = obj.getClass().getConstructor().newInstance(); BeanUtils.copyProperties(obj, newInstance); return (T) newInstance; } catch (Exception e) { throw new RuntimeException(e); } } /** * Deep cloning * * @param obj * @param <T> * @return */ public static <T> T deepClone(T obj) { if (null == obj) return null; try { ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(obj); ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bis); Object o = ois.readObject(); return (T) o; } catch (Exception e) { throw new RuntimeException(e); } } /** * Deep cloning through Json serialization * * @param obj * @param <T> * @return */ public static <T> T deepCloneByJson(T obj) { String jsonString = JSON.toJSONString(obj); Object object = JSON.parseObject(jsonString, obj.getClass()); return (T) object; } }