欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

设计模式学习(1)-------原型模式

程序员文章站 2022-06-02 20:15:31
...

先来个原型设计模式的个人理解:属于创建型设计模式(单例模式Singleton、建造者模式Builder、工厂模式Factory、原型模式Prototype)的一种。其中单例、建造者、工厂模式后面学习。原型模式其实就是复制产生对象。和new出对象比起来。性能更好,所以用得也比较广泛。实现方式就是实现Cloneable接口。调用object的clone()方法,进行浅拷贝。下面是详细介绍:

在介绍这个原型模式是什么的时候,我们先看一段代码

package prototypeDesign;

public class Book implements Cloneable{
    private String title;
    private Author author;
    private int pageNum;
    public Book clone() throws CloneNotSupportedException{
        return (Book)super.clone();//这句话是重点;
    }    
}

上面这段代码Book类实现了Cloneable接口。Book类的对象可以调用这个clone()方法生成一个新的的对象。让我想到在很多地方需要生成类实例的时候,我们可以不用new的形式来创建对象。因为上文说过,拷贝比new性能更好。我们再需要一个调用入口:

public class TestUtils {
    public static void main(String[] args) throws Exception {        
        //浅复制
        Book book = new Book();
        book.setPageNum(10);
        book.setTitle("原型模式");
        Author author = new Author();
        author.setAge(25);
        book.setAuthor(author);
        Book book1 = book.clone(); 
    }

}

就这样,根据book对象,clone一下,就生成了book1了。这就是我理解的原型模式最简单的实现。当然我们可以把book实例私有化一下,放到工具类里面,再写一个静态方法,就可以实现随用随调了。比如:

package prototypeDesign;
public class TargetUtils {
    private static Book book = new Book();
    public static Book initBook() throws CloneNotSupportedException{
        return book.clone();
    }
}
public class Test {
    public static void main(String[] args) throws Exception {
        Book initBook = TargetUtils.initBook();
        Book initBook2 = TargetUtils.initBook();
        Book initBook3 = TargetUtils.initBook();        
    }
}

你看,稍微包装一下就可以用工具类的方式来创造一个Book实例了。虽然这样写代码要多一点,但是一个对象多次被new的话,用这种方式,可以提高性能,对象越复杂,差异就越明显。

现在我们再来总结一下,何为原型模式。即我们再创建一个对象的时候需要借助一个原型,而上例中book就是这个原型,当然要想clone出新实例,必须还要实现Cloneable接口。所以这里又有一个知识点需要我们去扩展,那就是Cloneable接口,需要我们去了解。

于是乎我在学习的过程中有去看了一下Cloneable接口,个人理解,这哥们就是一个标识,就像Serializable接口一样。按理说所有类都继承的Object里面就有一个clone()方法。但如果不实现一下Cloneable接口,调用clone()会抛出CloneNotSupportedException异常。没这哥们成不了事。

接下来我们来说说这个clone()方法稍微深一点的东西:关于深拷贝和浅拷贝。先把定义放这:

浅拷贝: 对简单类型的成员变量进行值的复制,对引用类型的成员变量只复制引用,不复制引用的对象.

深拷贝: 对简单类型的成员变量进行值的复制,对引用类型的成员变量也进行引用对象的复制.

来来来,上代码加以解释:

package prototypeDesign;

public class Book implements Cloneable{
    private String title;
    private Author author;
    private int pageNum;
    public Book clone() throws CloneNotSupportedException{
        return (Book)super.clone();//这句话是重点;
    }    
}

把最前面的代码再搬到这里来。这就是浅拷贝 ,Book中的pageNum是简单类型的成员变量。在克隆时对值进行了复制,Author是引用类型的成员变量,把引用地址复制给了新的实例。也就是说新生成的Book实例和原型实例的author属性所引用的是同一个内存空间。这就是浅拷贝。不知道你理解了吗?

相反深拷贝就是把Author的实例也一样拷贝一份。让新生成实例的author属性和原型实例的author属性指向不同的内存空间。通常我们clone()都是浅拷贝。如果需要实现深拷贝,我们可以对引用类型的属性再拷贝一份,如果遇到里面还有引用类型的,就需要再浅拷贝一次,然后通过set方法装载进去。还有一种就是通过序列化,再反序列化实现。

针对深拷贝,上点代码说明一下

package prototypeDesign;
public class Author implements Cloneable{
    private int age;
    public Author clone() throws CloneNotSupportedException{
        return (Author)super.clone();
    }
    //省略了set和get方法
}
package prototypeDesign;
public class TargetUtils {
    private static Book book = new Book();
    public static Book initBook() throws CloneNotSupportedException{
        Book book1 = book.clone();
        book1.setAuthor(book.getAuthor().clone());
        return book1;
    }
}

看看上面的两段代码,把Author的实例也浅拷贝了一下。然后装载如Book新实例中。当然如果Author也有引用类型的属性,那也需要像这样clone来装载进去。

再上一段通过序列化反序列化实现深拷贝的代码

public DeepBook DeepClone() throws IOException, ClassNotFoundException{
        ByteArrayOutputStream  os = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(os);
        oos.writeObject(this);
        ByteArrayInputStream is = new ByteArrayInputStream(os.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(is);
        return (DeepBook)ois.readObject();
    }

这种方式一样也实现了深拷贝的目的。

接下来就要开始记录再深一点东西了。

调用Object的clone()方法。是没有调用构造方法的。打开Object类可以看到

protected native Object clone() throws CloneNotSupportedException;

这是调用了c语音来实现的。是由虚拟机直接复制的内存块。所以速度比new出对象的方式要快很多

同时又因为没有调用构造函数,所以不受权限的控制

这个原型原型设计模式比较简单,就不特意去找源码了。这是我写的第一篇文章,很多地方写得不好,还请手下留情,有好的建议或者一些问题可以在下面留言。一起学习进步!

相关标签: n