1. 程式人生 > 其它 >java 設計模式之原型模式(三)

java 設計模式之原型模式(三)

技術標籤:Android設計模式java原型模式

java 設計模式之原型模式③

每一個優秀的人,都有一段沉默的時光。那一段時光,是付出了很多努力,忍受孤獨和寂寞,不抱怨不訴苦,日後說起時,連自己都能被感動日子。

設計模式學習,近期我會把23中設計模式都寫成部落格,敬請期待~

什麼是原型模式?

  • 原型模式是指:用原型例項指定建立物件的種類,並且通過拷貝這些原型,建立新的物件
  • 原型設計模式是一種建立型設計模式,執行一個物件再建立另外一個可定製的物件,無需知道如何建立的細節。
  • 工作原理:通過將一個原型物件傳給那個要發動建立的物件,這個要發動建立的物件通過請求原型物件拷貝它們自己來實施建立,既 物件.clone();

什麼是克隆?

什麼是克隆呢?可能大家都聽說過克隆羊多利吧


就是一個已經存在的東西,完完全玩的複製一份,就稱之為克隆,說的通俗一點就是拷貝貼上~

原型設計模式思路

咋們也模仿羊來克隆出來一隻多利(克隆羊),

先用普通的最簡單的做法來克隆

然後在給大家介紹使用原型模式來克隆

普通方法克隆

建立一直普通的’羊’,用來克隆:

public class Sheep implements Cloneable {
    String name;//羊的名字
    int age;//樣的年齡
    String color;//羊的顏色

    public Sheep(String name, int age,
String color) { this.name = name; this.age = age; this.color = color; } public void setName(String name) { this.name = name; } @Override public String toString() { return "Sheep{" + "name='" + name + '\''
+ ", age=" + age + ", color='" + color + '\'' + '}'; } }

這隻羊非常簡單,只有名字,年齡和顏色

來看看是如何克隆的~

Sheep sp1 = new Sheep("tom", 3, "red");
Sheep sp2 = new Sheep("tom", 3, "red");

 Log.i("原型模式普通方法:",sp1.toString()+"\t hashCode:"+sp1.hashCode());
 
Log.i("原型模式普通方法:",sp2.toString()+"\t hashCode:"+sp2.hashCode());

這裡非常好理解,就是new了2個羊達到’克隆’的目的

效果圖(1.1):


可以看出hashCode是不一樣的,說明2個記憶體指向不一樣,那麼就來驗證一下.

驗證方式非常簡單,通過修改名字來判斷會不會衝突

驗證程式碼:

Sheep sp1 = new Sheep("tom", 3, "red");
Sheep sp2 = new Sheep("tom", 3, "red");

Log.i("原型模式普通方法:",sp1.toString()+"\t hashCode:"+sp1.hashCode());
Log.i("原型模式普通方法:",sp2.toString()+"\t hashCode:"+sp2.hashCode());

sp2.setName("jack");//修改'克隆'羊的名字為jack

Log.i("原型模式普通方法:",sp1.toString()+"\t hashCode:"+sp1.hashCode());
Log.i("原型模式普通方法:",sp2.toString()+"\t hashCode:"+sp2.hashCode());

修改’克隆’羊的名字為jack

效果圖(1.2):


通過這張圖可以看出:即使修改’克隆’羊也不會影響被克隆羊的引數(這裡指名字).

用程式碼來解釋就是:記憶體地址不一樣,記憶體指向也不一樣.這是2個完全沒關係的東西

傳統方式的優缺點

  • 比較好理解,簡單易操作
  • 在建立新的物件時,總是需要重新獲取原始物件的屬性,如果建立的物件過多,效率不高。
  • 總是需要重新初始化物件,而不是動態的獲取物件執行時的狀態。

淺克隆

對於基本資料型別是成員變數,淺拷貝會直接進行值拷貝,也就是將該屬性值複製一份給新的物件。

原型模式使用:

  • 實現Cloneable介面
  • 實現clone()方法

淺拷貝程式碼實現:

public class Sheep implements Cloneable {
    String name;//羊的名字
    int age;//樣的年齡
    String color;//羊的顏色

    public Sheep(String name, int age, String color) {
        this.name = name;
        this.age = age;
        this.color = color;
    }

