剛開始接觸裝飾者的設計模式,感覺挺難理解的,不夠後來花了一個晚上的時間,終於有頭緒了
裝飾者設計模式:如果想對已經存在的對象進行裝飾,那麼就定義一個類,在類中對已經有的對象進行功能的增強或添加另外的行為,這個類就叫裝飾者類。被修飾的類叫被裝飾者類,是已經存在有的功能。在裝飾者類之間又可以互相裝飾
特點:
1.裝飾類通過構造方法來接收被裝飾者的對象,調用它裡面的功能或行為
2. 基於對被裝飾對象的功能進行擴展,提供更強大的功能
Java中的IO流是典型的裝飾者模式
下面來看一行簡短的代碼:
擴展一個接口,定義一個抽象的方法,這個接口實際上就是一個被裝飾者類
interface Work { public void work(); }
畫畫類:
class Drawing implements Work { //實現接口 @Override public void work() { //必須實現接口中的方法 // TODO Auto-generated method stub System.out.println("畫畫"); } }
上色類:
class Colour implements Work { Work w;//在內部維護一個被裝飾的類 public Colour(Work w) { this.w = w; } @Override public void work() { w.work(); System.out.println("給畫上色"); } }
裝裱類:
class Mounting implements Work { Work w;//在內部維護一個被裝飾的類 public Mounting(Work w) { this.w = w; } @Override public void work() { w.work(); System.out.println("給畫裝裱"); } }
測試類:
public class Test { public static void main(String[] args) throws FileNotFoundException { Work w = new Drawing(); Colour c = new Colour(w); Mounting m = new Mounting(c); m.work(); } }
上面是一個簡單的裝飾者模式。
裝飾者模式的設計原則:
1.多用組合,少用繼承
繼承的子類實現父類的行為,是在編譯時靜態決定的,而且繼承的行為是相同的,但是利用裝飾者之間的相互修飾(組合)
就可以擴展出強大的功能,可以動態的進行擴展,也可以撤銷
2.對擴展開放,對修改關閉
擴展是在繼承的前提下實現,在子類中修改。擴展是在繼承被裝飾者類實現行為或功能,不需要修改裝飾者的類,只是在此基礎 上,裝飾另外的功能或行為
需求:定義一個人吃面,可以加生菜,也可以加辣椒,也可以都加
EatNoodle類:
class EatNoodle { public void eat() { System.out.print("吃面"); } }
裝飾者類
class SunEatNoodle1 { EatNoodle eatNd; public SunEatNoodle1(EatNoodle eatNd) { //定義構造方法來接收被裝飾者的對象 this.eatNd = eatNd; } public void eat() { eatNd.eat(); System.out.println("加點香菜,感覺好吃"); } }
裝飾者之間的相互修飾
class SunEatNoodle2 { //EatNoodle eatNd; SunEatNoodle1 sunEat1; public SunEatNoodle2( SunEatNoodle1 sunEat1) { //定義構造方法來接收裝飾者的對象 //this.eatNd = eatNd; this.sunEat1 = sunEat1; } public void eat() { System.out.println(); //eatNd.eat(); sunEat1.eat(); System.out.println("再來點辣椒,就更好吃了"); } }
測試類:
public class Demo1 { public static void main(String[] args) { EatNoodle eatNoodle = new EatNoodle(); //eatNoodle.eat(); SunEatNoodle1 sunEat1 = new SunEatNoodle1(eatNoodle); sunEat1.eat(); SunEatNoodle2 sunEat2 = new SunEatNoodle2(sunEat1);//裝飾者類之間的相互修飾 sunEat2.eat(); } }
運行結果:
裝飾者與繼承的關系
在添加 不同功能的時候。我們會想到了用繼承來實現。而且剛學裝飾者模式的時候,覺得挺難理解的,裝飾者的前世今生就是繼承。那為什麼不用繼承呢?這樣豈不是更容易理解,用起來也挺方便。下面通過一個簡單的Demo做一下比較。
需求:用繼承來實現通過readLine讀取代碼 ,每一行加上一個行號和分號的功能
//1.添加行號,默認繼承BufferedReader class BufferedReaderLineNum extends BufferedReader { int count = 0; public BufferedReaderLineNum(BufferedReader in) { //Reader:默認創建一個目標文件的緩沖字符輸入流的大小 super(in); } @Override//3.重寫父類的readLine方法 public String readLine() throws IOException { String countent = super.readLine();//調用父類默認的行號 if (countent == null) { return null; } //System.out.println(count + " " + countent); count++; return count + " " + countent; } } //添加分號 class BufferedReaderSemicolon extends BufferedReader {
BufferedReader reader; public BufferedReaderSemicolon(BufferedReader in) {//Reader:默認創建一個目標文件的緩沖字符輸入流的大小 super(in); this.reader = in; } @Override//3.重寫父類的readLine方法 public String readLine() throws IOException { String countent = reader.readLine(); if (countent == null) { //判斷數據已經讀完 return null; } return countent + ";"; } } public class Demo1 { public static void main(String[] args) throws IOException { //FileReader用於字符輸入流,FileInputStream是用字節輸入流 //1.創建通道,拿到一個指定的目標文件 FileReader fileRead = new FileReader("C:\\java\\decorator\\Test.java"); BufferedReader reader = new BufferedReader(fileRead); //2.創建行號的緩沖流 BufferedReaderLineNum readerLineNum = new BufferedReaderLineNum(reader); //.創建分號的緩沖流 BufferedReaderSemicolon readerSemicolon = new BufferedReaderSemicolon(readerLineNum); String content = null; //使用readLine(),包含該行內容的字符串,不包含任何行終止符,如果已到達流末尾,則返回 null while((content = readerSemicolon.readLine()) != null) { System.out.println(content); } //4.關閉資源 readerLineNum.close(); } }
然後再通過裝飾者模式來實現上面的功能:
//添加行號 class BufferederReadLineNum extends BufferedReader { BufferedReader bufferedReader; //1.內部維護的被裝飾者的類 int count = 1; public BufferederReadLineNum(BufferedReader read) { super(read);//如果沒有這行代碼,會顯示編譯報錯,顯示沒有隱式性的給這個構造函數定義 this.bufferedReader = read; } @Override public String readLine() throws IOException { String countent = bufferedReader.readLine(); if(countent == null) { return null; } countent = count + " " + countent; count++; return countent; } } //2.添加分號 class BufferedReaderSemicolon1 extends BufferedReader { BufferedReader bufferedReader; //1.內部維護的被裝飾者的類 public BufferedReaderSemicolon1(BufferedReader read) { super(read); this.bufferedReader = read; } @Override//3.重寫父類的readLine方法 public String readLine() throws IOException { String countent = super.readLine(); //調用父類默認的行號 if (countent == null) { //判斷數據已經讀完 return null; } return countent + ";"; } } public class Test2 { public static void main(String[] args) throws IOException { //1.創建通道,拿到一個指定的目標文件 FileReader fileRead = new FileReader("C:\\java\\代碼\\src\\Test.java"); //2.創建緩沖區 BufferedReader bufferedReader = new BufferedReader(fileRead); //3創建分號的緩沖流 BufferedReaderSemicolon1 readerSemicolon = new BufferedReaderSemicolon1(bufferedReader); //創建行號的緩沖流 在裝飾者創建的 BufferederReadLineNum readerLineNum = new BufferederReadLineNum(readerSemicolon); //4.讀取數據 String content = null; //使用readLine(),包含該行內容的字符串,不包含任何行終止符,如果已到達流末尾,則返回 null while((content = readerLineNum.readLine()) != null) { System.out.println(content); } //5.關閉資源 readerLineNum.close(); } }
通過上面的代碼,可能不能完全看出使用裝飾者模式的強大功能,你可以在多創建幾個類,添加多個功能,結果就顯而易見了。
在實現多個功能的時候,使用繼承的體系會過於龐大,顯得很臃腫。
使用了裝飾者來進行動態性的添加一些附加功能,確保在運行時,不用改變該對象的結構就可以在外部添加附加的功能。通常也是通過繼承來實現給定類的功能擴展來實現更強大的功能。提高了代碼的可維護性和簡潔性。