文档章节

小鸟的名片 - 原型模式

 草帽行者
发布于 2016/05/16 10:37
字数 2191
阅读 79
收藏 1
点赞 2
评论 0

#设计模式 - 原型模式

[toc]

简介

Prototype

原型模式:调用object.clone()方法,将对象复制一份,产生一个新的对象,和原有对象一样,然后再修改细节的数据,而不通过new 关键字来产生一个对象,而是通过这种复制的方式产生对象的实现模式叫原型模式.

  • 类型
    • 浅拷贝
    • 深拷贝

组成角色

  • 抽象原型(Prototype)角色:这是一个抽象角色,通常由一个Java接口或Java抽象类实现。此角色给出所有的具体原型类所需的接口。
  • 具体原型(Concrete Prototype)角色:被复制的对象。此角色需要实现抽象的原型角色所要求的接口。
  • 客户(Client)角色:客户类提出创建对象的请求。

浅拷贝 与 深拷贝

浅拷贝

浅拷贝:只拷贝基本的数据类型,对于数组、容器对象、引用对象都不会拷贝,只是创建对应对象指向这些不能拷贝对象的地址空间

深拷贝

深拷贝:除拷贝基本数据类型外,还在原型模式中实现了数组、容器对象、引用对象的拷贝

优缺点

  • 优点

    • 性能优良:相对于new 一个对象来说,它由于是在内存中进行二进制流的拷贝,所以它的性能要好很多,这一点特别体现在循环体中产生大量对象时
    • 隐藏了创建新实例 的复杂性
  • 缺点

    • 每一个类必须被配一个clone方法
    • 深层复制比较复杂

适用场合

  • 复制对象的结构与数据
  • 希望对目标对象的修改稿不影响既有的原型对象
  • 创建对象的成本较大的情况,eg:类创建时比较耗时,有各种属性要配置
  • 一个对象多个修改者

注意问题

  • 构造函数不会执行
  • 原型模式与单例模式冲突:使用原型模式复制对象不会调用类的构造方法,单例模式要保证类的实例的唯一性,所以单例模式不要实现Cloneable
  • 原型模式中不要出现final对象

案例

小鸟的名片

大牛:“我们小鸟也是有身份的人哦,你看,你们公司都给你配上名片了。”

小鸟:“牛哥,你就别取笑我了,你又不是不知道我的水平,小菜鸟一枚,名片上印着高级工程师,就代表我水平达到高级了?面子工程,还不容给我来点实际的,比如说,把这经费给我,去买2本专业书籍,或者涨涨工资啊!”

大牛:“哈哈,不错,看来我们的小鸟很务实,今天就教你一手涨工资的技能。”

小鸟:“来吧!来吧!这个我最喜欢了”

大牛:“如果说,你们公司为了响应环保号召,决定使用电子名片,需要的时候,才根据电子名片进行打印,要你来开发这款项目的打印模块时,你打算怎么弄?”

小鸟:“如果要打印电子名片,首先我需要知道名片上有什么内容,就拿我这张名片来说,上面有:公司名称、公司产品、我的名字、电话号码...”,有这些信息,那么我这里就需要一个名片类(主要是我上面说的那些信息 + get/set + toString).

注意:为了解说方便,我们只使用 “公司名称”和“个人名称”这两个字段

public class VisitingCard {

    // 公司名称
    private String companyName;
    // 个人名称
    private String userName;

    public String getCompanyName() {
        return companyName;
    }

