안녕하세요 여러분, 저는 Lao Tian입니다. 오늘은 디자인 패턴에 대해 原型模式
여러분과 공유하겠습니다. 적절한 인생 이야기와 실제 프로젝트 시나리오를 사용하여 디자인 패턴에 대해 이야기하고 마지막으로 디자인 패턴을 한 문장으로 요약합니다.
아직도 기억나네요. 4학년 때 일자리를 구하던 중 우연히 인터넷에서 비교적 아름다운 프로그래머 이력서 템플릿을 발견했는데, 그 후 학급 전체가 복사하기 시작했습니다. 미친듯한 이력서 (U disk) 동시에 몇몇 학생들은 자신의 과거 이력서를 내용이나 이름을 바꾸지 않고 그대로 복사해 면접관(캠퍼스 모집 면접관)에게 제출했다는 농담도 나왔다. 나중에는 모두가 결과를 추측할 수 있을 것입니다. 모두가 인턴십에 나섰고, 그들 중 일부는 여전히 일자리를 찾고 있었습니다. 회사 면접관과 다른 반 친구들의 피드백: 똑같은 이력서를 여러 장 받았습니다. 돌아와서 이야기를 나눈 후 모두가 문제가 무엇인지 알고 복사하여 제출했다고 인정했습니다. 전혀 바꾸지 않고.
이력서 사본을 두 가지 유형으로 나눕니다:
프로토타입 인스턴스를 사용하여 생성할 객체의 종류를 지정하고, 이 프로토타입에 대처하여 새로운 객체를 생성합니다
대략 의미: 객체 생성을 지정합니다. 프로토타입 인스턴스 유형을 사용하고 이러한 프로토타입을 복사하여 새 객체를 생성합니다.
프로토타입 모드: Prototype Pattern
, 창의적인 모드입니다.
호출자는 생성 세부 정보를 알 필요가 없으며 객체를 생성하기 위해 생성자를 호출할 필요도 없습니다.
프로토타입 모드에는 다음과 같은 사용 시나리오가 있습니다.
scope='prototype'
scope='prototype'
我们可以将一些getter和setter之类封装成一个工厂方法,然后对于使用的人来说,调用方法就可以了,不需要知道里面的getter和setter是怎么处理的。我们也可以使用JDK
提供的实现Cloneable
接口,实现快速复制。
创建对象的四种方式:
new、反射、克隆、序列化
大家是否有遇到过这种常见,就是项目中规定,不能把与数据库表映射的entity类返回给前端,所以通常返回给前端的有各种O,比如:XxxVO、XxxBO、XxxDTO...
这时候就会出现下面的场景,大家也想已经猜到了。
下面是与数据库表映射的UserEntity
일부 getter 및 setter를 팩토리 메서드로 캡슐화한 다음 사용자를 위해 메서드를 호출하면 됩니다. 내부의 getter와 setter가 어떻게 처리되는지 알 필요가 없습니다. JDK에서 제공되는 구현
복제 가능 인터페이스로 빠른 복사가 가능합니다.
객체를 생성하는 네 가지 방법:
새로 만들기, 반사, 복제, 직렬화🎜
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
指向的是同一个地址。
这就是所谓的浅克隆,只是克隆了对象,对于该对象的非基本类型属性,仍指向原来对象的属性所指向的对象的内存地址。
关系如下:
关于深克隆,我们来用一个很经典的案例,西游记里的孙悟空。一个孙悟空能变成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분 만에 마스터 프로토타입 모드의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!