찾다
Javajava지도 시간5분 만에 마스터 프로토타입 모드


안녕하세요 여러분, 저는 Lao Tian입니다. ​​오늘은 디자인 패턴에 대해 原型模式 여러분과 공유하겠습니다. 적절한 인생 이야기와 실제 프로젝트 시나리오를 사용하여 디자인 패턴에 대해 이야기하고 마지막으로 디자인 패턴을 한 문장으로 요약합니다.

5분 만에 마스터 프로토타입 모드

Story

아직도 기억나네요. 4학년 때 일자리를 구하던 중 우연히 인터넷에서 비교적 아름다운 프로그래머 이력서 템플릿을 발견했는데, 그 후 학급 전체가 복사하기 시작했습니다. 미친듯한 이력서 (U disk) 동시에 몇몇 학생들은 자신의 과거 이력서를 내용이나 이름을 바꾸지 않고 그대로 복사해 면접관(캠퍼스 모집 면접관)에게 제출했다는 농담도 나왔다. 나중에는 모두가 결과를 추측할 수 있을 것입니다. 모두가 인턴십에 나섰고, 그들 중 일부는 여전히 일자리를 찾고 있었습니다. 회사 면접관과 다른 반 친구들의 피드백: 똑같은 이력서를 여러 장 받았습니다. 돌아와서 이야기를 나눈 후 모두가 문제가 무엇인지 알고 복사하여 제출했다고 인정했습니다. 전혀 바꾸지 않고.

이력서 사본을 두 가지 유형으로 나눕니다:

  • 하나는 이력서를 복사한 후 내용을 수정하는 방법
  • 다른 하나는 내용을 변경하지 않고 이력서를 복사하는 방법입니다.

프로토타입 패턴 정의

프로토타입 인스턴스를 사용하여 생성할 객체의 종류를 지정하고, 이 프로토타입에 대처하여 새로운 객체를 생성합니다

대략 의미: 객체 생성을 지정합니다. 프로토타입 인스턴스 유형을 사용하고 이러한 프로토타입을 복사하여 새 객체를 생성합니다.

프로토타입 모드: Prototype Pattern, 창의적인 모드입니다.

호출자는 생성 세부 정보를 알 필요가 없으며 객체를 생성하기 위해 생성자를 호출할 필요도 없습니다.

사용 시나리오

프로토타입 모드에는 다음과 같은 사용 시나리오가 있습니다.

  • 클래스 초기화는 더 많은 리소스를 소비합니다
  • new로 생성된 객체는 매우 지루한 프로세스(데이터 준비, 액세스 권한 등)가 필요합니다.
  • 생성자가 더 복잡합니다
  • 객체는 루프 본문 내에서 생성됩니다
  • Spring에서는 프로토타입 패턴이 널리 사용됩니다. 예: scope='prototype'scope='prototype'

我们可以将一些getter和setter之类封装成一个工厂方法,然后对于使用的人来说,调用方法就可以了,不需要知道里面的getter和setter是怎么处理的。我们也可以使用JDK提供的实现Cloneable接口,实现快速复制。

创建对象的四种方式:

new、反射、克隆、序列化

实际案例

大家是否有遇到过这种常见,就是项目中规定,不能把与数据库表映射的entity类返回给前端,所以通常返回给前端的有各种O,比如:XxxVO、XxxBO、XxxDTO...

这时候就会出现下面的场景,大家也想已经猜到了。

下面是与数据库表映射的UserEntity

일부 getter 및 setter를 팩토리 메서드로 캡슐화한 다음 사용자를 위해 메서드를 호출하면 됩니다. 내부의 getter와 setter가 어떻게 처리되는지 알 필요가 없습니다. JDK에서 제공되는 구현복제 가능 인터페이스로 빠른 복사가 가능합니다.

객체를 생성하는 네 가지 방법:

새로 만들기, 반사, 복제, 직렬화🎜

실제 사례

