歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux編程 >> Linux編程 >> Java 的設計模式之一裝飾者模式

Java 的設計模式之一裝飾者模式

日期:2017/3/1 9:08:00   编辑:Linux編程

剛開始接觸裝飾者的設計模式,感覺挺難理解的,不夠後來花了一個晚上的時間,終於有頭緒了

裝飾者設計模式:如果想對已經存在的對象進行裝飾,那麼就定義一個類,在類中對已經有的對象進行功能的增強或添加另外的行為,這個類就叫裝飾者類。被修飾的類叫被裝飾者類,是已經存在有的功能。在裝飾者類之間又可以互相裝飾

特點:
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();
        
    }
}

通過上面的代碼,可能不能完全看出使用裝飾者模式的強大功能,你可以在多創建幾個類,添加多個功能,結果就顯而易見了。

在實現多個功能的時候,使用繼承的體系會過於龐大,顯得很臃腫。

使用了裝飾者來進行動態性的添加一些附加功能,確保在運行時,不用改變該對象的結構就可以在外部添加附加的功能。通常也是通過繼承來實現給定類的功能擴展來實現更強大的功能。提高了代碼的可維護性和簡潔性。

Copyright © Linux教程網 All Rights Reserved