设计模式 - 原型模式,就该这样学!

 

目录

开始

为什么要引入原型模式

原型模式概述

原型模式代码实现(浅拷贝)

浅拷贝和深拷贝的区别

原型模式代码实现(深拷贝)

方式一:直接 copy

方式二:序列化和反序列化(推荐)


开始


为什么要引入原型模式

a)问题:现在有一个小猪猪,名字为 lyj,年龄为:3,颜色为:粉色,请编写程序创建和 lyj 猪猪 属性完全相同的 10 头猪猪.

b)传统的处理方式如下:

基于 lyj 这个猪猪,直接 new 出 10 只一样的猪猪.

data class Pig(
    val name: String,
    val age: Int,
    val color: String,
)

fun main() {
    val pig = Pig("lyj", 3, "粉色")

    val p1 = Pig(pig.name, pig.age, pig.color)
    val p2 = Pig(pig.name, pig.age, pig.color)
    val p3 = Pig(pig.name, pig.age, pig.color)
    val p4 = Pig(pig.name, pig.age, pig.color)
    //...
    
    println(p1)
    println(p2)
    println(p3)
    println(p4)
    //...

}

c)传统方式创建的优缺点:

优点:

  • 好理解,操作简单.

缺点:

  • 创建新的对象时,总是需要重新获取原始对象的属性,如果创建的对象比较复杂时,效率比较低(可能含有大量的计算操作).
  • 总是需要重新初始化对象,而不是动态的获取对象运行时的状态,不够灵活.

原型模式概述

原型模式(Prototype Pattern)是一种创建型的设计模式,运行一个对象通过复制自身的实例来创建新的对象.   简而言之,提供了一个快速创建新对象的方法,这个新对象是已有对象的精确复制品(根据具体的业务场景,决定是深拷贝 还是 浅拷贝).

优点:

  • 简化对象创建:复制复杂对象时,可以通过原型模式简化对象的复制过程,并且可以动态的配置对象状态.
  • 性能提升:同时提升了对象创建的速度(某些情况下,复制现有对象比创建新对象更节省资源,例如大型对象创建可能涉及到大量的计算 或者 I/O 操作,原型模式可以避免这些重复初始化的资源开销,直接复制即可).
  • 解耦合,高扩展:如果原始对象发生变化(增加或者减少属性),其他克隆对象也会发生响应的变化,无需修改代码.

缺点:

  • 由于要对被复制的类配备一个克隆方法,因此如果是对老代码的改造,就需要修改代码,违背了开闭原则.

角色如下:

  • 原型类(Prototype):原型类,声明一个克隆自己的接口.
  • 具体的原型类(ConcretePrototype):实现一个克隆自己的操作.
  • 客户端(Cline):让一个原型对象克隆自己,从而创建一个新的对象(属性一样).

原型模式代码实现(浅拷贝)

a)猪猪类先实现克隆接口

data class Pig(
    val name: String,
    val age: Int,
    val color: String,
) {

    /**
     * Kotlin 的数据类中默认提供了 copy 方法用于浅拷贝
     */
    fun clone(): Any {
        return this.copy()
    }

}

b)客户端

fun main() {
    val pig = Pig("lyj", 3, "pink")

    val p1 = pig.clone()
    val p2 = pig.clone()
    val p3 = pig.clone()
    //...

    println("$p1, ${p1.hashCode()}")
    println("$p2, ${p2.hashCode()}")
    println("$p3, ${p3.hashCode()}")
    //...
}

c)运行结果如下:

 

d)注意:

kotlin 的数据类默认提供了 copy() 方法,用于实现对这个对象的 浅拷贝,但如果在 copy 时传入其他参数,就会为当前对象创建新对象,但是对内的引用变量还是传入地址(Pig 的 hashCode 不一致,单 child 的hashCode 一致)

例如,复制当前对象并修改 name

data class Pig(
    var name: String,
    val age: Int,
    val color: String,
    val child: PigChild
) {

    /**
     * kotlin 的数据类默认提供了 copy() 方法,用于实现对这个对象的 浅拷贝
     * 但如果在 copy 时传入其他参数,就会为当前对象创建新对象,但是对内的引用变量还是传入地址(Pig 的 hashCode 不一致,单 child 的hashCode 一致)
     * 例如,复制当前对象并修改 name
     */
    fun copyWithId(name: String): Pig {
        return this.copy(name = name)
    }

}

data class PigChild (
    var name: String,
)

fun main() {
    val p = Pig("lyj", 1, "pink", PigChild("aaa"))

    val p2 = p.copy(name = "ccc")

    println("${p.hashCode()}, ${p.child.hashCode()}")
    println("${p2.hashCode()}, ${p2.child.hashCode()}")
}

执行如下: 

 

 

浅拷贝和深拷贝的区别

成员变量中有 基本数据类型 和 引用数据类型.

a)浅拷贝

基本数据类型:复制 值 ,给新开辟空间的成员变量.

引用数据类型:复制 地址,给新开辟空间的成员变量.

b)深拷贝

基本数据类型:复制 值 ,给新开辟空间的成员变量.

引用数据类型:复制 该引用对象中所有的成员变量所引用的对象,直到所有可达的对象.  也就是对整个对象进行拷贝.

c)可以这样理解,浅拷贝只是对最外层的对象进行了 1 层拷贝,而深拷贝则是对象中无论有多少嵌套对象,都会进行拷贝(进行了 n 层拷贝).

原型模式代码实现(深拷贝)

方式一:直接 copy

实际上就是对成员变量中的所有 引用类型 都使用 copy 进行一遍处理.

data class Child(val name: String)

data class Parent(val name: String, val child: Child) {
    fun deepCopy(): Parent {
        return this.copy(child = this.child.copy())
    }
}

fun main() {
    val p = Parent("John", Child("New York"))

    val p1 = p.deepCopy()

    println(p == p1) //true
    println(p === p1) //false
    println(p.child === p1.child) //false
}

 

但是这种方式的缺点就是,将来如果 Parent 中有很多个成员变量都是引用对象 ,将来必须要一个个都进行 copy.

Ps:kotlin 中 == 表示比较两个对象的内容是否相等.  === 表示两个引用是否指向同一个对象.

方式二:序列化和反序列化(推荐)

推荐使用这种方式.

如果类中有多个引用对象,就不需要一个个处理了.

data class Child(
    val name: String
): Serializable

data class Parent(
    val name: String,
    val child: Child
): Serializable {

    fun deepCopy(): Parent {
        //序列化
        val bo = ByteArrayOutputStream()
        ObjectOutputStream(bo).use { it.writeObject(this) }
        //反序列化
        val bi = ByteArrayInputStream(bo.toByteArray())
        return ObjectInputStream(bi).use { it.readObject() as Parent }
    }

}

fun main() {
    val p = Parent("John", Child("New York"))

    val p1 = p.deepCopy()

    println(p == p1) //true
    println(p === p1) //false
    println(p.child === p1.child) //false
}

  • 21
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

陈亦康

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值