Home  >  Article  >  Java  >  Master Prototype Mode in Five Minutes

Master Prototype Mode in Five Minutes

Java后端技术全栈
Java后端技术全栈forward
2023-08-25 15:52:43950browse


Hello everyone, I am Lao Tian, ​​Today I will share with you the Prototype pattern## in the design pattern. #. Use appropriate life stories and real project scenarios to talk about the design pattern, and finally summarize the design pattern in one sentence.

Master Prototype Mode in Five Minutes

##Story I still remember looking for At work, I accidentally found a relatively beautiful programmer resume template from the Internet, and then the whole class started copying the resume (U disk) like crazy. At the same time, there was also a joke. Several students copied their past resumes without changing the contents or their names. They submitted them to the interviewer (campus recruitment interviewer) as the deadline. Later, everyone should be able to guess the result. Everyone went for internships, and some of them were still looking for jobs.

Feedback from the interviewer at the company and other classmates later: I received several resumes that were exactly the same. When I came back, everyone knew what the problem was after we talked about it. I admitted that I had copied it and submitted it without any changes. Harmful, embarrassing one.

There are two types of resume copies:

  • One is to copy the resume and then modify the information to your own
  • The other is to copy the resume without changing the content.

Prototype pattern definition

Specify the kinds of objects to create using a prototype instance, and create new objects by coping with this prototype

roughly meaning: Specify the types of objects to create using prototype instances, and create new objects by copying these prototypes.

Prototype pattern: Prototype Pattern, which is a creational pattern.

The caller does not need to know any creation details, nor does it need to call the constructor to create the object.

Usage scenarios

The prototype mode has the following usage scenarios:

  • Class initialization consumes more resources
  • An object generated by new requires a very cumbersome process (data preparation, access permissions, etc.)
  • The constructor is more complex
  • When a large number of objects are generated in the loop body
  • In Spring, the prototype pattern is very much used Widely, for example: scope='prototype'

We can encapsulate some getters and setters into a factory method, and then for those who use it, Just call the method, you don't need to know how the getters and setters inside are handled. We can also use the Cloneable interface provided by JDK to achieve fast copying.

Four ways to create objects:

new, reflection, cloning, serialization

Actual case

Have you ever encountered this common situation? It is stipulated in the project that the entity class mapped with the database table cannot be returned to the front end, so the following are usually returned to the front end: Various O, such as: XxxVO, XxxBO, XxxDTO...

The following scene will appear at this time, and everyone may have guessed it.

The following is the UserEntity entity class mapped to the database table.

public class UserEntity {
    private Long id;
    private String name;
    private Integer age;
    //....可能还有很多属性
    //省略getter setter
}

The UserVO entity class returned to the front end or caller.

public class UserVO {
    private Long id;
    private String name;
    private Integer age;
    //....可能还有很多属性
    //省略getter setter
}

At this time, the UserEntity found from the database needs to be converted into UserVO and then returned to the front end (or caller).

public class ObjectConvertUtil {

    public static UserVo convertUserEntityToUserVO(UserEntity userEntity) {
        if (userEntity == null) {
            return null;
        }
        UserVo userVo = new UserVo();

        userVo.setId(userEntity.getId());
        userVo.setName(userEntity.getName());
        userVo.setAge(userEntity.getAge());
         //如果还有更多属性呢?
        return userVo;
    }
}

从这个util类中,我们可以看出,如果一个类的属性有几十个,上百个的,这代码量是不是有点恐怖?

于是,我们通常都会使用一些工具类来处理,比如常见有以下:

BeanUtils.copy();
JSON.parseObject()
Guava工具类
.....

这些工具类就用到了原型模式。

通过一个对象,创建一个新的对象。

也把原型模式称之为对象的拷贝、克隆。

其实对象的克隆分浅克隆和深克隆,下面我们就来聊聊浅克隆和深克隆。

  • 浅克隆:创建一个新对象,新对象的属性和原来对象完全相同,对于非基本类型属性,仍指向原来对象的属性所指向的对象的内存地址。
  • 深克隆:创建一个新对象,属性中引用的其他对象也会被克隆,不再指向原有对象地址。

我们先来聊聊浅克隆,都喜欢由浅入深。

浅克隆

比如,我现在相对用户信息User进行克隆,但是User中有用户地址信息UserAddress属性。

以下是代码的实现:

//用户地址信息
public class UserAddress  implements Serializable{
    private String province;
    private String cityCode;

    public UserAddress(String province, String cityCode) {
        this.province = province;
        this.cityCode = cityCode;
    }
}
//用户信息
public class User implements Cloneable {
    private int age;
    private String name;
    //用户地址信息
    private UserAddress userAddress;

    //getter setter 省略

    @Override
    protected Object clone() throws CloneNotSupportedException { 
        return super.clone();
    }
}
//测试
public class UserTest {
    public static void main(String[] args) throws Exception {
        User user = new User();
        user.setAge(20);
        user.setName("田维常");
        UserAddress userAddress = new UserAddress("贵州", "梵净山");
        user.setUserAddress(userAddress);

        User clone = (User) user.clone();

        System.out.println("克隆前后UserAddress比较:" + (user.getUserAddress() == clone.getUserAddress()));
    }
}

