Pattern-No.05 設計模式之裝飾者模式

一、裝飾者模式定義:動態的將責任附加到對象上。若要擴展功能,裝飾者提供了比繼承更有彈性的替代方案。裝飾者與被裝飾者擁有共同的超類,繼承的目的是繼承類型,而不是行爲。java

二、要點async

  • 具體被裝飾者和抽象裝飾類都繼承於抽象被裝飾者類,繼承的是類型,而不是行爲ide

  • 行爲來自裝飾者和基礎組件,或與其餘裝飾者之間的組合關係。裝飾者一般是用其餘相似於工廠或生成器這樣的模式建立的this

  • 能夠用一個或多個裝飾者包裝一個對象spa

  • 裝飾者能夠在所委託被裝飾者的行爲以前或以後,加上本身的行爲,以達到特定的目的設計

  • 對象能夠在任什麼時候候被裝飾,因此能夠在運行時動態的,不限量的用你喜歡的裝飾者來裝飾對象code

  • 裝飾模式中使用繼承的關鍵是想達到裝飾者和被裝飾對象的類型匹配,而不是得到其行爲對象

  • 裝飾者通常對組件的客戶是透明的,除非客戶程序依賴於組件的具體類型。在實際項目中能夠根據須要爲裝飾者添加新的行爲,作到「半透明」裝飾者繼承

三、設計圖
接口

四、案例實現

1)不一樣的飲料添加不一樣類型的佐料

package com.shma.decorate;

/**
 * 被裝飾者和裝飾者公共的抽象類,被裝飾者和裝飾者都繼承這個抽象類
 * @author admin
 *
 */
public abstract class Beverage {

	//飲料描述
	protected String description = "Unknown Beverage";
	
	public String getDescription() {
		return description;
	}
	
	/**
	 * 計算飲料價格
	 * @return
	 */
	public abstract double cost();
}

package com.shma.decorate;

/**
 * 佐料裝飾者抽象類
 * 全部的具體佐料對象集成這個抽象類
 * 這個抽象類又集成自Beverage抽象類
 * @author admin
 *
 */
public abstract class CondimentDecorator extends Beverage {

	
	protected Beverage beverage;

	/**
	 * 佐料具體實現類須要重寫描述這個方法
	 */
	public abstract String getDescription();
	
}
package com.shma.decorate.beverage;

import com.shma.decorate.Beverage;

/**
 * 具體飲料類
 * @author admin
 *
 */
public class DarkRoast extends Beverage {

	public DarkRoast() {
		description = "Dark Roast Coffee";
	}
	
	@Override
	public double cost() {
		// TODO Auto-generated method stub
		return 0.99;
	}

}

package com.shma.decorate.beverage;

import com.shma.decorate.Beverage;

/**
 * 具體飲料類
 * @author admin
 *
 */
public class Decaf extends Beverage {

	public Decaf() {
		description = "Decaf Coffee";
	}
	
	@Override
	public double cost() {
		// TODO Auto-generated method stub
		return 1.05;
	}

}

package com.shma.decorate.beverage;

import com.shma.decorate.Beverage;

public class Espresso extends Beverage {
  
	public Espresso() {
		description = "Espresso";
	}
  
	@Override
	public double cost() {
		// TODO Auto-generated method stub
		return 1.99;
	}
}

package com.shma.decorate.beverage;

import com.shma.decorate.Beverage;

public class HouseBlend extends Beverage {
	public HouseBlend() {
		description = "House Blend Coffee";
	}

	@Override
	public double cost() {
		// TODO Auto-generated method stub
		return .89;
	}
}
package com.shma.decorate.condiment;

import com.shma.decorate.Beverage;
import com.shma.decorate.CondimentDecorator;

/**
 * 佐料具體實現類
 * @author admin
 *
 */
public class Milk extends CondimentDecorator {

	public Milk(Beverage beverage) {
		this.beverage = beverage;
	}
	
	@Override
	public String getDescription() {
		return beverage.getDescription() + ", Milk";
	}

	@Override
	public double cost() {
		return .10 + beverage.cost() ;
	}

}

package com.shma.decorate.condiment;

import com.shma.decorate.Beverage;
import com.shma.decorate.CondimentDecorator;

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

package com.shma.decorate.condiment;

import com.shma.decorate.Beverage;
import com.shma.decorate.CondimentDecorator;

public class Soy extends CondimentDecorator {

	public Soy(Beverage beverage) {
		this.beverage = beverage;
	}

	public String getDescription() {
		return beverage.getDescription() + ", Soy";
	}

	public double cost() {
		return .15 + beverage.cost();
	}
}

package com.shma.decorate.condiment;

import com.shma.decorate.Beverage;
import com.shma.decorate.CondimentDecorator;
 
public class Whip extends CondimentDecorator {

	public Whip(Beverage beverage) {
		this.beverage = beverage;
	}
 
	public String getDescription() {
		return beverage.getDescription() + ", Whip";
	}
 
	public double cost() {
		return .10 + beverage.cost();
	}
}
package com.shma.decorate;

import com.shma.decorate.beverage.DarkRoast;
import com.shma.decorate.beverage.Espresso;
import com.shma.decorate.beverage.HouseBlend;
import com.shma.decorate.condiment.Mocha;
import com.shma.decorate.condiment.Soy;
import com.shma.decorate.condiment.Whip;

public class TestAppMain {

	public static void main(String[] args) {
		Beverage beverage = new Espresso();
		System.out.println(beverage.getDescription() 
				+ " $" + beverage.cost());
 
		Beverage beverage2 = new DarkRoast();
		beverage2 = new Mocha(beverage2);
		beverage2 = new Mocha(beverage2);
		beverage2 = new Whip(beverage2);
		System.out.println(beverage2.getDescription() 
				+ " $" + beverage2.cost());
 
		Beverage beverage3 = new HouseBlend();
		beverage3 = new Soy(beverage3);
		beverage3 = new Mocha(beverage3);
		beverage3 = new Whip(beverage3);
		System.out.println(beverage3.getDescription() 
				+ " $" + beverage3.cost());
	}
}


2)java.io中使用裝飾者模式解析

  • 一個類的功能擴展能夠有兩種方式 :

    (1) 類的繼承 ( 高耦合,會產生更多的子類,從而引發類的爆炸 )

    (2) 對象組合即裝飾模式 ( 降耦,不會創造更多的子類 ) 動態的爲對象添加功能) 因此類應該對擴展開放,對修改關閉 。

  • 裝飾者模式中的四個概念對應io中的類

    (1) 抽象的構件角色( Component):它是一個接口,定義了要實現的方法和功能,在io中,InputStream、OutputStream、Writer、Reader接口類實現了抽象構建角色

    (2) 具體的構件角色(ConcreteComponent):它實現了Component接口,是其具體的功能實現。在io中BufferedReader、BufferedWriter、FileInputStream、FileOutputStream等類實現了具體的構件角色

    (3) 裝飾角色(Decorator):它是一個類,該類也實現了 Component 接口,同時也必須持有接口 Component 的對象的引用,該類也實現了 Component 接口中的方法。

     i:該類的構造方法須要傳遞過來一個 Component 對象的引用

    ii:重寫的方法(便是添加的功能)須要調用 Component 對象的該方法

    在io中,FilterInputStream、FilterReader、FilterOutputStream、FilterWriter等接口類定義了該功能

    (4) 具體的裝飾角色( Decorator 類的子類,能夠有一個,也能夠有多個):這些類繼承了類 Decorator, 要重寫父類的方法(要添加的功能),和自身的構造方法

     i:構造方法要用到 super

    ii:第一步: super 父類的該方法;第二步:添加本身的功能(一些方法、屬性)

  • 以InputStream爲例:

    (1) InputStream爲抽象類

public abstract class InputStream implements Closeable {
    public abstract int read() throws IOException;
    public int read(byte b[]) throws IOException {
	return read(b, 0, b.length);
    }
    
    public int read(byte b[], int off, int len) throws IOException {
	if (b == null) {
	    throw new NullPointerException();
	} else if (off < 0 || len < 0 || len > b.length - off) {
	    throw new IndexOutOfBoundsException();
	} else if (len == 0) {
	    return 0;
	}

	int c = read();
	if (c == -1) {
	    return -1;
	}
	b[off] = (byte)c;

	int i = 1;
	try {
	    for (; i < len ; i++) {
		c = read();
		if (c == -1) {
		    break;
		}
		b[off + i] = (byte)c;
	    }
	} catch (IOException ee) {
	}
	return i;
    }
    
    public long skip(long n) throws IOException {

	long remaining = n;
	int nr;
	if (skipBuffer == null)
	    skipBuffer = new byte[SKIP_BUFFER_SIZE];

	byte[] localSkipBuffer = skipBuffer;
		
	if (n <= 0) {
	    return 0;
	}

	while (remaining > 0) {
	    nr = read(localSkipBuffer, 0,
		      (int) Math.min(SKIP_BUFFER_SIZE, remaining));
	    if (nr < 0) {
		break;
	    }
	    remaining -= nr;
	}
	
	return n - remaining;
    }
    
    public void close() throws IOException {}
    
    ...
}

    (2) FileInputStream實現了InputStream的接口

public class FileInputStream extends InputStream {
    /* File Descriptor - handle to the open file */
    private FileDescriptor fd;

    private FileChannel channel = null;
    
    public FileInputStream(String name) throws FileNotFoundException {
        this(name != null ? new File(name) : null);
    }
    
    public native int read() throws IOException; //經過調用c讀取文件數據
    
    private native int readBytes(byte b[], int off, int len) throws IOException;
    
    public int read(byte b[]) throws IOException {
	return readBytes(b, 0, b.length);
    }
    
    ......
}

    (3) FilterInputStream是裝飾者抽象類,它繼承InputStream,同時擁有InputStream引用對象,過濾流類,起裝飾器做用,用於對輸入裝配各類功能

public class FilterInputStream extends InputStream {
    /**
     * The input stream to be filtered. 
     */
    protected volatile InputStream in;
    
    protected FilterInputStream(InputStream in) {
	this.in = in;
    }  
    
    public int read() throws IOException {
	return in.read();
    }
    
    .......
}

    (4) BufferedInputStream:使輸入流具備緩衝功能,是一種能夠裝配緩衝功能的裝飾器

public class BufferedInputStream extends FilterInputStream {
    private static int defaultBufferSize = 8192;
    protected volatile byte buf[];
    
    public BufferedInputStream(InputStream in) {
	this(in, defaultBufferSize);
    }
    
    public BufferedInputStream(InputStream in, int size) {
	super(in);
        if (size <= 0) {
            throw new IllegalArgumentException("Buffer size <= 0");
        }
	buf = new byte[size];
    }
    
    private void fill() throws IOException {
        byte[] buffer = getBufIfOpen();
	if (markpos < 0)
	    pos = 0;		/* no mark: throw away the buffer */
	else if (pos >= buffer.length)	/* no room left in buffer */
	    if (markpos > 0) {	/* can throw away early part of the buffer */
		int sz = pos - markpos;
		System.arraycopy(buffer, markpos, buffer, 0, sz);
		pos = sz;
		markpos = 0;
	    } else if (buffer.length >= marklimit) {
		markpos = -1;	/* buffer got too big, invalidate mark */
		pos = 0;	/* drop buffer contents */
	    } else {		/* grow buffer */
		int nsz = pos * 2;
		if (nsz > marklimit)
		    nsz = marklimit;
		byte nbuf[] = new byte[nsz];
		System.arraycopy(buffer, 0, nbuf, 0, pos);
                if (!bufUpdater.compareAndSet(this, buffer, nbuf)) {
                    // Can't replace buf if there was an async close.
                    // Note: This would need to be changed if fill()
                    // is ever made accessible to multiple threads.
                    // But for now, the only way CAS can fail is via close.
                    // assert buf == null;
                    throw new IOException("Stream closed");
                }
                buffer = nbuf;
	    }
        count = pos;
	int n = getInIfOpen().read(buffer, pos, buffer.length - pos);
        if (n > 0)
            count = n + pos;
    }
    
     public synchronized int read() throws IOException {
	if (pos >= count) {
	    fill();
	    if (pos >= count)
		return -1;
	}
	return getBufIfOpen()[pos++] & 0xff;
    }
    
    ......
}
相關文章
相關標籤/搜索