    public Sheep(String name, int age, String color) {
        this.name = name;
        this.age = age;
        this.color = color;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Sheep{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", color='" + color + '\'' +
                '}';
    }

    @NonNull
    @Override
    public Object clone() {
        try {
            //引用物件拷貝 將物件的方法拷貝給當前的值
            Sheep sheep = (Sheep) super.clone();
            return sheep;
        } catch (Exception e) {
            Log.i("原型模式:", e.getMessage());
            return null;
        }
    }
}

實現程式碼:

 Sheep sheep1 = new Sheep("tom", 3, "red",lazinessSheep);

Sheep sheep2 = (Sheep) sheep1.clone();//'克隆羊'

Log.i("原型模式:", "============  克隆之前  ===============");
Log.i("原型模式:", "sheep1=>" + sheep1.toString() + "\t hashCode=>" + sheep1.hashCode());
Log.i("原型模式:", "sheep2=>" + sheep2.toString() + "\t hashCode=>" + sheep2.hashCode());

Log.i("原型模式:", "===========================");

sheep1.setName("jack");

Log.i("原型模式:", "============  克隆之後  ===============");
Log.i("原型模式:", "sheep1=>" + sheep1.toString() + "\t hashCode=>" + sheep1.hashCode());
Log.i("原型模式:", "sheep2=>" + sheep2.toString() + "\t hashCode=>" + sheep2.hashCode());

分析:

  • sheep1 是本身的羊
  • sheep2 是通過sheep1 .clone()克隆出來的羊
  • 先輸出結果,然後改變sheep1本身羊的name,來看看會不會s影響heep2( 克隆羊)

效果圖(1.3):


可以看出這種克隆方式對於基本資料型別能夠完成很好的克隆,

這就是淺克隆

如果說我的羊現在有一隻朋友’懶洋洋’來看看有如何不同:

我的朋友類:

public class LazinessSheep{
    String name = "懶洋洋";
    int age = 2;

    public LazinessSheep() {
    }


    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "LazinessSheep{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

克隆羊類:

public class Sheep implements Cloneable {
    String name;//羊的名字
    int age;//樣的年齡
    String color;//羊的顏色
    public LazinessSheep lazinessSheep;//羊的朋友(懶洋洋)

    public Sheep(String name, int age, String color, LazinessSheep lazinessSheep) {
        this.name = name;
        this.age = age;
        this.color = color;
        this.lazinessSheep = lazinessSheep;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Sheep{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", color='" + color + '\'' +
                ", lazinessSheep=" + lazinessSheep +
                '}';
    }
    @NonNull
    @Override
    public Object clone() {
        try {
            //引用物件拷貝 將物件的方法拷貝給當前的值
            Sheep sheep = (Sheep) super.clone();
            return sheep;
        } catch (Exception e) {
            Log.i("原型模式:", e.getMessage());
            return null;
        }
    }
}

實現程式碼:

LazinessSheep lazinessSheep = new LazinessSheep();

Sheep sheep1 = new Sheep("tom", 3, "red",lazinessSheep);

 //'克隆羊'
Sheep sheep2 = (Sheep) sheep1.clone();
Log.i("原型模式:", "============  克隆之前  ===============");
Log.i("原型模式:", "sheep1=>" + sheep1.toString() + "\t hashCode=>" + sheep1.hashCode());
Log.i("原型模式:", "sheep2=>" + sheep2.toString() + "\t hashCode=>" + sheep2.hashCode());

Log.i("原型模式:", "===========================");

sheep1.lazinessSheep.setName("灰太狼");

Log.i("原型模式:", "============  克隆之後  ===============");
Log.i("原型模式:", "sheep1=>" + sheep1.toString() + "\t hashCode=>" + sheep1.hashCode());
Log.i("原型模式:", "sheep2=>" + sheep2.toString() + "\t hashCode=>" + sheep2.hashCode());

分析:

  • lazinessSheep是我的朋友
  • sheep1是本身的羊
  • sheep2是被克隆的羊
  • 先輸出第一次的結果,然後改變我朋友(lazinessSheep)的名字為灰太狼

來看看有什麼不同:

效果圖(1.4):


通過效果圖(1.4)可以看出我們只改變了

sheep1本身的羊朋友的值(名字),但是sheep2克隆羊朋友的值(名字)也改變了

現在的問題如下:


可以看出,淺拷貝無法複製引用型別,因為他沒有複製引用型別,只是吧現在的狀態指向了記憶體地址,不管你複製10個還是20個克隆羊,引用型別還是指向的原本的記憶體地址

這就是淺克隆為什麼不能克隆引用型別,只能克隆基本資料型別了

深克隆

所謂深克隆,就是解決淺克隆不能複製引用資料型別的問題,達到克隆的每一個個羊,都是單獨的,和原本並沒有區別.

深克隆程式碼:

public class LazinessSheep implements Cloneable{
    String name = "懶洋洋";
    int age = 2;

