作用: 裝飾者設計模式的作用是使被裝飾者的功能更強大,比如一把ak47裝上消聲器,裝上瞄準鏡,槍的使用方式不變,改變的是這把槍的功能更加強大,功能更多。 舉例1: 女人可以通過化妝、受教育、禮貌 變身成化妝的女人(顏值+)、高智商的女人(IQ+)、有禮貌的女人(禮貌用語用的更多) 註意:為了顯示出視 ...
作用:
裝飾者設計模式的作用是使被裝飾者的功能更強大,比如一把ak47裝上消聲器,裝上瞄準鏡,槍的使用方式不變,改變的是這把槍的功能更加強大,功能更多。
舉例1:
女人可以通過化妝、受教育、禮貌 變身成化妝的女人(顏值+)、高智商的女人(IQ+)、有禮貌的女人(禮貌用語用的更多)
註意:為了顯示出視頻中代碼演進過程,這裡的代碼都是一步步演進的,請註意包名
這樣我們先定義一個Women類,她有顏值、IQ、名字 屬性、說話的say()方法:
1 package com.mi.decorator.decorator1; 2 3 public class Women { 4 5 //顏值 6 private int beautyIndex; 7 //智商 8 private int iq; 9 //名字 10 private String name; 11 12 //構造方法初始化成員變數 13 public Women(int beautyIndex,int iq,String name){ 14 this.beautyIndex = beautyIndex; 15 this.iq = iq; 16 this.name = name; 17 } 18 19 public String getName() { 20 return name; 21 } 22 23 public int getBeautyIndex() { 24 return beautyIndex; 25 } 26 27 public int getIq() { 28 return iq; 29 } 30 31 //說話方法 32 public void say() { 33 System.out.println("我的名字叫"+name); 34 } 35 }
此時,我們創建一個化妝的女人類,她擁有Women類的所有屬性,但是她的顏值更高(+20),如果每個方法都寫一次,那麼會有大量與Women類相同的代碼,此時最先想到的應是繼承,然後只重寫getBeautyIndex()方法,因為Women類的屬性是使用有參構造方法初始化的,繼承則必須寫有參構造方法,初始化該類內部的Women對象,使用代碼如下:
1 package com.mi.decorator.decorator1; 2 3 public class DressupWomen extends Women{ 4 5 public DressupWomen(int beautyIndex, int iq, String name) { 6 super(beautyIndex, iq, name); 7 } 8 9 @Override 10 public int getBeautyIndex() { 11 //獲取原顏值並+20返回 12 return super.getBeautyIndex()+20; 13 } 14 15 }
同樣使用繼承創建受教育的女人類,她同樣擁有Women類的所有屬性,但是她的IQ更高(+20),同樣需要有參構造方法
1 package com.mi.decorator.decorator1; 2 3 public class EducatedWomen extends Women { 4 5 public EducatedWomen(int beautyIndex, int iq, String name) { 6 super(beautyIndex, iq, name); 7 } 8 9 @Override 10 public int getIq() { 11 //返回原iq +20 12 return super.getIq()+20; 13 } 14 15 }
同樣使用繼承創建一個禮貌的女人,同樣擁有Women的所有屬性,但是她的說話(say)更禮貌
1 package com.mi.decorator.decorator1; 2 3 public class PoliteWomen extends Women { 4 5 public PoliteWomen(int beautyIndex, int iq, String name) { 6 super(beautyIndex, iq, name); 7 } 8 9 @Override 10 public void say() { 11 System.out.println("大家好,"); 12 super.say(); 13 System.out.println("請多多指教!"); 14 } 15 }
寫個測試類Test
1 package com.mi.decorator.decorator1; 2 3 public class Test { 4 5 public static void main(String[] args) { 6 7 Women sisterFeng = new Women(50, 80, "鳳姐"); 8 System.out.println("---------------原始女人-----------------"); 9 // 列印顏值 10 System.out.println(sisterFeng.getBeautyIndex()); 11 // 列印iq 12 System.out.println(sisterFeng.getIq()); 13 sisterFeng.say(); 14 System.out.println("---------------化妝女人-----------------"); 15 Women sisterFeng1 = new DressupWomen(50, 80, "鳳姐"); 16 // 列印顏值 17 System.out.println(sisterFeng1.getBeautyIndex()); 18 // 列印iq 19 System.out.println(sisterFeng1.getIq()); 20 sisterFeng1.say(); 21 System.out.println("---------------智慧女人-----------------"); 22 Women sisterFeng2 = new EducatedWomen(50, 80, "鳳姐"); 23 // 列印顏值 24 System.out.println(sisterFeng2.getBeautyIndex()); 25 // 列印iq 26 System.out.println(sisterFeng2.getIq()); 27 sisterFeng2.say(); 28 System.out.println("---------------禮貌女人-----------------"); 29 Women sisterFeng3 = new PoliteWomen(50, 80, "鳳姐"); 30 // 列印顏值 31 System.out.println(sisterFeng3.getBeautyIndex()); 32 // 列印iq 33 System.out.println(sisterFeng3.getIq()); 34 sisterFeng3.say(); 35 } 36 37 }
輸出:
---------------原始女人----------------- 50 80 我的名字叫鳳姐 ---------------化妝女人----------------- 70 80 我的名字叫鳳姐 ---------------智慧女人----------------- 50 100 我的名字叫鳳姐 ---------------禮貌女人----------------- 50 80 大家好, 我的名字叫鳳姐 請多多指教!
此時我們看到輸出已經看出不同屬性加強的屬性都發生了變化,看起來也沒什麼問題,但是現實中很多女人都是兼有多種屬性的,如果再去繼承Women類差不多上要重寫了整個類,
這樣的代碼不如不繼承Women類,我們可以繼承已經實現了某種屬性增強的Women子類,比如:
1 package com.mi.decorator.decorator1; 2 3 /** 4 * 會打扮有禮貌的女人 5 */ 6 public class DressupPoliteWomen extends PoliteWomen { 7 8 public DressupPoliteWomen(int beautyIndex, int iq, String name) { 9 super(beautyIndex, iq, name); 10 } 11 12 @Override 13 public int getBeautyIndex() { 14 return super.getBeautyIndex()+20; 15 } 16 17 }
1 package com.mi.decorator.decorator1; 2 3 /** 4 * 即受過教育又會打扮的女人 5 */ 6 public class DressupEducateWomen extends DressupWomen { 7 8 public DressupEducateWomen(int beautyIndex, int iq, String name) { 9 super(beautyIndex, iq, name); 10 } 11 12 @Override 13 public int getIq() { 14 return super.getIq()+20; 15 } 16 17 }
1 package com.mi.decorator.decorator1; 2 3 /** 4 * 受教育有禮貌的女人 5 */ 6 public class EducatedPoliteWomen extends PoliteWomen { 7 8 public EducatedPoliteWomen(int beautyIndex, int iq, String name) { 9 super(beautyIndex, iq, name); 10 } 11 12 @Override 13 public int getIq() { 14 return super.getIq()+20; 15 } 16 }
1 package com.mi.decorator.decorator1; 2 3 /** 4 * 會打扮、受過教育、有禮貌的女人 5 */ 6 public class DressupEducatedPoliteWomen extends DressupPoliteWomen { 7 8 public DressupEducatedPoliteWomen(int beautyIndex, int iq, String name) { 9 super(beautyIndex, iq, name); 10 } 11 12 @Override 13 public int getIq() { 14 return super.getIq()+20; 15 } 16 17 }
寫個測試:
1 package com.mi.decorator.decorator1; 2 3 /** 4 * 多屬性女人測試 5 */ 6 public class Test2 { 7 8 public static void main(String[] args) { 9 10 DressupPoliteWomen fengjie1 = new DressupPoliteWomen(50, 80, "鳳姐"); 11 System.out.println(fengjie1.getBeautyIndex()); 12 System.out.println(fengjie1.getIq()); 13 fengjie1.say(); 14 System.out.println("---------------------------------------"); 15 DressupEducateWomen fengjie2 = new DressupEducateWomen(50, 80, "鳳姐"); 16 System.out.println(fengjie2.getBeautyIndex()); 17 System.out.println(fengjie2.getIq()); 18 fengjie2.say(); 19 System.out.println("---------------------------------------"); 20 EducatedPoliteWomen fengjie3 = new EducatedPoliteWomen(50, 80, "鳳姐"); 21 System.out.println(fengjie3.getBeautyIndex()); 22 System.out.println(fengjie3.getIq()); 23 fengjie3.say(); 24 System.out.println("---------------------------------------"); 25 DressupEducatedPoliteWomen fengjie4 = new DressupEducatedPoliteWomen(50, 80, "鳳姐"); 26 System.out.println(fengjie4.getBeautyIndex()); 27 System.out.println(fengjie4.getIq()); 28 fengjie4.say(); 29 } 30 31 }
輸出:
70 80 大家好, 我的名字叫鳳姐 請多多指教! --------------------------------------- 70 100 我的名字叫鳳姐 --------------------------------------- 50 100 大家好, 我的名字叫鳳姐 請多多指教! --------------------------------------- 70 100 大家好, 我的名字叫鳳姐 請多多指教!
看到這裡,有個問題出現了,如果我們添加一個屬性,那我們先需要一個繼承Women類的子類,然後我們還需要繼續擴充多種屬性並存的類,這樣看來,單純使用繼承會使子類的數量成指數增長,這時候怎麼辦呢?我們可不可以讓每個導出類都可以互相包裝,這樣代碼量和子類數量不就減少了麽?答案是肯定的!
為了不混淆視聽,新建一個包decorator2,將decorator1包中的單屬性增強女人類和Women類複製到這個2包中
新建一個抽象類WrapperWomen,繼承自Women類,她的內部持有一個Women對象的引用,重寫所有Women的方法
1 package com.mi.decorator.decorator2; 2 3 public abstract class WrapperWomen extends Women { 4 5 private Women women; 6 7 public WrapperWomen(Women women) { 8 super(women.getBeautyIndex(), women.getIq(), women.getName()); 9 this.women = women; 10 } 11 12 @Override 13 public String getName() { 14 return women.getName(); 15 } 16 17 @Override 18 public int getBeautyIndex() { 19 return women.getBeautyIndex(); 20 } 21 22 @Override 23 public int getIq() { 24 return women.getIq(); 25 } 26 27 @Override 28 public void say() { 29 System.out.println("我的名字叫" + women.getName()); 30 } 31 32 }
讓所有單屬性增強的Women子類繼承WrapperWomen,重寫構造方法為接收一個Women對象的引用,在構造方法內部調用父類WrapperWomen的構造方法
1 package com.mi.decorator.decorator2; 2 3 public class DressupWomen extends WrapperWomen { 4 5 public DressupWomen(Women women) { 6 super(women); 7 } 8 9 @Override 10 public int getBeautyIndex() { 11 return super.getBeautyIndex()+20; 12 } 13 }
1 package com.mi.decorator.decorator2; 2 3 4 public class EducatedWomen extends WrapperWomen { 5 6 public EducatedWomen(Women women) { 7 super(women); 8 } 9 10 @Override 11 public int getIq() { 12 return super.getIq() + 20; 13 } 14 15 }
1 package com.mi.decorator.decorator1; 2 3 4 public class PoliteWomen extends WrapperWomen{ 5 6 public PoliteWomen(Women women) { 7 super(women); 8 } 9 10 @Override 11 public void say() { 12 System.out.println("大家好,"); 13 super.say(); 14 System.out.println("請多多指教!"); 15 } 16 }
測試類:
1 package com.mi.decorator.decorator2; 2 3 public class Test { 4 5 public static void main(String[] args) { 6 7 System.out.println("-------------------打扮-------------------"); 8 //因為這些類都是間接的繼承自Women類,都可以看作Women類型 9 Women sisterFeng = new Women(50,80,"鳳姐"); 10 Women dressup = new DressupWomen(sisterFeng); 11 System.out.println(dressup.getBeautyIndex()); 12 System.out.println(dressup.getIq()); 13 dressup.say(); 14 System.out.println("--------------打扮&受教育----------------"); 15 Women dressupEducated = new EducatedWomen(dressup); 16 System.out.println(dressupEducated.getBeautyIndex()); 17 System.out.println(dressupEducated.getIq()); 18 dressupEducated.say(); 19 System.out.println("--------------打扮&有禮貌----------------"); 20 Women dressupPolite = new PoliteWomen(dressup); 21 System.out.println(dressupPolite.getBeautyIndex()); 22 System.out.println(dressupPolite.getIq()); 23 dressupPolite.say(); 24 System.out.println("--------------受教育&有禮貌--------------"); 25 Women EducatedPolite = new PoliteWomen(new EducatedWomen(sisterFeng)); 26 System.out.println(EducatedPolite.getBeautyIndex()); 27 System.out.println(EducatedPolite.getIq()); 28 EducatedPolite.say(); 29 System.out.println("----------------完美女人------------------"); 30 Women perfectWomen = new PoliteWomen(dressupEducated); 31 System.out.println(perfectWomen.getBeautyIndex()); 32 System.out.println(perfectWomen.getIq()); 33 perfectWomen.say(); 34 System.out.println("-------------------------------------------"); 35 } 36 37 }
輸出:
------------------打扮------------------ 70 80 我的名字叫鳳姐 --------------打扮&受教育---------------- 70 100 我的名字叫鳳姐 --------------打扮&有禮貌---------------- 70 80 大家好, 我的名字叫鳳姐 請多多指教! --------------受教育&有禮貌-------------- 50 100 大家好, 我的名字叫鳳姐 請多多指教! ---------------完美女人----------------- 70 100 大家好, 我的名字叫鳳姐 請多多指教! --------------------------------------—
以上的方式在JDK中的I/O中最為常見,所以說JDK的I/O是裝飾者設計模式的典型實現,也可以如下寫:
1 Women baby = new PoliteWomen(new DressupWomen(new EducatedWomen(new Women(90,100,"baby")))); 2 System.out.println(baby.getBeautyIndex()); 3 System.out.println(baby.getIq()); 4 baby.say();
輸出:
110
120
大家好,
我的名字叫baby
請多多指教!
以上代碼中的Women類其實可以用介面實現,這樣會更靈活,代碼還能少一些
舉例2:
一把ak47可以裝上消聲器,可以裝上瞄準鏡,槍的使用方式不變,改變的是這把槍的功能更加強大,功能更多。
為了區分代碼,這裡新建一個包decorator3
新建一個Gun介面,具有槍應有的方法
1 package com.mi.decorator.decorator3; 2 3 /** 4 * 槍介面 5 */ 6 public interface Gun { 7 8 //瞄準 9 void aim(); 10 //射擊 11 void shoot(); 12 //裝彈 13 void load(); 14 //卸彈 15 void unload(); 16 }
新建上例同理的包裝類實現Gun介面的包裝抽象類,持有父類的引用,在構造方法中初始化父類引用,實現Gun介面的所有方法,使用父類引用調用介面方法(多態機制會運行中指向實現類方法)
題外話:實現多態的條件是什麼?有重寫有繼承,有父類類型指向子類對象?其實還有一種,介面類型指向實現類對象
1 package com.mi.decorator.decorator3; 2 3 public abstract class WrapperGun implements Gun { 4 5 private Gun gun; 6 public WrapperGun(Gun gun){ 7 this.gun = gun; 8 } 9 @Override 10 public void aim() { 11 gun.aim(); 12 } 13 14 @Override 15 public void shoot() { 16 gun.shoot(); 17 } 18 19 @Override 20 public void load() { 21 gun.load(); 22 } 23 24 @Override 25 public void unload() { 26 gun.unload(); 27 } 28 29 }
新建實現Gun介面的Ak47(那個包裝類作為槍上的配件使用,看完代碼就能明白了)
1 package com.mi.decorator.decorator3; 2 3 public class Ak47 implements Gun { 4 5 @Override 6 public void aim() { 7 System.out.println("------ak47瞄準ing-------"); 8 } 9 10 @Override 11 public void shoot() { 12 System.out.println("------ak47開火ing-------"); 13 } 14 15 @Override 16 public void load() { 17 System.out.println("------ak47裝彈ing-------"); 18 } 19 20 @Override 21 public void unload() { 22 System.out.println("------ak47卸彈ing-------"); 23 } 24 25 }
瞄準鏡類,繼承自WrapperGun抽象類
1 package com.mi.decorator.decorator3; 2 3 public class AimGlassGun extends WrapperGun{ 4 5 public AimGlassGun(Gun gun) { 6 super(gun); 7 } 8 9 @Override 10 public void aim() { 11 System.out.println("瞄準中,因為裝配了瞄準鏡,射擊精度提高80%"); 12 super.aim(); 13 } 14 15 }
消聲器類,繼承自WrapperGun抽象類
1 package com.mi.decorator.decorator3; 2 3 public class SilentGun extends WrapperGun{ 4 5 public SilentGun (Gun gun){ 6 super(gun); 7 } 8 9 @Override 10 public void shoot() { 11 System.out.println("因為佩戴了消音器,槍聲減小80%"); 12 super.shoot(); 13 } 14 15 }
寫個測試類:
1 package com.mi.decorator.decorator3; 2 3 public class Test { 4 5 public static void main(String[] args) { 6 //被裝飾對象在包裝的最內部 7 Gun ak47 = new AimGlassGun(new SilentGun(new Ak47())); 8 ak47.aim(); 9 ak47.shoot(); 10 } 11 12 }
輸出:
瞄準中,因為裝配了瞄準鏡,射擊精度提高80%
------ak47瞄準ing-------
因為佩戴了消音器,槍聲減小80%
------ak47開火ing-------
以上這個例子可以說是很清晰的看出裝飾者模式的優點了,裝飾類是不能直接new出來的,它內部必須擁有一個Gun的實現類引用;
如果我需要一把帶瞄準帶消音器的M4A1,我只需要實現Gun介面,然後把M4A1對象的引用交給瞄準鏡和消音器的層層包裝,就可以得到。相比最開始的例子那樣使用繼承每種屬性都要繼承一次導出一個新的類,這簡直是簡單極了!
總結:
裝飾者設計模式特點:動態地給一個對象添加一些額外的職責。就增加功能來說,裝飾器模式相比生成子類更為靈活。比如我想給一把槍添加屬性,我無需修改槍這個類,只需要對他進行包裝,就能獲得需要的Gun,相比於繼承產生子類而言,更加靈活,代碼更少,實現類也更少。
何時使用:在不想增加很多子類的情況下擴展類。
優點:裝飾類和被裝飾類可以獨立發展,不會相互耦合,裝飾模式是繼承的一個替代模式,裝飾模式可以動態擴展一個實現類的功能。
缺點:多層裝飾比較複雜。
使用場景: 1、擴展一個類的功能。 2、動態增加功能,動態撤銷。