work hard work smart

专注于Java后端开发。 不断总结,举一反三。
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

设计模式-原型模式

Posted on 2018-10-06 17:12  work hard work smart  阅读(143)  评论(0编辑  收藏  举报

一、定义

原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象

特点:不需要创建任何创建的细节,不调用构造函数

类型:创建型

 

二、适用场景

1、类初始化消耗较多资源

2、new产生的对象需要非常繁琐的过程(数据准备、访问权限等)

3、构造函数比较复杂

4. 循环体重生产大量对象时

 

三、优点

原型模式性能比直接new一个对象性能高

简化创建过程

 

四、缺点

1.必须配备克隆方法

2.对克隆复杂对象或对克隆出的对象进行复杂的改造时,容易引入风险

3、深拷贝、浅拷贝要运用得当

 

五、原型的扩展

深克隆

浅克隆

 

六、Code

这个demo没有使用原型模式

1. 创建Mail类

public class Mail {
    private String name;
    private String emailAddress;
    private String contnet;

    public Mail(){
        System.out.println("Mail构造函数");
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getEmailAddress() {
        return emailAddress;
    }

    public void setEmailAddress(String emailAddress) {
        this.emailAddress = emailAddress;
    }

    public String getContnet() {
        return contnet;
    }

    public void setContnet(String contnet) {
        this.contnet = contnet;
    }

    @Override
    public String toString() {
        return "Mail{" +
                "name='" + name + '\'' +
                ", emailAddress='" + emailAddress + '\'' +
                ", contnet='" + contnet + '\'' +
                '}';
    }

  

2. 创建MailUtil类

public class MailUtil {

    public static  void sendMail(Mail mail){
        String outputContet = "向{0}同学,邮件地址{1},邮件内容:{2} 发送邮件";
        System.out.println(MessageFormat.format(outputContet,mail.getName(), mail.getEmailAddress(), mail.getContnet()));

    }

    public static  void saveOriginMailRecord(Mail mail){
        System.out.println("存储原始邮件:" + mail);
    }
}

  

3. 测试类

public class Test {
    public static void main(String[] args) {
        Mail mail = new Mail();
        mail.setContnet("初始化m模板");

        for(int i = 0; i < 10 ; i++){
            mail.setName("姓名:" +i);
            mail.setEmailAddress("name" + i + "@163.com");
            mail.setContnet("恭喜您");
            MailUtil.sendMail(mail);
        }
        MailUtil.saveOriginMailRecord(mail);
    }
}

  

七. Code 使用原型模式(这里我们假设new Mail的操作非常复杂)

 1. 创建Mail类,实现Cloneable接口,并且重写Override方法。

public class Mail implements  Cloneable {
    private String name;
    private String emailAddress;
    private String contnet;

    public Mail(){
        System.out.println("Mail构造函数");
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getEmailAddress() {
        return emailAddress;
    }

    public void setEmailAddress(String emailAddress) {
        this.emailAddress = emailAddress;
    }

    public String getContnet() {
        return contnet;
    }

    public void setContnet(String contnet) {
        this.contnet = contnet;
    }


    @Override
    protected Object clone() throws CloneNotSupportedException {
        System.out.println("克隆Mail对象");
        return super.clone();
    }

    @Override
    public String toString() {
        return "Mail{" +
                "name='" + name + '\'' +
                ", emailAddress='" + emailAddress + '\'' +
                ", contnet='" + contnet + '\'' +
                '}';
    }
}

  

2. MailUtil类不变

3.测试类

public class Test {
    public static void main(String[] args) throws CloneNotSupportedException {
        Mail mail = new Mail();
        mail.setContnet("初始化m模板");

        for(int i = 0; i < 10 ; i++){
            Mail mailTemp = (Mail) mail.clone();
            mailTemp.setName("姓名:" +i);
            mailTemp.setEmailAddress("name" + i + "@163.com");
            mailTemp.setContnet("恭喜您");
            MailUtil.sendMail(mailTemp);
        }
        MailUtil.saveOriginMailRecord(mail);
    }
}

  

八 . 浅克隆

1. 创建Pig类,并实现Cloneable,重写Clone方法

public class Pig  implements  Cloneable{
    private String name;
    private Date birdhtday;


    public Pig(String name, Date birdhtday) {
        this.name = name;
        this.birdhtday = birdhtday;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Date getBirdhtday() {
        return birdhtday;
    }

    public void setBirdhtday(Date birdhtday) {
        this.birdhtday = birdhtday;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    @Override
    public String toString() {
        return "Pig{" +
                "name='" + name + '\'' +
                ", birdhtday=" + birdhtday +
                '}' + super.toString();
    }
}

  

2. 创建测试类

public class Test
{
    public static void main(String[] args) throws CloneNotSupportedException {
        Date birdhday = new Date(0L);
        Pig pig1 = new Pig("小花", birdhday);
        Pig pig2 = (Pig)pig1.clone();
        System.out.println(pig1);
        System.out.println(pig2);

        pig1.getBirdhtday().setTime(666666666666L);
        System.out.println(pig1);
        System.out.println(pig2);


    }
} 

输出结果如下

Pig{name='小花', birdhtday=Thu Jan 01 08:00:00 CST 1970}com.design.pattern.creational.prototype.clone.Pig@69f42679
Pig{name='小花', birdhtday=Thu Jan 01 08:00:00 CST 1970}com.design.pattern.creational.prototype.clone.Pig@56a57bb2
Pig{name='小花', birdhtday=Sat Feb 16 09:11:06 CST 1991}com.design.pattern.creational.prototype.clone.Pig@69f42679
Pig{name='小花', birdhtday=Sat Feb 16 09:11:06 CST 1991}com.design.pattern.creational.prototype.clone.Pig@56a57bb2

测试的代码,pig1和pig2是两个对象。但是修改pig1的时间,pig2的时间也被改了。

Debug后发现,pig1和pig2的date地址都是444。如下图

这就是浅拷贝。

 

九:深克隆

在浅克隆的基础上,修改Pig类的clone方法

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Pig pig = (Pig)super.clone();
        //深克隆
        pig.birdhtday = (Date) pig.birdhtday.clone();
        //return super.clone();
        return  pig;
    }

  

然后查看输出

Pig{name='小花', birdhtday=Thu Jan 01 08:00:00 CST 1970}com.design.pattern.creational.prototype.clone.Pig@2ad3eec2
Pig{name='小花', birdhtday=Thu Jan 01 08:00:00 CST 1970}com.design.pattern.creational.prototype.clone.Pig@3fe01885
Pig{name='小花', birdhtday=Sat Feb 16 09:11:06 CST 1991}com.design.pattern.creational.prototype.clone.Pig@2ad3eec2
Pig{name='小花', birdhtday=Thu Jan 01 08:00:00 CST 1970}com.design.pattern.creational.prototype.clone.Pig@3fe01885

这样在修改Pig1日期的时候,Pig2不变。达到了预期的效果。这就是深克隆。

 

十、在JDK和开源框架中的应用

1. Object中的clone方法。 是一个native方法

   protected native Object clone() throws CloneNotSupportedException;

 

2. ArrayList类,重新了clone方法

    public Object clone() {
        try {
            @SuppressWarnings("unchecked")
                ArrayList<E> v = (ArrayList<E>) super.clone();
            v.elementData = Arrays.copyOf(elementData, size);
            v.modCount = 0;
            return v;
        } catch (CloneNotSupportedException e) {
            // this shouldn't happen, since we are Cloneable
            throw new InternalError();
        }
    }

  

3. HasMap类,重新了clone方法

 public Object clone() {
        HashMap<K,V> result = null;
        try {
            result = (HashMap<K,V>)super.clone();
        } catch (CloneNotSupportedException e) {
            // assert false;
        }
        if (result.table != EMPTY_TABLE) {
            result.inflateTable(Math.min(
                (int) Math.min(
                    size * Math.min(1 / loadFactor, 4.0f),
                    // we have limits...
                    HashMap.MAXIMUM_CAPACITY),
               table.length));
        }
        result.entrySet = null;
        result.modCount = 0;
        result.size = 0;
        result.init();
        result.putAllForCreate(this);

        return result;
    }

其它地方还很多。