    public void setCompanyName(String companyName) {
        this.companyName = companyName;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    @Override
    public String toString() {
        return "VisitingCard{" +
                "companyName='" + companyName + '\'' +
                ", userName='" + userName + '\'' +
                '}';
    }
}

小鸟:“之后,我的打印,即客户端调用”

VisitingCard card = new VisitingCard();
card.setCompanyName("XX公司");
card.setUserName("小鸟");
System.out.println(card.toString());

大牛:“如果你们boss要求你打印5个人的名片呢?”

小鸟:“那还不简单,我直接把上面的打印代码,复制5份,就OK了”

大牛:“如果,现在要你你打印100张呢?难道你复制100份?”

小鸟:“我这一时图快,当然不能这么弄了,我可以将它用for循环包含起来的,由于打印的是5个不同人的名片,他们除了名字不同外,都是我们公司的,所以我这里还要声明一个数组,用来存放这5个人的名字” 说完,小鸟就重新写下了自己的代码.

String[] userNames = {"小鸟", "张三","李四","王五","赵六"};
for (int i = 0; i < userNames.length; i++) {
    VisitingCard card = new VisitingCard();
    card.setCompanyName("XX公司");
    card.setUserName(userNames[i]);

    System.out.println(card.toString());
}

大牛:“这还有点样子,你还记得你刚才所说的,名片上其他信息都一样,就是名字不同吗?”

小鸟:“记得啊,这里有什么问题吗?”

浅拷贝

大牛:“听说过克隆吗?”

小鸟:“听过,当然听过,记得初中生物课就有一节课是关于克隆的。这个跟我们今天的涨工资技能有关?”

大牛:“小鸟,不错呦!其实我们程序中也有克隆一词,它提供的对外接口就是Cloneable

第1步:实现Cloneable接口

实现Cloneable --> 覆写clone()

小鸟:“你是说让我用名片类去实现Cloneable”

小鸟:“牛哥,是这样吗?”

public class VisitingCard implements Cloneable{

    // 公司名称
    private String companyName;
    // 个人名称
    private String userName;

    @Override
    public VisitingCard clone() {
        VisitingCard visitingCard = null;

        try {
            visitingCard = (VisitingCard) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }

        return visitingCard;
    }

    public String getCompanyName() {
        return companyName;
    }

    public void setCompanyName(String companyName) {
        this.companyName = companyName;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    @Override
    public String toString() {
        return "VisitingCard{" +
                "companyName='" + companyName + '\'' +
                ", userName='" + userName + '\'' +
                '}';
    }
}

**第2步:客户端调用 **

String[] userNames = {"小鸟", "张三","李四","王五","赵六"};
VisitingCard cardClone = new VisitingCard();
cardClone.setTelPhones("我是电话号码");
for (int i = 0; i < userNames.length; i++) {
    // 调用clone方法创建对象,而不是new 
    VisitingCard card = cardClone.clone();
    card.setCompanyName("XX公司");
    card.setUserName(userNames[i]);

    System.out.println(card.toString());
}

深拷贝

大牛:“嗯嗯,悟性不错! 有没有想过给它增加电话号码?”

小鸟:“这还不容易,电话号码,像有在家那个地区用的,有在公司的所在地区用的,所以我们一般会有多个,故此,声明一个list对象用来存放电话号码,然后生命其get和set方法就ok了。”

大牛:“你要不要写写试一下.”

小鸟:“写就写。”

public class VisitingCard implements Cloneable{

    // 公司名称
    private String companyName;
    // 个人名称
    private String userName;

    private ArrayList<String> telPhones = new ArrayList<>();

    @Override
    public VisitingCard clone() {
        VisitingCard visitingCard = null;

        try {
            visitingCard = (VisitingCard) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }

        return visitingCard;
    }

    public String getCompanyName() {
        return companyName;
    }