    public LazinessSheep() {
    }

    public void setName(String name) {
        this.name = name;
    }
    @Override
    public String toString() {
        return "LazinessSheep{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
    @NonNull
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

將需要克隆的有引用型別也實現Cloneable介面實現clone()方法

public class Sheep implements Cloneable {
    String name;//羊的名字
    int age;//樣的年齡
    String color;//羊的顏色
    public LazinessSheep lazinessSheep;//羊的朋友(懶洋洋)

    public Sheep(String name, int age, String color, LazinessSheep lazinessSheep) {
        this.name = name;
        this.age = age;
        this.color = color;
        this.lazinessSheep = lazinessSheep;
    }

    public Sheep(String name, int age, String color) {
        this.name = name;
        this.age = age;
        this.color = color;
    }

    public void setName(String name) {
        this.name = name;
    }
    
    @Override
    public String toString() {
        return "Sheep{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", color='" + color + '\'' +
                ", lazinessSheep=" + lazinessSheep +
                '}';
    }

    @NonNull
    @Override
    public Object clone() {
        try {
            //引用物件拷貝 將物件的方法拷貝給當前的值
            Sheep sheep = (Sheep) super.clone();
            lazinessSheep = (LazinessSheep) sheep.lazinessSheep.clone();
            return sheep;
        } catch (Exception e) {
            Log.i("原型模式:", e.getMessage());
            return null;
        }
    }
}

修改羊本身的類:

重點方法:

 @NonNull
    @Override
    public Object clone() {
        try {
            //引用物件拷貝 將物件的方法拷貝給當前的值
            Sheep sheep = (Sheep) super.clone();
            lazinessSheep = (LazinessSheep) sheep.lazinessSheep.clone();
            return sheep;
        } catch (Exception e) {
            Log.i("原型模式:", e.getMessage());
            return null;
        }
    }

因為這裡的引用型別也是由基本資料型別組成的,

所以在這裡通過

lazinessSheep = (LazinessSheep) sheep.lazinessSheep.clone();

把LazinessSheep的clone();賦值給當前類的lazinessSheep即可

實現程式碼:
還是上邊一樣的程式碼:

///   原型模式之深克隆   \\\\\\\\\\\\\\\\\\\\
LazinessSheep lazinessSheep = new LazinessSheep();

Sheep sheep1 = new Sheep("tom", 3, "red",lazinessSheep);

//'克隆羊'
Sheep sheep2 = (Sheep) sheep1.clone();
Log.i("原型模式:", "============  克隆之前  ===============");
Log.i("原型模式:", "sheep1=>" + sheep1.toString() + "\t hashCode=>" + sheep1.hashCode());
Log.i("原型模式:", "sheep2=>" + sheep2.toString() + "\t hashCode=>" + sheep2.hashCode());

Log.i("原型模式:", "===========================");

sheep1.lazinessSheep.setName("灰太狼");

Log.i("原型模式:", "============  克隆之後  ===============");
Log.i("原型模式:", "sheep1=>" + sheep1.toString() + "\t hashCode=>" + sheep1.hashCode());
Log.i("原型模式:", "sheep2=>" + sheep2.toString() + "\t hashCode=>" + sheep2.hashCode());

效果圖(1.5):


可以看到,改變sheep1的引用資料型別lazinessSheep為灰太狼之後並沒有影響克隆之後的值,這樣就達到了深克隆的效果

總結

  • 淺克隆:只能克隆基本資料型別,因為克隆引用資料型別他不會建立新的記憶體地址,而是吧當前克隆的指向被克隆的地址
  • 深克隆:所有資料型別都可以克隆,如果原始物件發生變化,其他克隆物件也會發生相應的變化,無需修改程式碼。
  • 建立新的物件比較複雜時,可以使用原型模式簡化物件的建立過程,同時也能提高效率。
  • 不用重新初始化物件,而是動態地獲取物件執行的狀態。

完整程式碼

最近文章:

java 設計模式之單例模式(一)

java設計模式之工廠模式(二)

java設計模式之建造者模式(四)

java 設計模式之介面卡模式(五)

去設計模式/設計原則目錄頁

原創不易,您的點贊就是對我最大的支援,點個贊支援一下哦~