Java设计模式之原型模式

Java设计模式之原型模式

//首先新建一个类Mail
public class Mail {
    //这个类总共有三个属性
    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) {
        this.content = content;
    }

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

//新建另外一个类
public class MailUtil {
    public static void sendMail(Mail mail){
        String content="{0}同学,邮件地址:{1},内容:{2}";
        System.out.println(MessageFormat.format(content,mail.getName()
                ,mail.getEmailAddress(),mail.getContent()));
    }
    public static void saveOriginalContent(Mail mail){
        System.out.println("保存原始内容:"+mail.getContent());
    }
}


//调用
public static void main(String[]a){
        Mail mail=new Mail();
        mail.setContent("初始化模板");
        for (int i=0;i<10;i++){
            mail.setName("姓名"+i);
            mail.setEmailAddress("邮箱"+i);
            mail.setContent("邮件内容。。。");
            MailUtil.sendMail(mail);
        }
        MailUtil.saveOriginalContent(mail);

    }


//结果
Mail class constructor
姓名0同学,邮件地址:邮箱0,内容:邮件内容。。。
姓名1同学,邮件地址:邮箱1,内容:邮件内容。。。
姓名2同学,邮件地址:邮箱2,内容:邮件内容。。。
姓名3同学,邮件地址:邮箱3,内容:邮件内容。。。
姓名4同学,邮件地址:邮箱4,内容:邮件内容。。。
姓名5同学,邮件地址:邮箱5,内容:邮件内容。。。
姓名6同学,邮件地址:邮箱6,内容:邮件内容。。。
姓名7同学,邮件地址:邮箱7,内容:邮件内容。。。
姓名8同学,邮件地址:邮箱8,内容:邮件内容。。。
姓名9同学,邮件地址:邮箱9,内容:邮件内容。。。
保存原始内容:邮件内容。。。

这里有两个问题,第一是MailUtil.saveOriginalContent(mail)方法应该是保存原始内容“初始化模板”而不是for循环里面最后被赋值了。第二个问题是考虑实例化Mail对象比较消耗资源的问题。

下面看看引入原型模式后的优雅实现方法。

//Mail类实现Cloneable接口
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) {
        this.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("clone 方法");
        return super.clone();
    }
}



public class MailUtil {
    public static void sendMail(Mail mail){

    }
    public static void saveOriginalContent(Mail mail){
        System.out.println("保存原始内容:"+mail.getContent());
    }
}


//调用
public static void main(String[]a) throws CloneNotSupportedException {
        Mail mail=new Mail();
        mail.setContent("初始化模板");
        System.out.println("初始化的mail对象:"+mail);
        for (int i=0;i<10;i++){
            Mail tmpMail= (Mail) mail.clone();
            tmpMail.setName("姓名"+i);
            tmpMail.setEmailAddress("邮箱"+i);
            tmpMail.setContent("邮件内容。。。");
            MailUtil.sendMail(tmpMail);
            System.out.println("克隆的mail对象:"+tmpMail);
        }
        MailUtil.saveOriginalContent(mail);
    }


//结果:@符号后面的地址不同表示实例化的mail对象不同.
Mail class constructor
初始化的mail对象:Mail{name='null', emailAddress='null', content='初始化模板'}com.zk.javatest.prototype.Mail@1540e19d
clone 方法
克隆的mail对象:Mail{name='姓名0', emailAddress='邮箱0', content='邮件内容。。。'}com.zk.javatest.prototype.Mail@677327b6
clone 方法
克隆的mail对象:Mail{name='姓名1', emailAddress='邮箱1', content='邮件内容。。。'}com.zk.javatest.prototype.Mail@14ae5a5
clone 方法
克隆的mail对象:Mail{name='姓名2', emailAddress='邮箱2', content='邮件内容。。。'}com.zk.javatest.prototype.Mail@7f31245a
clone 方法
克隆的mail对象:Mail{name='姓名3', emailAddress='邮箱3', content='邮件内容。。。'}com.zk.javatest.prototype.Mail@6d6f6e28
clone 方法
克隆的mail对象:Mail{name='姓名4', emailAddress='邮箱4', content='邮件内容。。。'}com.zk.javatest.prototype.Mail@135fbaa4
clone 方法
克隆的mail对象:Mail{name='姓名5', emailAddress='邮箱5', content='邮件内容。。。'}com.zk.javatest.prototype.Mail@45ee12a7
clone 方法
克隆的mail对象:Mail{name='姓名6', emailAddress='邮箱6', content='邮件内容。。。'}com.zk.javatest.prototype.Mail@330bedb4
clone 方法
克隆的mail对象:Mail{name='姓名7', emailAddress='邮箱7', content='邮件内容。。。'}com.zk.javatest.prototype.Mail@2503dbd3
clone 方法
克隆的mail对象:Mail{name='姓名8', emailAddress='邮箱8', content='邮件内容。。。'}com.zk.javatest.prototype.Mail@4b67cf4d
clone 方法
克隆的mail对象:Mail{name='姓名9', emailAddress='邮箱9', content='邮件内容。。。'}com.zk.javatest.prototype.Mail@7ea987ac
保存原始内容:初始化模板

 

