前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >3分钟学设计模式(创建型):5、原型模式

3分钟学设计模式(创建型):5、原型模式

作者头像
木瓜煲鸡脚
发布2021-11-30 15:39:52
1760
发布2021-11-30 15:39:52
举报
文章被收录于专栏:Jasper小笔记Jasper小笔记

前言

设计模式,是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。它描述了在软件设计过程中的一些不断重复发生的问题,以及该问题的解决方案。也就是说,它是解决特定问题的一系列套路,是前辈们的代码设计经验的总结,具有一定的普遍性,可以反复使用。其目的是为了提高代码的可重用性、代码的可读性和代码的可靠性。

经过汇总的23种设计模式它是总结了面向对象设计当中最有价值的经验。对之前来讲可能是对其中部分设计模式还是相对来说熟悉的但仔细琢磨还是会有些疑问,正好在目前相对来说有更多的业余时间,可以来一次重新学习设计模式!

本篇内容关于原型模式。包含原型模式的设计与实现。

定义

原型模式是创建型模式的一种,其特点在于通过“复制”一个已经存在的实例来返回新的实例,而不是新建实例。被复制的实例就是我们所称的“原型”,这个原型是可定制的。——维基百科

原型模式多用于创建复杂的或者耗时的实例,因为这种情况下,复制一个已经存在的实例使程序运行更高效;或者创建值相等,只是命名不一样的同类数据。

我们需要一系列对象,这些对象都是具有相同的特征,且它们都是独立的。就像马里奥里很多蘑菇人,它们是独立的不会踩一个全部都挂掉。但它们是相同特征也就是深拷贝。

避免手动去创建相同内容的对象因为第一复杂繁琐效率不高,第二在外面真不一定能创建一模一样的对象很多属性是私有的且没有公开方法方法。

对于一个马里奥怪物类,我们可以去手动创建蘑菇人对象,也可以创建乌龟对象。但我们再创建一个蘑菇人或者一百个就是通过原型模式的方法。只有定制化创建时去手动设计,之后再需要一模一样的对象就通过原型方法获取。达到定制一个批量复制。

自定义实现

先画个结构图

首先是一个抽象接口,第二就是一个具体的原型类,去实现clone方法。具体原型类的实例通过使用clone方法就能产生一个一模一样的新对象

代码语言:javascript
复制
interface Prototype{
    Object clone();
}
代码语言:javascript
复制
class Guaiwu{
    private String name;
    private String icon;
    public Guaiwu(String name,String icon){
        this.name = name;
        this.icon = icon;
    }
    public Guaiwu(Guaiwu obj){
        this.name = obj.name;
        this.icon = obj.icon;
    }
    @Override
    public object clone(){
        return new Guaiwu(this);
    }
}

指定一个对象

代码语言:javascript
复制
Guaiwu wugui = new Guaiwu("小乌龟","乌龟样子");

批量复制

代码语言:javascript
复制
Guaiwu wugui1 = wugui.clone();
Guaiwu wugui2 = wugui.clone();
Guaiwu wugui3 = wugui.clone();
...

以上呢就是一个简单的原型模式的一个自定义实现。

Object clone方法

不过其实在Java当中已经提供了clone方法,它是一个Object的本地方法

也就是说每个对象都已经具备了clone方法,不需要像上面定义抽象标准再在每个具体原型类里面去实现。

代码语言:javascript
复制
class Noodle{
    String name;
    String size;
    public Noodle(String name,String size){
        this.name = name;
        this.size = name;
    }
}
代码语言:javascript
复制
class Client{
    public static void main(String[] args){
        // 指定原型对象
        Noodle noodle = new Noodle("长寿面","超大份");
        // 批量复制
        Noodle n1 = noodle.clone();
        Noodle n2 = noodle.clone();
        // 比较
        System.out.println(n1);
        System.out.println(noodle == n1);
        System.out.println(n1 == n2);
    }
}