    public void setCompanyName(String companyName) {
        this.companyName = companyName;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public List<String> getTelPhones() {
        return telPhones;
    }

    public void setTelPhones(String addString) {
        this.telPhones.add(addString);
    }

    @Override
    public String toString() {
        return "VisitingCard{" +
                "companyName='" + companyName + '\'' +
                ", userName='" + userName + '\'' +
                ", telPhones=" + telPhones +
                '}';
    }
}

测试代码

String[] userNames = {"小鸟", "张三","李四","王五","赵六"};
VisitingCard cardClone = new VisitingCard();
for (int i = 0; i < userNames.length; i++) {
    // 调用clone方法创建对象,而不是new
    VisitingCard card = cardClone.clone();
    card.setCompanyName("XX公司");
    card.setUserName(userNames[i]);
    card.setTelPhones("139-0322-345"+i);
    System.out.println(card.toString());
}

小鸟:“牛哥,貌似我开始的思路不对耶,你看结果这里,电话号码,我本来是每人设置了一个,但是随着for循环执行次数的增加,电话号码也跟着增加了,是不是我的分析有问题啊!”

输入图片说明

大牛:“你前面的分析完全没问题,只是你需要改一下覆写的clone方法,让list对象也调用一下其clone 方法”

@Override
public VisitingCard clone() {
    VisitingCard visitingCard = null;

    try {
        visitingCard = (VisitingCard) super.clone();
        // 给list对象添加clone方法
        this.telPhones = (ArrayList<String>) this.telPhones.clone();
    } catch (CloneNotSupportedException e) {
        e.printStackTrace();
    }

    return visitingCard;
}

大牛:“你再运行试试。”

小鸟:“咦!这次是对的,每人只有一个号码,这是为啥呢,为啥我要调用list对象的clone方法呢?”

大牛:“这里就涉及到两个概念,浅拷贝与深拷贝,浅拷贝就是你前面的那种实现方式,它进行拷贝的时候,数组,引用类型这些数据是不会拷贝的。”

小鸟:“既然不会拷贝,那为什么我还能用呢?”

大牛:“这是因为,拷贝的时候,它会创建一个变量指向数组,引用类型这些数据所在的地址,其实就是相当于拷贝后的对象和拷贝前的对象共用了一个地址,这也就是为什么你会看到电话号码随for循环次数增加时,也会增加的原因,因为它们改变的是一处地址空间。”

小鸟:“那后面修改clone方法修改list对象,调用其clone 方法是不是就是就是叫深拷贝。”

大牛:“是的,我们小鸟挺有老板天分啊,挺会总结的.”

© 著作权归作者所有

共有 人打赏支持
粉丝 6
博文 35
码字总数 29999
作品 0
广州
程序员
设计模式(十六)原型模式

相关文章 设计模式系列 前言 公众号有同学留言设计模式,才发现好久没有写设计模式了。关于创建型设计模式只差原型模式没写了,这一篇就来填补这个空缺。 1.原型模式定义 原型模式定义 定义:...

刘望舒
2017/06/30
0
0
只有一个中国 - 单例模式

设计模式 - 单例模式 [toc] 简介 Singleton 保证类有且仅有一个实例,并提供一个访问它的全局站点 类型 饿汉模式 懒汉模式 懒汉模式 PK 饿汉模式 | / | 懒汉模式 | 饿汉模式 | 备注 || :--...

草帽行者
2016/05/11
28
0
Python+树莓派+YOLO打造一款人工智能相机

摘要:今天,我们将自己动手打造出一款基于深度学习的照相机,当小鸟出现在摄像头画面中时,它将能检测到小鸟并自动进行拍照。最终成品所拍摄的画面如下所示: 不久之前,亚马逊刚刚推出了D...

Freebuf
01/03
0
0
小米 VS 华为 - 抽象工厂模式

抽象工厂模式 简介 定义了一个接口用于创建相关或有依赖关系的对象族,而无明确指定具体的类 组成角色 抽象工厂角色: 这是工厂方法模式的核心,它与应用程序无关。是具体工厂角色必须实现的...

草帽行者
2016/05/06
1K
4
四则运算法则表 - 简单工厂模式

简单工厂模式 简介 定义一个创建对象的类,由这个类来封装实例化对象的行为 组成角色 工厂类角色:这是本模式的核心,含有一定的商业逻辑和判断逻辑,根据逻辑不同,产生具体的工厂产品。 抽...

草帽行者
2016/05/06
98
0
给你一个巴掌,再给你一颗蜜枣,能干否? - 装饰模式

设计模式 - 装饰模式 [toc] 简介 Decorator - 作用 当脚本运行时,在子类中增加行为会影响原有类所有的实例,而装饰者却不然,取而代之的是它能给不同对象各自添加新行为 添加辅助的额外功...

草帽行者
2016/06/07
54
0
给我一个电源,我便能伴你走天涯 - 适配器模式

设计模式 - 适配器模式 [toc] 简介 adapter class/object 别名:变压器模式 、包装模式(还包括:装饰模式) 定义:将一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口不匹配...

草帽行者
2016/05/23
36
0
cocos2dx实例开发之flappybird(入门版)

cocos2dx社区里有个系列博客完整地复制原版flappybird的所有特性,不过那个代码写得比较复杂,新手学习起来有点捉摸不透,这里我写了个简单的版本。演示如下: 创建项目 VS2013+cocos2dx 3....

u012234115
2014/10/27
0
0
那些年,我们一起努力的日子

我们的文档~ 我们的图片设计和原型图~~ 我们的培训文档(持续补充中)~~~ 我们的网盘 我们的发展历程~ 2015.10.31 团队例行周会 2015.12.12 六大股东会首签约入股合同! 2015.12.18 上海萌享...

MiniBu
2015/09/24
79
0
四则运算法则表延伸 - 工厂方法模式

工厂方法模式 简介 定义了一个创建对象的抽象方法,由子类决定要实例化的类 工厂方法的模式将对象的实例化推迟到子类 组成角色 抽象工厂角色: 这是工厂方法模式的核心,它与应用程序无关。是...

草帽行者
2016/05/06
30
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

机器学习管理平台 MLFlow

最近工作很忙,博客一直都没有更新。抽时间给大家介绍一下Databrick开源的机器学习管理平台-MLFlow。 谈起Databrick,相信即使是不熟悉机器学习和大数据的工程湿们也都有所了解,它由Spark的...

naughty
今天
0
0
idea tomcat 远程调试

tomcat 配置 编辑文件${tomcat_home}/bin/catalina.sh,在文件开头添加如下代码。    CATALINA_OPTS="-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=7829" Idea端配......

qwfys
今天
1
0
遍历目录下的文件每250M打包一个文件

#!/usr/bin/env python # -*- utf-8 -*- # @Time : 2018/7/20 0020 下午 10:16 # @Author : 陈元 # @Email : abcmeabc@163.com # @file : tarFile.py import os import tarfile import thr......

寻爱的小草
今天
1
0
expect同步文件&expect指定host和要同步的文件&构建文件分发系统&批量远程执行命令

20.31 expect脚本同步文件 expect通过与rsync结合,可以在一台机器上把文件自动同步到多台机器上 编写脚本 [root@linux-5 ~]# cd /usr/local/sbin[root@linux-5 sbin]# vim 4.expect#!/...

影夜Linux
今天
1
0
SpringBoot | 第九章:Mybatis-plus的集成和使用

前言 本章节开始介绍数据访问方面的相关知识点。对于后端开发者而言,和数据库打交道是每天都在进行的,所以一个好用的ORM框架是很有必要的。目前,绝大部分公司都选择MyBatis框架作为底层数...

oKong
今天
13
0
win10 上安装解压版mysql

1.效果 2. 下载MySQL 压缩版 下载地址: https://downloads.mysql.com/archives/community/ 3. 配置 3.1 将下载的文件解压到合适的位置 我最终将myql文件 放在:D:\develop\mysql 最终放的位...

Lucky_Me
今天
2
0
linux服务器修改mtu值优化cpu

一、jumbo frames 相关 1、什么是jumbo frames Jumbo frames 是指比标准Ethernet Frames长的frame,即比1518/1522 bit大的frames,Jumbo frame的大小是每个设备厂商规定的,不属于IEEE标准;...

问题终结者
今天
2
0
expect脚本同步文件expect脚本指定host和要同步的文件 构建文件分发系统批量远程执行命令

expect脚本同步文件 在一台机器上把文件同步到多台机器上 自动同步文件 vim 4.expect [root@yong-01 sbin]# vim 4.expect#!/usr/bin/expectset passwd "20655739"spawn rsync -av ro...

lyy549745
今天
1
0
36.rsync下 日志 screen

10.32/10.33 rsync通过服务同步 10.34 linux系统日志 10.35 screen工具 10.32/10.33 rsync通过服务同步: rsync还可以通过服务的方式同步。那需要开启一个服务,他的架构是cs架构,客户端服务...

王鑫linux
今天
1
0
matplotlib 保存图片时的参数

简单绘图 import matplotlib.pyplot as pltplt.plot(range(10)) 保存为csv格式,放大后依然很清晰 plt.savefig('t1.svg') 普通保存放大后会有点模糊文件大小20多k plt.savefig('t5.p...

阿豪boy
今天
3
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部