原型模式(Prototype )


簡單的復制粘貼代碼會對以后的程序維護造成巨大的工作量。

為了避免這種災難的誕生,我們今天來學習原型模式,還是用代碼來逐步過渡到原型模式(創建型模式)的講解吧。

 

假設今天開學啦,有小明,小紅,小豬入學報到!

先來一個學生檔案類,有院系,入學時間,畢業時間幾個屬性,和屬性的set/get方法

 1 public class StudentFiles {
 2     private String department;
 3     private String admissionTime;
 4     private String graduationTime;
 5 
 6     public StudentFiles(String department, String admissionTime, String graduationTime) {
 7         this.department = department;
 8         this.admissionTime = admissionTime;
 9         this.graduationTime = graduationTime;
10     }
11 
12     @Override
13     public String toString() {
14         return "StudentFiles{" +
15                 "department='" + department + '\'' +
16                 ", admissionTime='" + admissionTime + '\'' +
17                 ", graduationTime='" + graduationTime + '\'' +
18                 '}';
19     }
20 }

再來一個學生類,有姓名,年齡和檔案三個屬性

 1 public class Student {
 2     private String name;
 3     private int age;
 4     private StudentFiles studentFiles;
 5 
 6     public Student(String name, int age) {
 7         this.name = name;
 8         this.age = age;
 9     }
10 
11     public StudentFiles getStudentFiles() {
12         return studentFiles;
13     }
14 
15     public void setStudentFiles(StudentFiles studentFiles) {
16         this.studentFiles = studentFiles;
17     }
18 
19     @Override
20     public String toString() {
21         return "Student{" +
22                 "name='" + name + '\'' +
23                 ", age=" + age +
24                 ", studentFiles=" + studentFiles +
25                 '}';
26     }
27 }
28 
29 
30 現在開始給他們辦理入學手續
31 客戶端代碼
32 public class Client {
33     public static void main(String[] args) {
34         StudentFiles xiaohongFiles = new StudentFiles("計算機系","2019-5-8","2023-5-8");
35         Student xiaohong=new Student("小紅",22);
36         xiaohong.setStudentFiles(xiaohongFiles);
37 
38         StudentFiles xiaomingFiles = new StudentFiles("計算機系","2019-5-8","2023-5-8");
39         Student xiaoming=new Student("小明",21);
40         xiaoming.setStudentFiles(xiaomingFiles);
41 
42         StudentFiles xiaozhuFiles = new StudentFiles("計算機系","2019-5-8","2023-5-8");
43         Student xiaozhu=new Student("小豬",23);
44         xiaozhu.setStudentFiles(xiaozhuFiles);
45 
46         System.out.println(xiaohong.toString());
47         System.out.println(xiaoming.toString());
48         System.out.println(xiaozhu.toString());
49     }
50 }

結果

現在三位同學開開心心的去上學了,但是我們發現檔案是個屬性相同的對象。我們在創建的時候只是簡單的復制粘貼過來的,復制粘貼的代碼越多維護代碼也就越多 。

 

那我們只制作一份檔案試試?

 1 public class Client {
 2     public static void main(String[] args) {
 3         StudentFiles studentFiles = new StudentFiles("計算機系","2019-5-8","2023-5-8");
 4 
 5         Student xiaohong=new Student("小紅",22);
 6         xiaohong.setStudentFiles(studentFiles);
 7 
 8         Student xiaoming=new Student("小明",21);
 9         xiaoming.setStudentFiles(studentFiles);
10 
11         Student xiaozhu=new Student("小豬",23);
12         xiaozhu.setStudentFiles(studentFiles);
13 
14         System.out.println(xiaohong.toString());
15         System.out.println(xiaoming.toString());
16         System.out.println(xiaozhu.toString());
17     }
18 }

結果

看了下結果是對的,可是別開心的太早。
現在小豬同學表現一點都不好,不能再學校按時畢業了,要延期一年。

1 studentFiles.setGraduationTime("2024-5-8");

結果

好了,現在小明和小紅都要延期畢業了,是不是會氣死其他兩個同學。

分析一下原因:我們只創建了一份檔案,讓三個同學的檔案都指向了這個檔案了,三個檔案是同一份檔案,這當然不合乎常理了。每個人的檔案都應該屬於自己,而不是和別人共用。

究其發生上面情況的原因是因為我們既不想復制代碼,偷懶又出現了大問題。那么存在那種我們通過代碼來復制對象的可能的方法嗎?
有的就是接下來出場的原型模式:

 

