設計模式筆記(四)——裝飾者模式

1. 是什麼——定義

裝飾者模式是在沒必要改變原類文件和使用繼承的狀況下,動態地擴展一個對象的功能。它是經過建立一個包裝對象,也就是裝飾來包裹真實的對象。java

2. 爲何——特色

1)  裝飾對象和真實對象有相同的接口。這樣客戶端對象就能以和真實對象相同的方式和裝飾對象交互。ide

2)  裝飾對象包含一個真實對象的引用。this

3)  裝飾對象接受全部來自客戶端的請求,它把這些請求轉發給真實的對象。spa

4)  裝飾對象能夠在轉發這些請求之前或之後增長一些附加功能。這樣就確保了在運行時,不用修改給定對象的結構就能夠在外部增長附加的功能。在面向對象的設計中,一般是經過繼承來實現對給定類的功能擴展。設計

3. 何時用——適用性

1)  須要擴展一個類的功能,或給一個類添加附加職責。代理

2)  須要動態的給一個對象添加功能,這些功能能夠再動態的撤銷。code

3)  須要增長由一些基本功能的排列組合而產生的很是大量的功能,從而使繼承關係變的不現實。對象

4)  當不能採用生成子類的方法進行擴充時。一種狀況是,可能有大量獨立的擴展,爲支持每一種組合將產生大量的子類,使得子類數目呈爆炸性增加。另外一種狀況多是由於類定義被隱藏,或類定義不能用於生成子類。繼承

4. UML圖


 

5. 怎麼用——使用方法

需求:接口

模擬人穿衣服

5.1  沒有任何原則的初始設計

public class Demo1 {
    public static void main(String[] args) {
        Person person = new Person();
        person.setName("二哈");
        person.wearTshirt();
        person.wearJeans();
        person.wearShoes();
    }
}

class Person {
    private String name;
    public void wearTshirt() {
        System.out.println(name + "穿了一件T恤");
    }
    public void wearSweater() {
        System.out.println(name + "穿了一件毛衣");
    }
    public void wearJeans() {
        System.out.println(name + "穿了一條褲子");
    }
    public void wearShoes() {
        System.out.println(name + "穿了一雙鞋");
    }
//    public ..... -- 違反開放-封閉原則
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}

能夠看到,若是要增長新的衣服,就須要擴展方法,這就違背了「開放-封閉原則」。

5.2  將「服裝」抽象

public class Demo2 {
    public static void main(String[] args) {
        Person person = new Person();
        Clothes Tshirt = new TShirt();
        Clothes shoes = new Shoes();
        Clothes superman = new SupermanClothes();
        person.wear(superman);
        person.wear(Tshirt);
        person.wear(shoes);
    }
}

class Person {
    private String name;
    public void wear(Clothes clothes) {
        System.out.println(name + "穿了" + clothes.getName());
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}

abstract class Clothes {
    private String name;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}

class TShirt extends Clothes {
    
}

class Shoes extends Clothes {
    
}

class SupermanClothes extends Clothes {
    
}

這樣確實達到效果了,但每次穿衣服的時候總感受是當着衆人的面穿,並且邏輯也不對:每次都是人在穿,但實際狀況是:穿了一件後,下一件衣服是套在原來那件衣服上!須要改進!

5.3  使用裝飾者模式

public class Demo3 {
    public static void main(String[] args) {
        Person person = new Person();
        person.setName("二狗");
        Clothes tshirt = new Tshirt();
        person.wear(tshirt);
        Clothes coat = new Coat();
        tshirt.wear(coat);
        coat.show();
    }
}
class Person {
    private String name;
    public void show() {
        System.out.println(name);
    }
    public void wear(Clothes clothes) {
        clothes.setWornClothes(this);
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}

abstract class Clothes extends Person {
    private Person wornClothes; //要被穿的衣服/人
    private String clothesName;
    @Override
    public void show() {
        System.out.println(clothesName + "\t");
        wornClothes.show();
    }
    public String getClothesName() {
        return clothesName;
    }
    public void setClothesName(String clothesName) {
        this.clothesName = clothesName;
    }
    public Person getWornClothes() {
        return wornClothes;
    }
    public void setWornClothes(Person wornClothes) {
        this.wornClothes = wornClothes;
    }
}

class Tshirt extends Clothes {
    public Tshirt () {
        this.setClothesName("T恤");
    }
}

class Coat extends Clothes {
    public Coat() {
        this.setClothesName("大衣");
    }
}

class Jeans extends Clothes {
    public Jeans() {
        this.setClothesName("褲子");
    }
}

 

6. 真實案例

需求:

a)  擴展BufferedReader類,使其每次讀取文本信息時能夠在文本前打印行號

b)  擴展BufferedReader類,使其每次讀取文本信息時能夠在文本後打印省略號

