设计模式之原型模式

一、什么原型模式

原型模式,用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型对象相同的新对象。

二、原型模式角色

  • 抽象原型类:规定了具体原型对象必须实现的clone方法
  • 具体原型类:实现抽象原型类的clone方法,它是可被复制的对象。

三、原型模式实现方式

  • 浅克隆:创建一个新对象,新对象的属性和原来对象完全相同,对于非基本类型属性,仍指向原有属性所指向的对象的内存地址。
  • 深克隆:创建一个新对象,属性中引用的其它对象也会被克隆,不再指向原有对象地址。

四、原型模式优缺点

1、浅克隆

原型类需要实现JDK 的Cloneable接口的clone方法,客户端(调用者)只要调用clone方法即可复制一个新对象。

缺点:

对于原型类中的引用类型的成员属性被克隆后,仍指向原型对象的成员属性的地址,修改原型对象的属性值,将同时改变克隆对象对应的属性值。

2、深克隆

由于浅克隆存在的缺点,深克隆可以通过对象序列化后,再进行反序列化,就可以实现完全的克隆。原型类需要实现Serializable接口。

3、深克隆扩展

在实际工作中,深克隆场景还是比较多,但是实现深克隆主要借助于第三方工具包,性能和简便性都有保证,常见的如阿里巴巴的fastjson jar,原型类既不需要实现Cloneable接口,也不需要实现Serializable接口。

五、原型模式demo示例

1、浅克隆代码实现

 public class Author {

     //姓名
     private String name;
     //
     private int age;
     //住址
     private String address;
 
     public String getName() {
         return name;
     }
 
     public void setName(String name) {
         this.name = name;
     }
 
     public int getAge() {
         return age;
     }
 
     public void setAge(int age) {
         this.age = age;
     }
 
     public String getAddress() {
         return address;
     }
 
     public void setAddress(String address) {
         this.address = address;
     }
 
     @Override
     public String toString() {
         return "Author{" +
                 "name='" + name + ''' +
                 ", age=" + age +
                 ", address='" + address + ''' +
                 '}';
     }
 }
public class Book implements Cloneable {

     // 书名
     private String name;
     // 价格
     private Double price;
     // 出版日期
     private String publishDate;
     //作者
     // 引用类型
     private Author author;
 
     public String getName() {
         return name;
     }
 
     public void setName(String name) {
         this.name = name;
     }
 
     public Double getPrice() {
         return price;
     }
 
     public void setPrice(Double price) {
         this.price = price;
     }
 
     public String getPublishDate() {
         return publishDate;
     }
 
     public void setPublishDate(String publishDate) {
         this.publishDate = publishDate;
     }
 
     public Author getAuthor() {
         return author;
     }
 
     public void setAuthor(Author author) {
         this.author = author;
     }
 
     /**
      * 克隆方法,调用该方法将复制一个新对象并返回
      * @return
      * @throws CloneNotSupportedException
      */     @Override
     public Book clone() throws CloneNotSupportedException {
         return (Book) super.clone();
     }
 
     @Override
     public String toString() {
         return "Book{" +
                 "name='" + name + ''' +
                 ", price=" + price +
                 ", publishDate='" + publishDate + ''' +
                 ", author=" + author +
                 '}';
     }
}
public class CloneClient {

     public static void main(String[] args) throws CloneNotSupportedException {
         Author author = new Author();
         author.setAddress("上海市青浦区");
         author.setAge(20);
         author.setName("胡大师");
         Book book = new Book();
         book.setAuthor(author);
         book.setName("Python");
         book.setPrice(88d);
         book.setPublishDate("2020-01-12");
         System.out.println("修改前,原型对象信息:"+book);
         //克隆出的新对象
         Book cloneBook = book.clone();
         System.out.println("修改前,克隆对象信息:"+cloneBook);
         System.out.println("原型对象和克隆对象的author引用同一个对象:"+(book.getAuthor() == cloneBook.getAuthor()));
         System.out.println("===================================");
         //修改原型对象的作者信息
         author.setName("胡大帅");
         author.setAge(30);
         author.setAddress("上海市闵行区");
         System.out.println("修改后,原型对象信息:"+book);
         System.out.println("修改后,克隆对象信息:"+cloneBook);
         System.out.println("原型对象和克隆对象的author引用同一个对象:"+(book.getAuthor() == cloneBook.getAuthor()));
     }
 }

控制台输出日志:

设计模式之原型模式插图

2、深克隆代码实现

public class Author implements Serializable {

     //姓名
     private String name;
     //
     private int age;
     //住址
     private String address;
 
     public String getName() {
         return name;
     }
 
     public void setName(String name) {
         this.name = name;
     }
 
     public int getAge() {
         return age;
     }
 
     public void setAge(int age) {
         this.age = age;
     }
 
     public String getAddress() {
         return address;
     }
 
     public void setAddress(String address) {
         this.address = address;
     }
 
     @Override
     public String toString() {
         return "Author{" +
                 "name='" + name + ''' +
                 ", age=" + age +
                 ", address='" + address + ''' +
                 '}';
     }
 }
public class Book implements Serializable {

     // 书名
     private String name;
     // 价格
     private Double price;
     // 出版日期
     private String publishDate;
     //作者
     // 引用类型
     private Author author;
 
     public String getName() {
         return name;
     }
 
     public void setName(String name) {
         this.name = name;
     }
 
     public Double getPrice() {
         return price;
     }
 
     public void setPrice(Double price) {
         this.price = price;
     }
 
     public String getPublishDate() {
         return publishDate;
     }
 
     public void setPublishDate(String publishDate) {
         this.publishDate = publishDate;
     }
 
     public Author getAuthor() {
         return author;
     }
 
     public void setAuthor(Author author) {
         this.author = author;
     }
 
     public Book clone(){
         try(ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("d:/clone.txt"));) {
             oos.writeObject(this);
             oos.flush();
 
         } catch (Exception e) {
             e.printStackTrace();
         }
         try(ObjectInputStream ois = new ObjectInputStream(new FileInputStream("d:/clone.txt"));) {
             Book o = (Book)ois.readObject();
             return o;
         } catch (Exception e) {
             e.printStackTrace();
         }
         return null;
     }
 
     @Override
     public String toString() {
         return "Book{" +
                 "name='" + name + ''' +
                 ", price=" + price +
                 ", publishDate='" + publishDate + ''' +
                 ", author=" + author +
                 '}';
     }
 }
public class DeepCloneClient {
 
     public static void main(String[] args) {
         Author author = new Author();
         author.setAddress("上海市青浦区");
         author.setAge(20);
         author.setName("胡大师");
         Book book = new Book();
         book.setAuthor(author);
         book.setName("Python");
         book.setPrice(88d);
         book.setPublishDate("2020-01-12");
         System.out.println("修改前,原型对象信息:"+book);
         //克隆出的新对象
         Book cloneBook = book.clone();
         System.out.println("修改前,克隆对象信息:"+cloneBook);
         System.out.println("原型对象和克隆对象的author指向不同地址:"+(book.getAuthor() == cloneBook.getAuthor()));
         System.out.println("===================================");
         //修改原型对象的作者信息
         author.setName("胡大帅");
         author.setAge(30);
         author.setAddress("上海市闵行区");
         System.out.println("修改后,原型对象信息:"+book);
         System.out.println("修改后,克隆对象信息:"+cloneBook);
         System.out.println("原型对象和克隆对象的author指向不同地址:"+(book.getAuthor() == cloneBook.getAuthor()));
     }
 }

控制台输出日志:

设计模式之原型模式插图2

3、深克隆扩展代码实现

通过maven引入依赖包:

<!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
<dependency>
     <groupId>com.alibaba</groupId>
     <artifactId>fastjson</artifactId>
     <version>1.2.75</version>
</dependency>
public class AliDeepCloneClient {

     public static void main(String[] args) {
         Author author = new Author("胡大帅",20,"上海");
         Book book = new Book("Java编程",99d,"2020-01-12",author);
         //使用JSON序列化为字符串
         String str = JSON.toJSONString(book);
         //再将字符串反序列化为对象
         Book cloneBook = JSON.parseObject(str, Book.class);
         System.out.println("修改前,原型对象信息="+book);
         System.out.println("修改前,克隆对象信息="+cloneBook);
         System.out.println("原型对象的author和克隆对象的author,指向地址="+(book.getAuthor() == cloneBook.getAuthor()));
         //修改原型对象的author值
         author.setAddress("黄浦区");
         author.setAge(35);
         System.out.println("修改后,原型对象信息="+book);
         System.out.println("修改后,克隆对象信息="+cloneBook);
     }
}

控制台输出日志:

设计模式之原型模式插图4

发表评论