1. 「補(bǔ)課」進(jìn)行時(shí):設(shè)計(jì)模式(6)——原型模式

        共 1298字,需瀏覽 3分鐘

         ·

        2020-11-02 10:15

        a1765c5a3d8edc72ee8504460c492d2d.webp

        1. 前文匯總

        「補(bǔ)課」進(jìn)行時(shí):設(shè)計(jì)模式系列

        2. 找工作

        這一天,郭靖大俠因?yàn)樵谔一◢u調(diào)戲侍女被黃蓉打出了桃花島,這下可玩大了,從桃花島被趕出來吃啥喝啥啊,得趕緊找份工作,西北風(fēng)可喝不飽肚子哇~~~

        這不,我們的郭大俠就開始寫簡歷,準(zhǔn)備向丐幫、全真教、白駝山和段氏家族投一份簡歷,看看能不能先混碗飯吃,等老婆的氣消了再回去。

        首先,先定義一個(gè)簡歷類:

        public?class?Resume?{
        ????private?String?name;
        ????private?String?position;
        ????private?int?salary;

        ????//?省略?get/set

        ????@Override
        ????public?String?toString()?{
        ????????return?"Resume{"?+
        ????????????????"name='"?+?name?+?'\''?+
        ????????????????",?position='"?+?position?+?'\''?+
        ????????????????",?salary="?+?salary?+
        ????????????????'}';
        ????}
        }

        然后,我們的郭大俠開始了熬夜寫簡歷的生活:

        public?class?Test?{
        ????public?static?void?main(String[]?args)?{
        ????????Resume?resume1?=?new?Resume();
        ????????resume1.setName("小郭");
        ????????resume1.setPosition("一代大俠");
        ????????resume1.setSalary(1000);
        ????????System.out.println(resume1);

        ????????Resume?resume2?=?new?Resume();
        ????????resume2.setName("小郭");
        ????????resume2.setPosition("一代大俠");
        ????????resume2.setSalary(1200);
        ????????System.out.println(resume2);

        ????????Resume?resume3?=?new?Resume();
        ????????resume3.setName("小郭");
        ????????resume3.setPosition("一代大俠");
        ????????resume3.setSalary(1500);
        ????????System.out.println(resume3);

        ????????//?...
        }

        簡歷這么一份一份的寫太累了,工作都沒找到可能先餓死了,不行,小郭同學(xué)需要提高寫簡歷的效率,于是,他去找了一個(gè)打印機(jī)回來:

        public?class?Test?{
        ????public?static?void?main(String[]?args)?{
        ????????//?效率倍增,直接循環(huán)開始寫簡歷
        ????????for?(int?i?=?0;?i?5;?i++)?{
        ????????????Resume?resume4?=?new?Resume();
        ????????????int?salary?=?(int)(1000?+?Math.random()?*?(2000?-?1000?+?1));
        ????????????resume4.setName("小郭");
        ????????????resume4.setPosition("一代大俠");
        ????????????resume4.setSalary(salary);
        ????????????System.out.println(resume4.toString());
        ????????}
        ????}
        }

        這個(gè)時(shí)候,感覺效率好像還是有點(diǎn)低,每次只能一張一張打印,浪費(fèi)時(shí)間,于是乎,我們的郭大俠又去搞了一個(gè)復(fù)印機(jī)回來。

        可是使用復(fù)印機(jī)需要我們原本的簡歷支持這個(gè)功能,聽過這個(gè)功能需要擴(kuò)展 Cloneable 接口:

        public?class?ResumeClone?implements?Cloneable?{
        ????private?String?name;
        ????private?String?position;
        ????private?int?salary;
        ????//?省略?get/set
        ????@Override
        ????protected?ResumeClone?clone(){
        ????????ResumeClone?resumeClone?=?null;
        ????????try{
        ????????????resumeClone?=?(ResumeClone)?super.clone();
        ????????}catch?(CloneNotSupportedException?e){
        ????????????e.printStackTrace();
        ????????}
        ????????return?resumeClone;
        ????}

        ????@Override
        ????public?String?toString()?{
        ????????return?"ResumeClone{"?+
        ????????????????"name='"?+?name?+?'\''?+
        ????????????????",?position='"?+?position?+?'\''?+
        ????????????????",?salary="?+?salary?+
        ????????????????'}';
        ????}
        }

        然后我們的復(fù)印機(jī)就能跑起來了:

        public?class?TestClone?{
        ????public?static?void?main(String[]?args)?{
        ????????int?num?=?5;
        ????????ResumeClone?resumeClone?=?new?ResumeClone();
        ????????while?(num?>?0){
        ????????????ResumeClone?resume1?=?resumeClone.clone();
        ????????????int?salary?=?(int)(1000?+?Math.random()?*?(2000?-?1000?+?1));
        ????????????resume1.setName("小郭");
        ????????????resume1.setPosition("一代大俠");
        ????????????resume1.setSalary(salary);
        ????????????System.out.println(resume1.toString());
        ????????????num?--;
        ????????}
        ????}
        }

        這里實(shí)際上我們只有第一個(gè)對象是使用打印機(jī)打印出來的,后面的對象都是通過復(fù)印機(jī)直接復(fù)印出來的。

        這其實(shí)就是設(shè)計(jì)模式中的原型模式。

        3. 原型模式

        原型模式(Prototype Pattern)的簡單程度僅次于單例模式和迭代器模式。正是由于簡單,使用的場景才非常地多,其定義如下:

        Specify the kinds of objects to create using a prototypical instance,andcreate new objects by copying this prototype.(用原型實(shí)例指定創(chuàng)建對象的種類,并且通過拷貝這些原型創(chuàng)建新的對象。)

        a4de234741047ea5b5b60b3a0d54f8e1.webp

        這個(gè)絕對是最簡單的設(shè)計(jì)模式,整個(gè)模式的核心就只有一個(gè) clone 方法,通過該方法進(jìn)行對象的拷貝, Java 提供了一個(gè) Cloneable 接口來標(biāo)示這個(gè)對象是可拷貝的,為什么說是「標(biāo)示」呢?翻開 JDK 的幫助看看 Cloneable 是一個(gè)方法都沒有的,這個(gè)接口只是一個(gè)標(biāo)記作用,在 JVM 中具有這個(gè)標(biāo)記的對象才有可能被拷貝。那怎么才能從「有可能被拷貝」轉(zhuǎn)換為「可以被拷貝」呢?方法是覆蓋 clone() 方法。

        通用代碼:

        public?class?PrototypeClass?implements?Cloneable{
        ????@Override
        ????protected?PrototypeClass?clone()?{
        ????????PrototypeClass?prototypeClass?=?null;
        ????????try?{
        ????????????prototypeClass?=?(PrototypeClass)?super.clone();
        ????????}?catch?(CloneNotSupportedException?e)?{
        ????????????e.printStackTrace();
        ????????}
        ????????return?prototypeClass;
        ????}
        }

        優(yōu)點(diǎn):

        1. 性能優(yōu)良

        原型模式是在內(nèi)存二進(jìn)制流的拷貝,要比直接 new 一個(gè)對象性能好很多,特別是要在一個(gè)循環(huán)體內(nèi)產(chǎn)生大量的對象時(shí),原型模式可以更好地體現(xiàn)其優(yōu)點(diǎn)。

        1. 逃避構(gòu)造函數(shù)的約束

        這既是它的優(yōu)點(diǎn)也是缺點(diǎn),直接在內(nèi)存中拷貝,構(gòu)造函數(shù)是不會(huì)執(zhí)行的。優(yōu)點(diǎn)就是減少了約束,缺點(diǎn)也是減少了約束。

        4. 構(gòu)造函數(shù)

        先看一個(gè)簡單的有關(guān)構(gòu)造函數(shù)的示例:

        public?class?ConstructorDemo?implements?Cloneable?{
        ????public?ConstructorDemo()?{
        ????????System.out.println("我被執(zhí)行了。。。");
        ????}

        ????@Override
        ????protected?ConstructorDemo?clone(){
        ????????ConstructorDemo?demo?=?null;
        ????????try?{
        ????????????demo?=?(ConstructorDemo)?super.clone();
        ????????}catch?(CloneNotSupportedException?e){
        ????????????e.printStackTrace();
        ????????}
        ????????return?demo;
        ????}
        }

        public?class?ConstructorTest?{
        ????public?static?void?main(String[]?args)?{
        ????????ConstructorDemo?demo?=?new?ConstructorDemo();
        ????????ConstructorDemo?demo1?=?demo.clone();
        ????}
        }

        執(zhí)行結(jié)果如下:

        我被執(zhí)行了。。。

        就輸出一次,這里可以證明對象拷貝的時(shí)候構(gòu)造函數(shù)是不會(huì)執(zhí)行的,原因在于拷貝是直接在堆中進(jìn)行,這其實(shí)也可以理解, new 的時(shí)候, JVM 要走一趟類加載流程,這個(gè)流程非常麻煩,在類加載流程中會(huì)調(diào)用構(gòu)造函數(shù),最后生成的對象會(huì)放到堆中,而拷貝就是直接拷貝堆中的現(xiàn)成的二進(jìn)制對象,然后重新一個(gè)分配內(nèi)存塊。

        5. 淺拷貝和深拷貝

        先看一個(gè)淺拷貝的案例:

        public?class?ShallowCopy?implements?Cloneable?{
        ????private?ArrayList?array?=?new?ArrayList<>?();
        ????@Override
        ????public?ShallowCopy?clone()?{
        ????????ShallowCopy?copy?=?null;
        ????????try?{
        ????????????copy?=?(ShallowCopy)?super.clone();
        ????????}?catch?(CloneNotSupportedException?e)?{
        ????????????e.printStackTrace();
        ????????}
        ????????return?copy;
        ????}

        ????public?void?setValue(String?value)?{
        ????????this.array.add(value);
        ????}

        ????public?ArrayList?getValue()?{
        ????????return?this.array;
        ????}
        }

        public?class?ShallowCopyTest?{
        ????public?static?void?main(String[]?args)?{
        ????????ShallowCopy?copy?=?new?ShallowCopy();
        ????????copy.setValue("123");
        ????????ShallowCopy?copy1?=?copy.clone();
        ????????copy1.setValue("456");
        ????????System.out.println(copy.getValue());
        ????}
        }

        執(zhí)行的結(jié)果是:

        [123,?456]

        這種情況就是淺拷貝, Java 只拷貝你指定的對象,至于你指定的對象里面的別的對象,它不拷貝,還是把引用給你,共享變量,這是一種非常不安全的方式,需要特別注意。

        內(nèi)部的數(shù)組和引用對象不會(huì)拷貝,其他的原始基本類型和 String 類型會(huì)被拷貝。

        那么這種情況如何進(jìn)行一個(gè)深拷貝呢?只需要修改一下剛才 clone 的方法:

        //?深拷貝
        @Override
        public?ShallowCopy?clone()?{
        ????ShallowCopy?copy?=?null;
        ????try?{
        ????????copy?=?(ShallowCopy)?super.clone();
        ????????this.array?=?(ArrayList)?this.array.clone();
        ????}?catch?(CloneNotSupportedException?e)?{
        ????????e.printStackTrace();
        ????}
        ????return?copy;
        }

        還是剛才的測試類,這次的運(yùn)行結(jié)果是:

        [123]





        感謝閱讀e38899d0136cbd1d19d4e0fd46875a36.webp



        瀏覽 17
        點(diǎn)贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評論
        圖片
        表情
        推薦
        點(diǎn)贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
          
          

            1. HD国产人妖TS另类视频 | chinese国产惩罚打屁股3 | 欧美激情16p | 麻豆系列在线观看 | 人z〇Z〇Z〇人另类ZOZ〇 |