设计模式之原型模式

一.介绍

1.定义:

指原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象
不需要知道任何创建的细节,不调用构造函数

2.类型:

创建型

3.适用场景:

类初始化消耗较多资源
new产生的一个对象需要非常繁琐的过程(数据准备,访问权限等)
构造函数比较复杂
循环体中生产大量对象时

4.优点:

原型模式性能比直接new一个对象性能高
简化创建过程

5.缺点:

必须配备克隆方法(cloneable接口)
对克隆复杂对象或对克隆出的对象进行复杂改造时,容易引入风险
深拷贝,浅拷贝要运用得当  

6.注意:
	深克隆和浅克隆是一个大坑

二.实例demo演示

1.创建一个邮件类,包括邮件的名字,邮件的地址,邮件的内容信息属性,但是为了显示效果,要实现Cloneable接口,并且覆盖clone方法

public class Mail implements Cloneable{
   private String name;
   private String emailAddress;
   private String Content;
    public Mail() { System.out.println("Mail Class Constructor..."); }
    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 getContent() { return Content; }
    public void setContent(String content) { Content = content; }
    @Override
    public String toString() {
        return "Mail{" +
                "name='" + name + '\'' +
                ", emailAddress='" + emailAddress + '\'' +
                ", Content='" + Content + '\'' +
                '}'+super.toString();
    }
    @Override
    protected Object clone() throws CloneNotSupportedException {
        System.out.println("克隆方法...");
        return super.clone();
    }
}

2.创建一个邮件的工具类,里面有发送邮件和保存邮件方法

public class MailUtil {
    /**
     *  发送邮件
     * @param mail
     */
    public static void sendMail(Mail mail) {
        String outputContent = "向{0}同学,邮件地址:{1},邮件内容:{2}发送邮件成功";
        System.out.println(MessageFormat.format(outputContent,mail.getName(),mail.getEmailAddress(),mail.getContent()));
    }
    /**
     * 存储原始文件
     *
     * @param mail
     */
    public static void saveOriginMailRecord(Mail mail){
        System.out.println("存储原始邮件: "+mail.getContent());
    }
}	

3.测试,具体的分析在代码注释中

public class Test {
    public static void main(String[] args) throws CloneNotSupportedException {
        /**
         *  此处缺点:
         *   虽然new对象是在for循环外面,但是最终返回的结果却不是初始化模板,因此推论就是对象还是被new了很多次.
         *   因此是非常消耗性能的,解决方式:
         *    在实体类中实现克隆Cloneable接口,并且覆盖其clone方法,以原型模式来解决性能问题
         *
         *    并在for循环中把已经new的对象进行克隆,使得每次调用之前会调用克隆方法进行克隆,掉方法相对于new对象对性能的影响还是挺小的
         *
         *
         */
        Mail mail = new Mail();
        mail.setContent("初始化模板");
        System.out.println("初始化mail: "+mail);
        for(int i=0;i<10;i++){
            Mail mail1 = (Mail) mail.clone();
            mail1.setName("姓名"+i);
            mail1.setEmailAddress("姓名"+i+"@ligh.com");
            mail1.setContent("恭喜你中奖了!");
            MailUtil.sendMail(mail1);
            System.out.println("克隆的mail: "+mail1);
        }
        MailUtil.saveOriginMailRecord(mail);
    }
}

三.深克隆和浅克隆的坑

1.创建一个猪类,然后实现Cloneable接口,并覆盖clone方法,针对clone方法不同的操作来看深克隆和浅克隆的效果

public class Pig implements Cloneable{
    private String name;
    private Date birthday; 
    public Pig(String name, Date birthday) {
        this.name = name;
        this.birthday = birthday;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Date getBirthday() {
        return birthday;
    }
    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        System.out.println("克隆方法...");
        /**
         * 直接return,是一种浅克隆的方式,导致的结果就是对被克隆的对象属性进行了修改,克隆之后的对象竟然该属性也进行了修改
         * 这是不科学的,因为我只是在克隆后修改一个对象属性,你却能同步修改的内容
         */
//        return super.clone();

        /**
         * 深克隆方式:
         *   这种方式的目的就是在修改了被克隆对象的时候,不会影响克隆对象的属性的变化,也就是被克隆对象在克隆时属性的修改不会同步到克隆对象
         *
         */
        Pig pig = (Pig) super.clone();
        pig.birthday = (Date)pig.birthday.clone();
        return pig;
    }
    
    @Override
    public String toString() {
        return "Pig{" +
                "name='" + name + '\'' +
                ", birthday=" + birthday +
                '}'+super.toString();
    }
}

2.测试:

public class Test {
    public static void main(String[] args) throws Exception{
        Date date = new Date(0L);
        Pig pig1 = new Pig("佩奇", date);
        Pig pig2 = (Pig) pig1.clone();
        System.out.println(pig1);
        System.out.println(pig2);
        System.out.println();
        /**
         * 在克隆之后对远对象进行重新赋值操作
         */
        pig1.getBirthday().setTime(66666666666L);

        System.out.println(pig1);
        System.out.println(pig2);

    }
}

3.坑的总结:
上面的测试,在克隆之后对原对象进行属性的修改,如果是浅克隆的话,修改的属性会被同步到克隆的对象,这是不行的。也是一个原型的大坑,所以要在克隆方法中进行操作,使用深克隆的方式进行操作。原型模式如果想用的好的话必须要知道这个坑。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值