因為這個模式使用頻繁,所有java已經給我們封裝好了,我們只需要掌握使用即可。

首先讓類實現Cloneable接口,接着重寫clone方法

1 public Object clone() throws CloneNotSupportedException{
2     return super.clone();
3 }

此時的客戶端代碼

1 StudentFiles xiaohongStudentFiles= (StudentFiles) studentFiles.clone();
2 StudentFiles xiaomingStudentFiles= (StudentFiles) studentFiles.clone();
3 StudentFiles xiaozhuStudentFiles= (StudentFiles) studentFiles.clone();

那我們再來看看這樣復制真的可以嗎,假設小豬不想和小紅做同學了,他要轉到電信院去。

1 xiaozhuStudentFiles.setDepartment("電信院");

結果

既然我們已經做好了偷懶的准備,為什么不進行到底呢?

其實我們已經了解到來上學的同學大多都是22歲,只有極個別是其他年齡。

那我們復制學生類好了,再給每個學生都賦上他們的姓名即可。

1 public Object clone() throws CloneNotSupportedException{
2     return super.clone();
3 }

客戶端的代碼

public class Client {
    public static void main(String[] args) throws CloneNotSupportedException {
        StudentFiles studentFiles = new StudentFiles("計算機系","2019-5-8","2023-5-8");
        Student student=new Student(22);//student的原型
        student.setStudentFiles(studentFiles);

        Student xiaohong= (Student) student.clone();
        xiaohong.setName("小紅");

        Student xiaoming=(Student) student.clone();
        xiaoming.setName("小明");
        xiaoming.setAge(15);

        Student xiaozhu=(Student) student.clone();
        xiaozhu.setName("小豬");

        System.out.println(xiaohong.toString());
        System.out.println(xiaoming.toString());
        System.out.println(xiaozhu.toString());
    }
}    

我們發現小明原來是個神通,才15歲是同學中的特例,我們為他修改下年齡。

結果

聰明的小明提前一年修滿了所有學分,他要提前畢業了。

1 StudentFiles tmp = xiaoming.getStudentFiles();
2 tmp.setGraduationTime("2022-5-8");
3 xiaoming.setStudentFiles(tmp);

結果

可以看到同學們都沾了小明的光提前畢業了,但是學校不允許這樣的情況發生呀,我們來研究下原因吧:

我們先了解淺拷貝和深拷貝的概念

淺拷貝:只拷貝基本數據類型,對於對象屬性拷貝其中的引用地址

深拷貝:復制的時候基本數據類型和對象引用都拷貝一份

 

很顯然我們的拷貝是屬於淺拷貝,我們修改年齡對其他人沒有影響,但是我們修改學籍對象的時候,每個拷貝的對象都發生了修改。
那java的深拷貝是怎么實現的呢?

我們修改一下Student的clon方法即可

1 public Object clone() throws CloneNotSupportedException{
2     Student student=(Student)super.clone();
3     student.setStudentFiles((StudentFiles)studentFiles.clone());
4     return student;
5 }

結果

 

總結:
淺拷貝:復制基本數據類型,引用類型沒有進行復制  

步驟:

1.實現Cloneable接口

2.實現clone方法

1 public Object clone() throws CloneNotSupportedException{
2         return super.clone();
3     }

 


深拷貝:復制基本數據類型和引用類型

步驟:

1.實現Cloneable接口

2.實現clone方法

1 public Object clone() throws CloneNotSupportedException{
2         Student student=(Student)super.clone();
3         student.setStudentFiles((StudentFiles)studentFiles.clone());
4         return student;
5     }

 


原型模式優點:
1.抽象出共同點,僅需要修改對象間的不同點
2.大大減少JVM創建對象的時間

 

 

其實是有遇到過類似的情況的,只不過因為並沒有學習到這里,當時使用了最笨的辦法一次次的new一個對象。

比如現在有一個student的list集合創建,然后批量插入數據庫。在循環處的new對象完全可以改成(Student) student.clone(),修改其中的屬性即可。大大減少java徐理解創建對象的時間,同時代碼也相對簡潔。

 

到這里創建型模式(建造者模式,工廠模式,原型模式)都搞定了,還剩下單例模式還沒寫博客了。

單例模式十分重要,運用spring的bean的創建上,是spring IOC的重要設計模式。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM