5분 만에 마스터 프로토타입 모드
안녕하세요 여러분, 저는 Lao Tian입니다. 오늘은 디자인 패턴에 대해 原型模式
여러분과 공유하겠습니다. 적절한 인생 이야기와 실제 프로젝트 시나리오를 사용하여 디자인 패턴에 대해 이야기하고 마지막으로 디자인 패턴을 한 문장으로 요약합니다.
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 id="span-style-display-none-span-span-style-display-inline-block-background-rgb-color-rgb-padding-px-px-px-border-top-right-radius-px-border-top-left-radius-px-margin-right-px-浅克隆-span-span-style-display-inline-block-vertical-align-bottom-border-bottom-px-solid-efebe-border-right-px-solid-transparent-span"><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
指向的是同一个地址。
这就是所谓的浅克隆,只是克隆了对象,对于该对象的非基本类型属性,仍指向原来对象的属性所指向的对象的内存地址。
关系如下:
深克隆
关于深克隆,我们来用一个很经典的案例,西游记里的孙悟空。一个孙悟空能变成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
结论
深克隆后每个孙悟空都有自己的金箍棒,而浅克隆后每个孙悟空用的金箍棒实质上还是同一根。
总结
切记:深和浅,指的是克隆对象里的属性(引用类型)是否指向同一个内存地址。
为了更深刻的理解深克隆和浅克隆,我们回答文中的简历拷贝的故事。
딥 카피: 이력서를 복사한 후 이력서의 정보를 수정하여 나만의 이력서로 만드세요. 얕은 카피: 이력서를 복사해도 이력서 내용이 전혀 변경되지 않습니다.
장점:
-
Java 프로토타입 모드는 메모리 바이너리 스트림 복사를 기반으로 하며 직접 새로 작성하는 것보다 성능이 좋습니다. 심층 복제를 사용하여 객체 상태를 저장하고, 이전 복사본을 저장(복제)할 수 있으며, 수정 시 실행 취소 기능으로 작동할 수 있습니다.
단점:
변환 중에 기존 클래스를 수정해야 하며 이는 "열기 및 닫기 원칙"을 위반합니다. 객체 사이에 중첩된 참조가 여러 개 있는 경우 각 레이어를 복제해야 합니다.
프로토타입 패턴의 정의, 사용 시나리오, 실제 사례, 얕은 복제, 깊은 복제, 장점과 단점 등을 포괄적으로 설명했습니다.
위 내용은 5분 만에 마스터 프로토타입 모드의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

핫 AI 도구

Undress AI Tool
무료로 이미지를 벗다

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

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

Stock Market GPT
더 현명한 결정을 위한 AI 기반 투자 연구

인기 기사

뜨거운 도구

메모장++7.3.1
사용하기 쉬운 무료 코드 편집기

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

스튜디오 13.0.1 보내기
강력한 PHP 통합 개발 환경

드림위버 CS6
시각적 웹 개발 도구

SublimeText3 Mac 버전
신 수준의 코드 편집 소프트웨어(SublimeText3)

옵션 클래스는 널 포인터 예외를 피하기 위해 무효가 될 수있는 값을 안전하게 처리하는 데 사용됩니다. 1. 옵션을 사용하여 인스턴스를 만듭니다. ofnullable null 값을 처리합니다. 2. 예외를 유발하기 위해 직접 호출을 피하기 위해 Ispresent 또는 Ifpresent Security를 통해 값을 점검하고 액세스하십시오. 3. Orelse 및 Orelseget을 사용하여 기본값을 제공하거나 OrelsetHrow를 사용하여 사용자 정의 예외를 던집니다. 4. 맵 및 필터 체인 작업을 통해 값을 변환하거나 필터링하여 코드 가독성 및 견고성을 향상시킵니다.

getClass () 메소드를 사용하여 class 객체를 반환하려면 str.getClass ()와 같은 객체의 런타임 클래스를 가져옵니다. 유형의 경우 String.class 구문을 직접 사용할 수 있습니다. 클래스 클래스는 getName (), getSimplename ()과 같은 메소드를 제공하여 Num.getClass (). getSimplename ()과 같은 클래스 정보를 얻기 위해 클래스 정보를 얻습니다.

atwo-dimensionalarrayinjavaisanarrayofarrays, withtwobrackets, likeint [] [] matrix, andcanbeinitializedwithValuesOrusingNew; int [] [] matrix = {{1,2}, {3,4}};

thecurrentworkingdirectoryinjavacanbeobtiredusingsystem.getProperty ( "user.dir"), whithebsolutepathwheretheprogramwaslaunched; onatially, paths.get ( "). toabsolutepath (). tostring ()

AsingletonclassinjavaensuresonlyOneNstance athroughoutanApplication 'slifecycleByUsingAprivateConstructor, aprivatestaticInstance, andapublicstaticgetInstance () 방법; 일반적인 시상법, 게으른 사파 펠라 즈

genericsinjavaprovidecompile-timetypesafetyandeliminatenateneateThinetEdEneDeTeneDeTeneDeDeMineDeTeneDeDeMineDeDeMineDeDeMined-XLASSES, 인터페이스 및 METHODSOPRENTONTYPEPARAMETERS; FOREXALLES, usingListEnsuresOnlyStringsCanBeadded, regineclasscastextions; generticsworkviAtamete를 예방합니다

ToimplementacustomComparatorinJava,createaclassorlambdathatoverridesthecomparemethodtodefinesortinglogic.Forexample,withaPersonclasshavingnameandagefields,defineAgeComparatorimplementingComparatorandoverridecomparetosortbyageusingInteger.compare(p1.a

Try-Catch 블록을 사용하면 프로그램 충돌과 예외가 우아하게 처리되는 것을 방지합니다. 가능한 오류 코드를 시도 블록에 넣고 ArithmeticeXception 또는 ArrayinDexoutOfBoundSexception과 같은 CATC를 사용하여 특정 예외를 포착하십시오. 다중 캐치 블록을 사용하여 특정 예외가 선호되도록 다른 예외를 순서대로 처리 할 수 있습니다. 마지막으로 블록은 정리 작업을 수행하는 데 사용되며, 예외가 발생하는지 여부에 관계없이 실행되며 리소스를 자유롭게하는 데 적합합니다. 자동 클로즈블을 구현하는 리소스의 경우 자동으로 리소스를 닫고 누출을 피할 수있는 Try-With-Resources 구문을 사용하는 것이 좋습니다. 이러한 메커니즘의 합리적인 사용은 프로그램 안정성을 향상시키고
