文档章节

小鸟的名片 - 原型模式

 草帽行者
发布于 2016/05/16 10:37
字数 2191
阅读 80
收藏 1

#设计模式 - 原型模式

[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
【APP开发】找师傅项目(APP+管理后台)

【业务需求】 师傅之家APP是涵盖家电水电等日常生活所需的一站式解决方案的家庭服务商。让业主有活能找到师傅干,让师傅能找到活干的撮合平台。 app基本功能: 公共功能: 登陆、注册、我的地...

师傅之家
2016/02/04
23
14
【产品原型制作】师傅之家App后台原型制作

【任务说明】 师傅之家App后台原型制作 根据 前台原型文件(已有) ,制作对应的后台原型; 【业务需求】 师傅之家APP是涵盖家电水电等日常生活所需的一站式解决方案的家庭服务商。让业主有活...

师傅之家
2016/01/12
0
4
“化繁为简 完美融合”——佳信智能全媒体客服新品上市&融资发布会

会议情况: 时间:4月9号下午 地点:广州贝塔咖啡[科韵路店](广州市天河区建中路24号2楼,近网易) 规模:150-200人 参会人员:电商、P2P、O2O、金融、旅游、互联网企业服务等行业内创业者...

佳信客服
2016/03/25
139
0
【UI设计】师傅之家 App-UI 设计

【任务说明】 师傅之家App UI设计 根据现有App原型文件 ,进行UI设计(附件列出来一部分APP原型页面) 需要设计的页大概30-40个 【业务需求】 师傅之家APP是涵盖家电水电等日常生活所需的一站...

师傅之家
2016/01/13
187
5

没有更多内容

加载失败,请刷新页面

加载更多

java框架学习日志-7(静态代理和JDK代理)

静态代理 我们平时去餐厅吃饭,不是直接告诉厨师做什么菜的,而是先告诉服务员点什么菜,然后由服务员传到给厨师,相当于服务员是厨师的代理,我们通过代理让厨师炒菜,这就是代理模式。代理...

白话
今天
21
0
Flink Window

1.Flink窗口 Window Assigner分配器。 窗口可以是时间驱动的(Time Window,例如:每30秒钟),也可以是数据驱动的(Count Window,例如:每一百个元素)。 一种经典的窗口分类可以分成: 翻...

满小茂
今天
17
0
my.ini

1

architect刘源源
今天
14
0
docker dns

There is a opensource application that solves this issue, it's called DNS Proxy Server It's a DNS server that solves containers hostnames, if could not found a hostname that mat......

kut
今天
15
0
寻找数学的广度——《这才是数学》读书笔记2700字

寻找数学的广度——《这才是数学》读书笔记2700字: 文|程哲。数学学习方式之广:国内外数学教育方面的专家,进行了很多种不同的数学学习方式尝试,如数学绘本、数学游戏、数学实验、数学步道...

原创小博客
今天
27
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部