HeadFirst設計模式筆記:(三)裝飾者模式 —— 裝飾對象

來源:互聯網
上載者:User

詳細內容參見《HeadFirst設計模式》第三章 3裝飾者模式裝飾對象


裝飾者模式,動態地將責任附加到對象上,若要擴充功能,裝飾者提供了比繼承更有彈性的替代方案。


以咖啡店為例。

(1) 裝飾者和被裝飾者對象有相同的超類型。

(2) 可以用一個或多個裝飾者封裝一個對象。

(3) 既然裝飾者和被裝飾者對象有相同的超類型,所以在任何需要原是對象(被封裝的)的場合,可以用裝飾過的對象代替它。

(4) 裝飾者可以在所委託被裝飾者的行為之前與/或之後,加上自己的行為,以達到特定的目的。

(5) 對象可以在任何時候被裝飾,所以可以再運行時動態地、不限量地用你喜歡的裝飾者類裝飾對象。


0. 設計原則

類應該對擴充開放,對修改關閉。

1. 抽象的咖啡基類。

// Beverage是一個抽象類別,有兩個方法:getDescription()和cost()。public abstract class Beverage {    String description = "Unknown Beverage";    // getDescription()已經在此實現了,但是cost()必須在子類中實現。    public String getDescription() {        return description;    }    public abstract double cost();}

2. 4種具體的咖啡,繼承自Berverage基類。

// 首先,讓Espresso擴充自Beverage類,因為Espresso是一種飲料。public class Espresso extends Beverage {    // 為了要設定飲料的描述,我們寫了一個構造器,記住,description執行個體變數繼承自Beverage類。    public Espresso() {        description = "Espresso";    }    // 最後,需要計算Espresso的價錢,現在不需要管調料的價錢,直接把Espresso的價格$1.99返回即可。    public double cost() {        return 1.99;    }}

public class HouseBlend extends Beverage {    public HouseBlend() {        description = "House Blend Coffee";    }    public double cost() {        return .89;    }}
public class DarkRoast extends Beverage {    public DarkRoast() {        description = "Dark Roast Coffee";    }    public double cost() {        return .99;    }}
public class Decaf extends Beverage {    public Decaf() {        description = "Decaf Coffee";    }    public double cost() {        return 1.05;    }}

3. 調料的抽象基類,繼承自Beverage基類。
// 首先,必須讓CondimentDecorator能夠取代Beverage,所以將CondimentDecorator擴充自Beverage類。public abstract class CondimentDecorator extends Beverage {    // 所有的調料裝飾類都必須重新實現getDescription()方法。    public abstract String getDescription();}

4. 4中調料裝飾者類,繼承自CondimentDecorator基類。注意,他們除了必須實現cost()之外,還必須實現getDescription()。

// 摩卡是一個裝飾者,所以它擴充紫CondimentDecorator。public class Mocha extends CondimentDecorator {// 別忘了,CondimentDecorator擴充自Beverage。    // 要讓Mocha能夠引用一個Beverage,做法如下:    // (1)用一個執行個體變數記錄飲料,也就是被裝飾者。    // (2)想辦法讓被裝飾者(飲料)被記錄在執行個體變數中。這裡的做法是:把飲料當作構造器的參數,再由構造器將此飲料記錄在執行個體變數中。    Beverage beverage;    public Mocha(Beverage beverage) {        this.beverage = beverage;    }    public String getDescription() {        // 我們希望敘述不只是描述飲料(例如“DarkRoast”),而是完整地連調料都描述出來(例如“DarkRoast,Mocha”)。        // 所以首先利用委託的做法,得到一個敘述,然後在其後加上附加的敘述(例如“Mocha”)。        return beverage.getDescription() + ", Mocha";    }    public double cost() {        // 要計算帶Mocha飲料的價錢。首先把調用委託給被裝飾對象,以計算價錢,然後再加上Mocha的價錢,得到最後結果。        return .20 + beverage.cost();    }}

public class Milk extends CondimentDecorator {    Beverage beverage;    public Milk(Beverage beverage) {        this.beverage = beverage;    }    public String getDescription() {        return beverage.getDescription() + ", Milk";    }    public double cost() {        return .10 + beverage.cost();    }}
public class Soy extends CondimentDecorator {    Beverage beverage;    public Soy(Beverage beverage) {        this.beverage = beverage;    }    public String getDescription() {        return beverage.getDescription() + ", Soy";    }    public double cost() {        return .15 + beverage.cost();    }}
public class Whip extends CondimentDecorator {    Beverage beverage;    public Whip(Beverage beverage) {        this.beverage = beverage;    }    public String getDescription() {        return beverage.getDescription() + ", Whip";    }    public double cost() {        return .10 + beverage.cost();    }}

5. 所有類的關係架構圖。



6. 測試代碼。

public class StarbuzzCoffee {    public static void main(String args[]) {        // 訂一杯Espresso,不需要調料,列印出它的描述和價錢。        Beverage beverage = new Espresso();        System.out.println(beverage.getDescription() + " $" + beverage.cost());        // 製造一個DarkRoast對象。        Beverage beverage2 = new DarkRoast();        // 用Mocha裝飾它。        beverage2 = new Mocha(beverage2);        // 用第二個Mocha裝飾它。        beverage2 = new Mocha(beverage2);        // 用Whip裝飾它。        beverage2 = new Whip(beverage2);        System.out                .println(beverage2.getDescription() + " $" + beverage2.cost());        // 最後,再來一杯調料為豆漿,摩卡和奶泡的HouseBlend咖啡。        Beverage beverage3 = new HouseBlend();        beverage3 = new Soy(beverage3);        beverage3 = new Mocha(beverage3);        beverage3 = new Whip(beverage3);        System.out                .println(beverage3.getDescription() + " $" + beverage3.cost());    }}


列印的結果。

Espresso $1.99Dark Roast Coffee, Mocha, Mocha, Whip $1.49House Blend Coffee, Soy, Mocha, Whip $1.34


7. java的io包採用的就是裝飾者模式。

自訂的FilterInputStream子類。

public class LowerCaseInputStream extends FilterInputStream {    public LowerCaseInputStream(InputStream in) {        super(in);    }    public int read() throws IOException {        int c = super.read();        return (c == -1 ? c : Character.toLowerCase((char) c));    }    public int read(byte[] b, int offset, int len) throws IOException {        int result = super.read(b, offset, len);        for (int i = offset; i < offset + result; i++) {            b[i] = (byte) Character.toLowerCase((char) b[i]);        }        return result;    }}
測試代碼。

public class InputTest {    public static void main(String[] args) throws IOException {        int c;        try {            InputStream in = new LowerCaseInputStream(new BufferedInputStream(                    new FileInputStream("test.txt")));            while ((c = in.read()) >= 0) {                System.out.print((char) c);            }            in.close();        } catch (IOException e) {            e.printStackTrace();        }    }}



相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.