> Java > java지도 시간 > 본문

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

풀어 주다: 2023-08-25 15:52:43
앞으로
1055명이 탐색했습니다.


안녕하세요 여러분, 저는 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엔티티 클래스. 🎜<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">public class UserEntity { private Long id; private String name; private Integer age; //....可能还有很多属性 //省略getter setter }</pre><div class="contentsignin">로그인 후 복사</div></div>🎜UserVO 엔터티 클래스가 프런트 엔드 또는 호출자에게 반환되었습니다. 🎜<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">public class UserVO { private Long id; private String name; private Integer age; //....可能还有很多属性 //省略getter setter }</pre><div class="contentsignin">로그인 후 복사</div></div>🎜이때 데이터베이스에서 찾은 UserEntity를 UserVO로 변환한 후 프런트엔드(또는 호출자)에게 반환해야 합니다. 🎜<div class="code" style="position:relative; padding:0px; margin:0px;"><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><div class="contentsignin">로그인 후 복사</div></div><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><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">BeanUtils.copy(); JSON.parseObject() Guava工具类 .....</pre><div class="contentsignin">로그인 후 복사</div></div><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;word-break: break-all;color: rgb(239, 112, 96);">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으로 문의하세요.
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