胡桃仁

虚度光阴27 载

原型模式(PROTOTYPE)—-对象创建型模式

1、介绍

1.1、意图

用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。

通过实例及其clone 方法快速创建该实例对应类型的对象。

2、clone 对象

2.1、通过clone 新建对象

php 实现简单的克隆

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
class Student
{
public $name = "zhang";
public $teacher;

$s1 = new Student();
$s2 = clone $s1;
var_dump($s2);

#### 结果 ####
object(Student)[2]
public 'name' => string 'zhang' (length=5)
public 'teacher' => null

浅复制(shallow copy)

php

当对象被复制后,PHP 5 会对对象的属性执行一个潜复制(shallow copy)。所有的属性仍然会是一个指向原来的变量的引用。
– (对象复制)[https://www.php.net/manual/zh/language.oop5.cloning.php]

意思就是:假如$s1teacher 属性是一个对象——$teacher1,$s2teacher 属性也是$teacher1(是$teacher1 的引用,$s1 中的 teacher 属性$teacher1 改变时,$s2 的 teacher 也会随之改变)。而对于是基本数据类型的成员变量,浅拷贝直接进行值传递,也就是把该属性复制一份给了新的对象。

要证明一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<?php
class Student
{
public $name = "zhang";
public $teacher;

class Teacher
{
public $name;
public function __construct(string $name)
{
$this->name = $name;
}
}

$s1 = new Student();
$teacher1 = new Teacher("李老师");
$s1->teacher = $teacher1; // $s1 引用了 $teacher1 作为了自己的属性
$s2 = clone $s1;
// 现在改变$teacher1 的name 为“赵老师”
$teacher1->name = "赵老师";
var_dump($s2);

#### 结果 ####
/home/vagrant/code/test-php/prototype.php:34:
object(Student)[3]
public 'name' => string 'zhang' (length=5)
public 'teacher' =>
object(Teacher)[2]
public 'name' => string '赵老师' (length=9)

最后结果:$s2 的老师是赵老师而不是李老师。

java 浅拷贝

setter、getter 不要,占用篇幅

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
class Teacher {
public String name;

public Teacher(String name) {
this.name = name;
}

@Override
public String toString() {
return "Teacher{" +
"name='" + name + '\'' +
'}';
}
}

class Student implements Cloneable {
public String name;
public Teacher teacher;

public Student(String name) {
this.name = name;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", teacher=" + teacher +
'}';
}
@Override
protected Object clone() throws CloneNotSupportedException {
Student s = null;
try {
s = (Student) super.clone(); // 向下转型必须强制类型转换
} catch (Exception e) {
System.out.println(e.getMessage());
}
return s;
}
}

public class Client {
public static void main(String[] args) throws CloneNotSupportedException {
Teacher teacher1 = new Teacher("李老师");
Student s1 = new Student("zhang");
s1.teacher = teacher1;
Student s2 = (Student) s1.clone();
// ##################### 看结果 ######################
System.out.println("teacher1 改变名字前的s2:" + s2);
teacher1.name = "赵老师";
System.out.println("teacher1 改变名字后的s2:" + s2);
}
}

跟php 的浅复制一样的意思,s2 克隆s1 后,s2 的teacher 属性是teacher1 对象的引用,teacher1 改变,s2 的teacher 属性也随着改变。

深复制

解决浅复制里引用类型的属性没有重新创建引用到的类型的对象的问题。
其实就是让引用类型的属性和常用数据类型的属性一样。

介绍
为所有引用数据类型的成员变量申请存储空间,并复制每个引用数据类型成员变量所引用的对象,知道该对象可达的所有对象。也就是对整个对象进行拷贝。

两种方法

  • 重写clone方法来实现深拷贝。
  • 通过对象序列化实现深拷贝
php 深复制
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
class Student
{
public $name = "zhang";
public $teacher;
// public function __clone()
// {
// $this->obj = strrev($this->obj);
// }
public function __clone()
{
$this->teacher = clone $this->teacher;
}
}

class Teacher
{
public $name;
public function __construct(string $name)
{
$this->name = $name;
}
}

$s1 = new Student();
$s1->teacher = new Teacher("李老师");
// 重写clone 实现
$s2 = clone $s1;
// php 序列化实现
$s3 = unserialize(serialize($s1));
var_dump(spl_object_hash($s3->teacher));
// ################# 比较指定对象的hash id ######################
var_dump(spl_object_hash($s1->teacher)); //'000000001a6ab6410000000018bd2100'
var_dump(spl_object_hash($s2->teacher)); //'000000001a6ab6470000000018bd2100'
var_dump(spl_object_hash($s3->teacher)); //‘000000001a6ab6450000000018bd2100’

$s2和$s3 的teacher 属性的hash id 跟$s1 的都不一样,深拷贝没毛病

java 深拷贝
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
import java.io.*;

class Teacher implements Cloneable, Serializable {
private static final long serialVersionUID = 1L;
public String name;

public Teacher(String name) {
this.name = name;
}

@Override
public String toString() {
return "Teacher{" +
"name='" + name + '\'' +
'}';
}

// Teacher 类的所有属性只有String 类型,所以直接默认覆写即可
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}

class Student implements Cloneable, Serializable {
public String name;
public Teacher teacher;

public Student(String name) {
this.name = name;
}

@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", teacher=" + teacher +
'}';
}

@Override
protected Object clone() throws CloneNotSupportedException {
Object deep = null;
// 这里完成对基本数据类型(属性)和String 的克隆
deep = super.clone();
// 对引用类型的属性,进行单独处理
Student student = (Student) deep;
student.teacher = (Teacher) teacher.clone();
return student;
}

public Object deepClone() {
ByteArrayOutputStream bos = null;
ObjectOutputStream oos = null;
ByteArrayInputStream bis = null;
ObjectInputStream ois = null;

try {
// 序列化
bos = new ByteArrayOutputStream();
oos = new ObjectOutputStream(bos);
oos.writeObject(this);
// 反序列化
bis = new ByteArrayInputStream(bos.toByteArray());
ois = new ObjectInputStream(bis);
return (Student) ois.readObject();
} catch (Exception e) {
e.printStackTrace();
return null;
} finally {
// 关闭流
try {
bos.close();
bis.close();
ois.close();
oos.close();
} catch (Exception e2) {
System.out.println(e2.getMessage());
}
}
}
}

public class DeepClone {
public static void main(String[] args) throws CloneNotSupportedException {
Student s1 = new Student("zhang");
s1.teacher = new Teacher("李老师");

// ################ 测试 #####################
// clone 方法复制
Student s2 = (Student) s1.clone();
// 通过hashCode 比较两个对象的teacher 属性
System.out.println("s1 的属性teacher 的hashCode:" + s1.teacher.hashCode());
System.out.println("s2 的属性teacher 的hashCode:" + s2.teacher.hashCode());
// 序列化 比较
Student s3 = (Student) s1.deepClone();
// 通过hashCode 比较两个对象的teacher 属性
System.out.println("s1 的属性teacher 的hashCode:" + s1.teacher.hashCode());
System.out.println("s3 的属性teacher 的hashCode:" + s3.teacher.hashCode());
}
}

总结

评论