输出结果

克隆前后 UserAddress 比较:true

两个对象属性 UserAddress 指向的是同一个地址。

这就是所谓的浅克隆,只是克隆了对象,对于该对象的非基本类型属性,仍指向原来对象的属性所指向的对象的内存地址。

关系如下:

Master Prototype Mode in Five Minutes


深克隆

关于深克隆,我们来用一个很经典的案例,西游记里的孙悟空。一个孙悟空能变成n多个孙悟空,手里都会拿着一个金箍棒。

按照前面的浅克隆,结果就是:孙悟空倒是变成很多孙悟空,但是金箍棒用的是同一根。

深克隆的结果是:孙悟空变成了很多个,金箍棒也变成很多个根。

下面我们用代码来实现:

//猴子,有身高体重和生日
public class Monkey {
    public int height;
    public int weight;
    public Date birthday;
}

孙悟空也是猴子,兵器 孙悟空有个金箍棒:

import java.io.Serializable;
//孙悟空的金箍棒
public class JinGuBang implements Serializable{
    public float  h=100;
    public float  d=10;
    //金箍棒变大
    public void big(){
        this.h *=10;
        this.d *=10;
    }
    //金箍棒变小
    public void small(){
        this.h /=10;
        this.d /=10;
    }
}

齐天大圣孙悟空:

import java.io.*;
import java.util.Date;

//孙悟空有七十二变,拔猴毛生成一个金箍棒
//使用JDK的克隆机制,
//实现Cloneable并重写clone方法
public class QiTianDaSheng extends Monkey implements Cloneable, Serializable {

    public JinGuBang jinGuBang;

    public QiTianDaSheng() {
        this.birthday = new Date();
        this.jinGuBang = new JinGuBang();
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return this.deepClone();
    }

    //深克隆
    public QiTianDaSheng deepClone() {
        try {
            //内存中操作完成、对象读写,是通过字节码直接操作
            //与序列化操作类似
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(bos);
            oos.writeObject(this);

            ByteArrayInputStream bais = new ByteArrayInputStream(bos.toByteArray());
            ObjectInputStream bis = new ObjectInputStream(bais);

            //完成一个新的对象,底层是使用new创建的一个对象
            //详情可以了解readObject方法
            QiTianDaSheng qiTianDaSheng = (QiTianDaSheng) bis.readObject();
            //每个猴子的生日不一样,所以每次拷贝的时候,把生日改一下
            qiTianDaSheng.birthday = new Date();
            return qiTianDaSheng;
        } catch (Exception ex) {
            ex.printStackTrace();
            return null;
        }
    }

    //浅克隆,就是简单的赋值
    public QiTianDaSheng shalllowClone(QiTianDaSheng target) {
        QiTianDaSheng qiTianDaSheng = new QiTianDaSheng();
        qiTianDaSheng.height = target.height;
        qiTianDaSheng.weight = target.weight;

        qiTianDaSheng.jinGuBang = target.jinGuBang;
        qiTianDaSheng.birthday = new Date();
        return qiTianDaSheng;

    }
}

接着我们就来测试一下:

public class DeepCloneTest {
    public static void main(String[] args) {
        QiTianDaSheng qiTianDaSheng = new QiTianDaSheng();
        try {
            QiTianDaSheng newObject = (QiTianDaSheng) qiTianDaSheng.clone();
            System.out.print("深克隆后 ");
            System.out.println("金箍棒是否一直:" + (qiTianDaSheng.jinGuBang == newObject.jinGuBang));
            
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        
        QiTianDaSheng newObject=qiTianDaSheng.shalllowClone(qiTianDaSheng);
        System.out.print("浅克隆后 ");
        System.out.println("金箍棒是否一直:" + (qiTianDaSheng.jinGuBang == newObject.jinGuBang));
    }
}

输出结果为:

深克隆后 金箍棒是否一直:false
浅克隆后 金箍棒是否一直:true

结论

深克隆后每个孙悟空都有自己的金箍棒,而浅克隆后每个孙悟空用的金箍棒实质上还是同一根。

Master Prototype Mode in Five Minutes

总结

切记:深和浅,指的是克隆对象里的属性(引用类型)是否指向同一个内存地址。

为了更深刻的理解深克隆和浅克隆,我们回答文中的简历拷贝的故事。

  • Deep copy: Copy a resume, and then modify the information in the resume into your own
  • ##Shallow copy: Copy a resume, resume content Completely unchanged

Advantages:

  • Java prototype mode is based on memory binary stream copy, which has better performance than direct new Better.
  • You can use deep cloning to save the object state, save an old copy (clone it), and then modify it, which can serve as an undo function.

Disadvantages:

  • needs to configure the clone method, and needs to modify existing classes during transformation, which violates the "open" principle of closure”.
  • If there are multiple nested references between objects, each layer needs to be cloned.
We have given a comprehensive explanation of the prototype pattern from the aspects of its definition, usage scenarios, real cases, shallow cloning, deep cloning, advantages and disadvantages, etc.

The above is the detailed content of Master Prototype Mode in Five Minutes. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:Java后端技术全栈. If there is any infringement, please contact admin@php.cn delete