剛開始接觸裝飾者的設計模式,感覺挺難理解的,不夠後來花了一個晚上的時間,終於有頭緒了
裝飾者設計模式:如果想對已經存在的對象進行裝飾,那麼就定義一個類,在類中對已經有的對象進行功能的增強或添加另外的行為,這個類就叫裝飾者類。被修飾的類叫被裝飾者類,是已經存在有的功能。在裝飾者類之間又可以互相裝飾
特點:
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();
}
}
通過上面的代碼,可能不能完全看出使用裝飾者模式的強大功能,你可以在多創建幾個類,添加多個功能,結果就顯而易見了。
在實現多個功能的時候,使用繼承的體系會過於龐大,顯得很臃腫。
使用了裝飾者來進行動態性的添加一些附加功能,確保在運行時,不用改變該對象的結構就可以在外部添加附加的功能。通常也是通過繼承來實現給定類的功能擴展來實現更強大的功能。提高了代碼的可維護性和簡潔性。