1. 程式人生 > >Head First 設計模式--裝飾者模式

Head First 設計模式--裝飾者模式

星巴茲咖啡準備更新訂單系統,為的是不違背開放-關閉的設計原則:類應該對擴充套件開放,對修改關閉。

我們的目標是允許類容易擴充套件,在不修改現有程式碼的情況下,就可搭配新的行為。

這個目標需要使用裝飾者模式實現:以飲料為主體,然後執行調料來“裝飾”飲料。

1.定義裝飾者模式
裝飾者模式動態的將責任附加到物件上,若要擴充套件功能,裝飾者提供了比繼承更具有彈性的替代方案。
 裝飾者模式類圖


2.把星巴茲咖啡系統進行修改,使其符合裝飾者類圖:


3.具體實現:

從飲料下手,將飲料作為一個抽象類:

package com.wk.cafe;

public abstract class Beverage {
	String description = "Unkown Beverage";
	
	public String getDescription(){
		return description;
	}
	
	public abstract double cost();
}
調料抽象類,也就是裝飾者類:
package com.wk.cafe;

public abstract class CondimentDecorator extends Beverage{
	public abstract String getDescription();
	
}
實現具體的飲料:
package com.wk.cafe;

public class Espresso extends Beverage {
	public Espresso(){
		description = "Espresso";
	}

	public double cost(){
		return 1.99;
	}
}
package com.wk.cafe;

public class HouseBlend extends Beverage {  
    public HouseBlend() {  
        description = "HouseBlend";  
    }  
  
    public double cost() {  
        return 0.89;  
    }  
}  
實現具體裝飾者類:
package com.wk.cafe;

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 0.20+beverage.cost();
	}
}


package com.wk.cafe;

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 0.20+beverage.cost();
	}
}
package com.wk.cafe;

public class Mocha extends CondimentDecorator {
	Beverage beverage;
	public Mocha(Beverage beverage){
		this.beverage = beverage;
	}
	public String getDescription(){
		return beverage.getDescription()+",Mocha";
	}

	public double cost(){
		return 0.20+beverage.cost();
	}
}
測試程式碼:
package com.wk.cafe;

public class StartbuzzCoffee {  
    public static void main(String args[]) {  
        Beverage beverage1 = new Espresso();  
        System.out.println(beverage1.getDescription() + "  $"  
                + beverage1.cost());  
        
        beverage1 = new Mocha(beverage1);
        beverage1 = new Whip(beverage1);
        System.out.println(beverage1.getDescription() + "  $"  
                + beverage1.cost());  
        
        Beverage beverage2 = new HouseBlend();  
        beverage2 = new Soy(beverage2);  
        beverage2 = new Mocha(beverage2);  
        beverage2 = new Whip(beverage2);  
        System.out.println(beverage2.getDescription() + "  $"  
                + beverage2.cost());  
    }  
  
} 
測試結果:

 JAVA中的裝飾者模式(java.io類):

Java I/O引出裝飾者模式的一個“缺點”:利用裝飾者模式,會造成設計中存在大量的小類。

我們可以編寫自己的Java I/O裝飾者(即ConcreteDecorator),它的作用是把輸入流中的所有小寫字母轉成大寫:

程式碼如下:

package com.wk.cafe;

import java.io.FilterInputStream;  
import java.io.IOException;  
import java.io.InputStream;  
  
public class UpperCaseInputStream extends FilterInputStream {  
  
    protected UpperCaseInputStream(InputStream in) {  
        super(in);  
        // TODO Auto-generated constructor stub  
    }  
  
    public int read() throws IOException {  
        int c = super.read();  
        return (c == -1 ? c : Character.toUpperCase((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.toUpperCase((char) b[i]);  
        }  
        return result;  
    }  
}  

對剛剛寫好的I/O裝飾者進行測試:
package com.wk.cafe;

import java.io.*;  

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