🎜이런 사람이 있나요? 일반적인 문제는 데이터베이스 테이블과 매핑된 엔터티 클래스를 프런트 엔드로 반환할 수 없다고 프로젝트에 규정되어 있으므로 일반적으로 XxxVO, XxxBO, XxxDTO...🎜와 같이 프런트 엔드로 반환되는 다양한 O가 있습니다. 🎜이 때, 다음과 같은 장면이 등장하는데 다들 짐작하셨을 겁니다. 🎜🎜다음은 데이터베이스 테이블UserEntity엔티티 클래스. 🎜<pre class='brush:php;toolbar:false;'>public class UserEntity { private Long id; private String name; private Integer age; //....可能还有很多属性 //省略getter setter }</pre>🎜UserVO 엔터티 클래스가 프런트 엔드 또는 호출자에게 반환되었습니다. 🎜<pre class='brush:php;toolbar:false;'>public class UserVO { private Long id; private String name; private Integer age; //....可能还有很多属性 //省略getter setter }</pre>🎜이때 데이터베이스에서 찾은 UserEntity를 UserVO로 변환한 후 프런트엔드(또는 호출자)에게 반환해야 합니다. 🎜<pre class='brush:php;toolbar:false;'>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; } }</pre><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 1px;margin-bottom: 1px;">从这个util类中,我们可以看出,如果一个类的属性有几十个,上百个的,这代码量是不是有点恐怖?</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 1px;margin-bottom: 1px;">于是,我们通常都会使用一些工具类来处理,比如常见有以下:</p><pre class='brush:php;toolbar:false;'>BeanUtils.copy(); JSON.parseObject() Guava工具类 .....</pre><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 1px;margin-bottom: 1px;">这些工具类就用到了原型模式。</p> <blockquote data-tool="mdnice编辑器" style="border-top: none;border-right: none;border-bottom: none;font-size: 0.9em;overflow: auto;color: rgb(106, 115, 125);padding: 10px 10px 10px 20px;margin-bottom: 20px;margin-top: 20px;border-left-color: rgb(239, 112, 96);background: rgb(255, 249, 249);"><p style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;color: black;line-height: 26px;">通过一个对象,创建一个新的对象。</p></blockquote> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 1px;margin-bottom: 1px;">也把原型模式称之为对象的拷贝、克隆。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 1px;margin-bottom: 1px;">其实对象的克隆分浅克隆和深克隆,下面我们就来聊聊浅克隆和深克隆。</p> <ul class="list-paddingleft-2" data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;"> <li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">浅克隆:创建一个新对象,新对象的属性和原来对象完全相同,对于非基本类型属性,仍指向原来对象的属性所指向的对象的内存地址。</section></li> <li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">深克隆:创建一个新对象,属性中引用的其他对象也会被克隆,不再指向原有对象地址。</section></li> </ul> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 1px;margin-bottom: 1px;">我们先来聊聊浅克隆,都喜欢由浅入深。</p> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;border-bottom: 2px solid rgb(239, 112, 96);font-size: 1.3em;"> <span style="display: none;"></span><span style="display: inline-block;background: rgb(239, 112, 96);color: rgb(255, 255, 255);padding: 3px 10px 1px;border-top-right-radius: 3px;border-top-left-radius: 3px;margin-right: 3px;">浅克隆</span><span style="display: inline-block;vertical-align: bottom;border-bottom: 36px solid #efebe9;border-right: 20px solid transparent;"> </span> </h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 1px;margin-bottom: 1px;">比如,我现在相对用户信息User进行克隆,但是User中有用户地址信息<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: " operator mono consolas monaco menlo monospace break-all rgb>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 指向的是同一个地址。

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

关系如下:

5분 만에 마스터 프로토타입 모드


深克隆

关于深克隆,我们来用一个很经典的案例,西游记里的孙悟空。一个孙悟空能变成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

结论

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

5분 만에 마스터 프로토타입 모드

总结

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

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

  • 딥 카피: 이력서를 복사한 후 이력서의 정보를 수정하여 나만의 이력서로 만드세요.
  • 얕은 카피: 이력서를 복사해도 이력서 내용이 전혀 변경되지 않습니다.

장점:

  • Java 프로토타입 모드는 메모리 바이너리 스트림 복사를 기반으로 하며 직접 새로 작성하는 것보다 성능이 좋습니다.
  • 심층 복제를 사용하여 객체 상태를 저장하고, 이전 복사본을 저장(복제)할 수 있으며, 수정 시 실행 취소 기능으로 작동할 수 있습니다.

단점:

  • 변환 중에 기존 클래스를 수정해야 하며 이는 "열기 및 닫기 원칙"을 위반합니다.
  • 객체 사이에 중첩된 참조가 여러 개 있는 경우 각 레이어를 복제해야 합니다.

프로토타입 패턴의 정의, 사용 시나리오, 실제 사례, 얕은 복제, 깊은 복제, 장점과 단점 등을 포괄적으로 설명했습니다.

위 내용은 5분 만에 마스터 프로토타입 모드의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명
이 기사는 Java后端技术全栈에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제
带你搞懂Java结构化数据处理开源库SPL带你搞懂Java结构化数据处理开源库SPLMay 24, 2022 pm 01:34 PM

本篇文章给大家带来了关于java的相关知识,其中主要介绍了关于结构化数据处理开源库SPL的相关问题,下面就一起来看一下java下理想的结构化数据处理类库,希望对大家有帮助。

Java集合框架之PriorityQueue优先级队列Java集合框架之PriorityQueue优先级队列Jun 09, 2022 am 11:47 AM

本篇文章给大家带来了关于java的相关知识,其中主要介绍了关于PriorityQueue优先级队列的相关知识,Java集合框架中提供了PriorityQueue和PriorityBlockingQueue两种类型的优先级队列,PriorityQueue是线程不安全的,PriorityBlockingQueue是线程安全的,下面一起来看一下,希望对大家有帮助。

完全掌握Java锁(图文解析)完全掌握Java锁(图文解析)Jun 14, 2022 am 11:47 AM

本篇文章给大家带来了关于java的相关知识,其中主要介绍了关于java锁的相关问题,包括了独占锁、悲观锁、乐观锁、共享锁等等内容,下面一起来看一下,希望对大家有帮助。

一起聊聊Java多线程之线程安全问题一起聊聊Java多线程之线程安全问题Apr 21, 2022 pm 06:17 PM

本篇文章给大家带来了关于java的相关知识,其中主要介绍了关于多线程的相关问题,包括了线程安装、线程加锁与线程不安全的原因、线程安全的标准类等等内容,希望对大家有帮助。

Java基础归纳之枚举Java基础归纳之枚举May 26, 2022 am 11:50 AM

本篇文章给大家带来了关于java的相关知识,其中主要介绍了关于枚举的相关问题,包括了枚举的基本操作、集合类对枚举的支持等等内容,下面一起来看一下,希望对大家有帮助。

详细解析Java的this和super关键字详细解析Java的this和super关键字Apr 30, 2022 am 09:00 AM

本篇文章给大家带来了关于Java的相关知识,其中主要介绍了关于关键字中this和super的相关问题,以及他们的一些区别,下面一起来看一下,希望对大家有帮助。

Java数据结构之AVL树详解Java数据结构之AVL树详解Jun 01, 2022 am 11:39 AM

本篇文章给大家带来了关于java的相关知识,其中主要介绍了关于平衡二叉树(AVL树)的相关知识,AVL树本质上是带了平衡功能的二叉查找树,下面一起来看一下,希望对大家有帮助。

一文掌握Java8新特性Stream流的概念和使用一文掌握Java8新特性Stream流的概念和使用Jun 23, 2022 pm 12:03 PM

本篇文章给大家带来了关于Java的相关知识,其中主要整理了Stream流的概念和使用的相关问题,包括了Stream流的概念、Stream流的获取、Stream流的常用方法等等内容,下面一起来看一下,希望对大家有帮助。

See all articles

핫 AI 도구

Undresser.AI Undress

Undresser.AI Undress

사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover

AI Clothes Remover

사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool

Undress AI Tool

무료로 이미지를 벗다

Clothoff.io

Clothoff.io

AI 옷 제거제

AI Hentai Generator

AI Hentai Generator

AI Hentai를 무료로 생성하십시오.

뜨거운 도구

SublimeText3 중국어 버전

SublimeText3 중국어 버전

중국어 버전, 사용하기 매우 쉽습니다.

PhpStorm 맥 버전

PhpStorm 맥 버전

최신(2018.2.1) 전문 PHP 통합 개발 도구

에디트플러스 중국어 크랙 버전

에디트플러스 중국어 크랙 버전

작은 크기, 구문 강조, 코드 프롬프트 기능을 지원하지 않음

맨티스BT

맨티스BT

Mantis는 제품 결함 추적을 돕기 위해 설계된 배포하기 쉬운 웹 기반 결함 추적 도구입니다. PHP, MySQL 및 웹 서버가 필요합니다. 데모 및 호스팅 서비스를 확인해 보세요.

Eclipse용 SAP NetWeaver 서버 어댑터

Eclipse용 SAP NetWeaver 서버 어댑터

Eclipse를 SAP NetWeaver 애플리케이션 서버와 통합합니다.