结构也是符合预期的,确实n1与n2的内容是和noodle一样。且它三个确实是存在三个对象而不是一个。

但这个Object提供的clone对于我们的需要来说其实是存在问题的,它虽然是新建了对象和我们上面自己实现的clone()一样,但新建对象的属性内容是直接赋值过去。也就是说如果属性是一个非基本类型。那么clone出来的对象里的这个属性和原型的对象里的这个属性是指向同一个对象。也就是说它是浅拷贝。

Object的这个clone,其实就是和上面完全自定义写的那个一样,虽然是新建了对象n1,但n1的属性内容只是把noodle的内容挨个的赋值进去。导致非基本类型是引用相同对象而不是新对象。

我们需要的是完完全全的深拷贝

能怎么办,重写实现,针对当前原型类的属性,完善clone逻辑

代码语言:javascript
复制
// 佐料
class Spice implements Cloneable{
    String name;
    String type;
    ...
}

// 面条(原型类)
class Noodle implements Cloneable{
    String name;
    String size;
    Spice spice;
    
    @Override
    public Noodle clone(){
        // 先调用原来的clone,也就是得到属性都是直接复制到新对象
        Noode noodle = (Noodle)super.clone();
        // 再把spice复制个新对象,设置进去
        noodle.spice = this.spice.clone();
        return noodle;
    }
}

这样就完成了clone方法对当前Noodle类型对象是完全拷贝

要注意的是访问clone方法是protected因此只能在java.lang访问。因此在外部非子类访问需要重写(开头的代码没有写所以提示下),且重写必须得实现Cloneable接口,说明如下:

这样写出来就比较复杂,为了进行深拷贝里面有复杂类型的属性,而属性里面可能还牵连很多其他的非基本类型。导致在原型对象类里面不停的嵌套去实现直到将所有属性的值都创建新对象。对于复杂类型复杂的组合依赖关系,上面的clone方法的实现就会复杂很多,要递归到只有基本类型为止,从最底层一个一个对象进行创建

序列化

不过还好有序列化,可以利用序列化的方式来达到深拷贝

代码语言:javascript
复制
@Override
public Object clone() {
    try{
        // 写出
        ByteArrayOutputStream aos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(aos);
        oos.writeObject(this);
        // 读取
        ByteArrayInputStream ais = new ByteArrayInputStream(aos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(ais);
        return ois.readObject();
    }catch (Exception e){
        e.printStackTrace();
    }
    return null;
}

最终还是可以一步到位的,里面的嵌套的非基本类型的属性,也都是新对象。使用序列化的方式就避免了手动去递归挖掘所有关联非基本类型的属性,进行新建拷贝内容再赋值的过程。

但怎么说还是得改写所有关联的属性的类以及下面不停嵌套的类,都要去实现序列化接口。只是对于clone方法的重写可以一步到位。

总结

相对之前来说这个原型模式思想是比较简单的,它可能会用在需要复用很多对象,且要求保证独立安全。优点在于:第一客户代码可以直接通过模板对象调用clone就能得到新对象,免去对使用类的了解程度的门槛。第二有些情况下就是需要进行复制,在这样的情况下也就比直接创建效率要高。缺点也就是在于类的成员属性的类型的复杂程度,这时候复制的逻辑就比较复杂,或者说需要很注意。但无论怎么说其实都要去修改代码,不符合开闭原则。还得看具体场景吧实际使用的不多。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-11-11,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 IT那个小笔记 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 定义
  • Object clone方法
  • 序列化
  • 总结
相关产品与服务
文件存储
文件存储(Cloud File Storage,CFS)为您提供安全可靠、可扩展的共享文件存储服务。文件存储可与腾讯云服务器、容器服务、批量计算等服务搭配使用,为多个计算节点提供容量和性能可弹性扩展的高性能共享存储。腾讯云文件存储的管理界面简单、易使用,可实现对现有应用的无缝集成;按实际用量付费,为您节约成本,简化 IT 运维工作。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档