重庆山艺网站建设,有什么做ppt的网站,建站之星怎么免费做网站,医院网站建设政策重修设计模式-创建型-原型模式
原型模式就是利用已有对象#xff08;原型#xff09;通过拷贝方式来创建对象的模式#xff0c;达到节省对象创建时间的目的。适用于对象创建成本较大#xff0c;且同一类的不同对象之间差别不大的场景。
比如一个对象中的数据需要经过复杂…重修设计模式-创建型-原型模式
原型模式就是利用已有对象原型通过拷贝方式来创建对象的模式达到节省对象创建时间的目的。适用于对象创建成本较大且同一类的不同对象之间差别不大的场景。
比如一个对象中的数据需要经过复杂计算才能得到如排序或者对象是从网络、文件系统等通过IO读取的这种情况下就可以用原型模式快速拷贝出一个新对象来使用而不是再经过复杂计算或读取 IO 来创建对象。
原型模式的核心就是对象的拷贝且有浅拷贝和深拷贝的区别。
浅拷贝只会复制基本类型的数据和引用对象的内存地址不会递归的拷贝引用对象本身比如 Java 中 Object 的 clonse() 方法。深拷贝不仅复制基本类型的数据也会拷贝引用类型的对象会开辟新的内存空间并将新开辟的内存地址引用给新对象从而得到一份完全独立的对象。
Kotlin 中 data class 的 copy() 方法或 Java 中 Object 的 clone() 方法都是浅拷贝的实现下面验证一下
data class User(var name: String, var age: Int, val address: Address): Cloneable {public override fun clone(): Any {return super.clone()}
}data class Address(var street: String, var city: String) : Cloneable {override fun clone(): Any {return super.clone()}
}测试 copy() 和 clone() 方法
fun main(args: ArrayString) {val user User(白泽, 18, Address(浦东新区花园石桥路28弄, 上海))val userCopy user.copy()val userCopy1 user.clone() as Userprintln(user1:${user})println(user2:${userCopy})println(user address:${user.address userCopy.address}) //判断值相当于equalprintln(user address:${user.address userCopy.address}) //判断内存地址println(---)println(user1:${user})println(user2:${userCopy1})println(user address:${user.address userCopy1.address}) //判断值相当于equalprintln(user address:${user.address userCopy1.address}) //判断内存地址
}代码执行结果
user1:User(name白泽, age18, addressAddress(street浦东新区花园石桥路28弄, city上海))
user2:User(name白泽, age18, addressAddress(street浦东新区花园石桥路28弄, city上海))
user address:true
user address:true
---
user1:User(name白泽, age18, addressAddress(street浦东新区花园石桥路28弄, city上海))
user2:User(name白泽, age18, addressAddress(street浦东新区花园石桥路28弄, city上海))
user address:true
user address:true可见引用类型对象指向的内存地址还是同一个并没有真正地进行拷贝这一点通过源码也可以得到验证。将 User 编译成字节码后再反编译成 Java 语言
public final class User implements Cloneable {NotNullprivate String name;private int age;NotNullprivate final Address address;//调用的是Object的clone()方法NotNullpublic Object clone() {return super.clone();}//componentX()方法用于解构赋值语法NotNullpublic final User copy(NotNull String name, int age, NotNull Address address) {Intrinsics.checkNotNullParameter(name, name);Intrinsics.checkNotNullParameter(address, address);return new User(name, age, address);}
}public final class Address implements Cloneable {NotNullprivate String street;NotNullprivate String city;NotNullpublic Object clone() {return super.clone();}NotNullpublic final Address copy(NotNull String street, NotNull String city) {Intrinsics.checkNotNullParameter(street, street);Intrinsics.checkNotNullParameter(city, city);return new Address(street, city);}
}可以看到copy() 方法并不会对引用类型对象进行拷贝工作而是直接传入。而 clone() 方法则是由底层实现执行逻辑相同。
HotSpotIntrinsicCandidate
protected native Object clone() throws CloneNotSupportedException;下面介绍深拷贝的几种实现方式 1.深拷贝实现—自己实现Cloneable的clone方法
data class User(var name: String, var age: Int, val address: Address): Cloneable {public override fun clone(): Any {return User(name, age, address.clone() as Address)}
}data class Address(var street: String, var city: String) : Cloneable {public override fun clone(): Any {return Address(street, city)}
}打印结果
user1:User(name白泽, age18, addressAddress(street浦东新区花园石桥路28弄, city上海))
user2:User(name白泽, age18, addressAddress(street浦东新区花园石桥路28弄, city上海))
user address:true
user address:false引用地址不相同了说明是两个独立的对象这种方式比较麻烦增减字段都需要对 clone() 方法进行维护如果引用对象嵌套较深还容易出错。
2.深拷贝实现—序列化
fun main(args: ArrayString) {val user User(白泽, 18, Address(浦东新区花园石桥路28弄, 上海))val userCopy deepCopy(user) as Userprintln(user1:${user})println(user2:${userCopy})println(user address:${user.address userCopy.address}) //判断值相当于equalprintln(user address:${user.address userCopy.address}) //判断内存地址
}fun deepCopy(obj: Any?): Any {val bo ByteArrayOutputStream()val oo ObjectOutputStream(bo)oo.writeObject(obj)val bi ByteArrayInputStream(bo.toByteArray())val oi ObjectInputStream(bi)return oi.readObject()
}打印结果
user1:User(name白泽, age18, addressAddress(street浦东新区花园石桥路28弄, city上海))
user2:User(name白泽, age18, addressAddress(street浦东新区花园石桥路28弄, city上海))
user address:true
user address:false这种实现方式需要类中所有引用类型包括嵌套的都实现 Serializable 接口否则会抛出 NotSerializableException 异常。
3.深拷贝实现—Json
fun main(args: ArrayString) {val user User(白泽, 18, Address(浦东新区花园石桥路28弄, 上海))val userCopy deepCopy(user)println(user1:${user})println(user2:${userCopy})println(user address:${user.address userCopy.address}) //判断值相当于equalprintln(user address:${user.address userCopy.address}) //判断内存地址
}inline fun reified T deepCopy(data: T): T {val gson Gson()val json gson.toJson(data)return gson.fromJsonT(json, T::class.java)
}打印结果
user1:User(name白泽, age18, addressAddress(street浦东新区花园石桥路28弄, city上海))
user2:User(name白泽, age18, addressAddress(street浦东新区花园石桥路28弄, city上海))
user address:true
user address:false这里使用 Kotlin 的内联函数配合范型实化封装了 deepCopy 方法其内部是通过Google 的 Json 解析库 Gson 进行实现的先将对象转为 Json 字符串然后再解析 Json 来创建新对象。
4.深拷贝实现—写时复制思想
以上深拷贝方式由于在拷贝时创建了大量对象都会伴随性能损耗。其实可以借助 Copy On Write 思想先通过浅拷贝将对象复制出来再使用中再进行对象的创建从而将对象创建的损耗平摊到后续业务中。
当然这种方式需要看具体业务场景再决定如何实现如果应用一个复杂的模式只得到一点点的性能提升这就是所谓的过度设计得不偿失。
经典场景
理论说完了再结合实际场景来尝试使用。比如 App 的个人中心展示了用户的所有信息如用户名生日地址个性签名等同时还有保存和重置按钮其中保存按钮需要实时更新状态只有真正的修改了用户信息才可以点击否则置灰无法点击。重置按钮点击后会恢复页面原信息。
这种需求场景可以利用原型模式的思想通过拷贝得到一个新对象后续的信息修改都基于新对象并且每次改动后都和原对象进行对比判断字段值是否真正修改来更新保存按钮状态。在点击重置按钮时重新基于原对象拷贝新对象并根据该对象刷新页面实现重置功能。
总结
利用已有对象来创建新对象以达到节省创建时间的目的就叫做原型模式。
原型模式的核心是对象的拷贝对象拷贝又分为浅拷贝和深拷贝其中浅拷贝会共用引用类型对象而深拷贝会创建新的引用对象。