c)  擴展BufferedReader類,使其每次讀取文本信息時能夠在文本前和文本後打印引號

6.1  沒有使用裝飾者模式

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;

public class Demo1 {
    public static void main(String[] args) throws Exception {
        BufferedReader br = new ReaderLineNumAndSusp(new InputStreamReader(new FileInputStream("D:/test.txt"), "GBK"));
        String readline = null;
        while ((readline = br.readLine()) != null) {
            System.out.println(readline);
        }
        br.close();
    }
}

//需求1:加強BufferedReader類,使每次讀取的內容前面加上行號
class ReaderLineNum extends BufferedReader {
    private int lineNum = 1;
    public ReaderLineNum(Reader in) {
        super(in);
    }
    @Override
    public String readLine() throws IOException {
        String readline = super.readLine();
        if (readline == null) {
            return null;
        }
        readline = lineNum + readline;
        lineNum++;
        return readline;
    }
}

//需求2:加強BufferedReader類,使每次讀取的內容後面加上省略號
class ReaderSusp extends BufferedReader {
    public ReaderSusp(Reader in) {
        super(in);
    }
    @Override
    public String readLine() throws IOException {
        String readline = super.readLine();
        if (readline == null) {
            return null;
        }
        readline = readline + "……";
        return readline;
    }
}

//需求3:加強BufferedReader類,使每次讀取的內容先後加上雙引號
class ReaderQuot extends BufferedReader {
    public ReaderQuot(Reader in) {
        super(in);
    }
    @Override
    public String readLine() throws IOException {
        String readline = super.readLine();
        if (readline == null) {
            return null;
        }
        readline = "「" + readline + "」";
        return readline;
    }
}

//需求4:加強BufferedReader類,使每次讀取的內容前加行號,內容後加省略號
class ReaderLineNumAndSusp extends BufferedReader {
    private int lineNum = 1;
    public ReaderLineNumAndSusp(Reader in) {
        super(in);
    }
    @Override
    public String readLine() throws IOException {
        String readline = super.readLine();
        if (readline == null) {
            return null;
        }
        readline = lineNum + readline + "……";
        lineNum++;
        return readline;
    }
}

//需求5:。。。

6.2  使用裝飾者模式

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;

public class Demo2 {
    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("D:/test.txt"), "GBK"));
        BufferedReader brLineNum = new ReaderLineNum(br);
        BufferedReader brSusp = new ReaderSusp(brLineNum);
        String readline = null;
        while ((readline = brSusp.readLine()) != null) {
            System.out.println(readline);
        }
        brSusp.close();
    }
}

class ReaderLineNum extends BufferedReader {
    private int lineNum = 1;
    private BufferedReader baseReader;
    public ReaderLineNum(Reader in) {
        super(in);
        this.baseReader = (BufferedReader) in;
    }
    @Override
    public String readLine() throws IOException {
        String readline = baseReader.readLine();
        if (readline == null) {
            return null;
        }
        readline = lineNum + readline;
        lineNum++;
        return readline;
    }
}

class ReaderSusp extends BufferedReader {
    private BufferedReader baseReader;
    public ReaderSusp(Reader in) {
        super(in);
        this.baseReader = (BufferedReader) in;
    }
    @Override
    public String readLine() throws IOException {
        String readline = baseReader.readLine();
        if (readline == null) {
            return null;
        }
        return readline + "……";
    }
}

能夠實現三個類完成7種功能。

 

7. 裝飾者模式與建造者模式的區別

建造者模式須要有一套完整的建造過程和順序

裝飾者模式更隨意,靈活(哪怕內褲外穿。。。)

8. 裝飾者模式與代理模式的區別

裝飾者模式能夠互相裝飾,而代理模式有很明確的主從關係。

相關文章
相關標籤/搜索