原型模式中的浅克隆与深克隆

先来看看浅克隆


//新建类Pig
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 {
        return super.clone();
    }

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


//调用
public static void main(String[] a) throws CloneNotSupportedException {
        Date birthday=new Date(0L);
        Pig pig1=new Pig("佩奇",birthday);
        Pig pig2= (Pig) pig1.clone();
        System.out.println(pig1);
        System.out.println(pig2);

        pig1.getBirthday().setTime(666666666666L);
        System.out.println("修改后:"+pig1);
        System.out.println("修改后:"+pig2);
    }


//结果:
Pig{name='佩奇', birthday=Thu Jan 01 08:00:00 CST 1970}com.zk.clone.Pig@4c75cab9
Pig{name='佩奇', birthday=Thu Jan 01 08:00:00 CST 1970}com.zk.clone.Pig@1ef7fe8e
修改后:Pig{name='佩奇', birthday=Sat Feb 16 09:11:06 CST 1991}com.zk.clone.Pig@4c75cab9
修改后:Pig{name='佩奇', birthday=Sat Feb 16 09:11:06 CST 1991}com.zk.clone.Pig@1ef7fe8e

从运行结果可以看出,修改了pig1对象的birthday属性,pig2对象的birthday属性也被修改了。看看下面这张调试过程的图。

虽然pig1与pig1克隆出的对象pig2是两个不同的对象,但是对象里面的birthday属性是引用的是同一个对象。因此也就看到了上面的两个birthday都被修改了。

这就是浅克隆。

 

下面看看深克隆:修改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 {
        //深克隆
        Pig pig= (Pig) super.clone();
        //将引用对象Date也克隆
        pig.birthday=(Date) pig.birthday.clone();
        return pig;
    }

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


//运行结果:
Pig{name='佩奇', birthday=Thu Jan 01 08:00:00 CST 1970}com.zk..clone.Pig@6d6f6e28
Pig{name='佩奇', birthday=Thu Jan 01 08:00:00 CST 1970}com.zk..clone.Pig@135fbaa4
修改后:Pig{name='佩奇', birthday=Sat Feb 16 09:11:06 CST 1991}com.zk.clone.Pig@6d6f6e28
修改后:Pig{name='佩奇', birthday=Thu Jan 01 08:00:00 CST 1970}com.zk.clone.Pig@135fbaa4

此时修改pig1对象的birthday属性不会影响pig2的birthday属性。

因此,使用原型模式时,一定要注意浅克隆与深克隆,否则很容易导致bug。

 

原型模式破坏单例模式

//饿汉式单例模式,实现Cloneable接口
public class HungrySingleton implements Serializable,Cloneable{
    private HungrySingleton(){
        if (hungrySingleton!=null){
            throw new RuntimeException("单例模式的构造器禁止反射");
        }
    }

    private final static HungrySingleton hungrySingleton=new HungrySingleton();

    public static HungrySingleton getInstance() {
        return hungrySingleton;
    }

    private Object readResolve(){
        return  hungrySingleton;
    }

    //重写此方法
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

//调用
HungrySingleton hungrySingleton=HungrySingleton.getInstance();
        Method method=hungrySingleton.getClass().getDeclaredMethod("clone");
        method.setAccessible(true);
        HungrySingleton cloneHungrySingleton= (HungrySingleton) method.invoke(hungrySingleton);
        System.out.println(hungrySingleton);
        System.out.println(cloneHungrySingleton);

//结果
com.zk.javatest.prototype.HungrySingleton@677327b6
com.zk.javatest.prototype.HungrySingleton@14ae5a5


从运行结果可以看出,两个实例对象不相同,单例模式被破坏。那如果防止这种破坏呢?很简单,重写clone()方法即可。

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

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章