應(yīng)用場景
裝飾器模式主要解決繼承關(guān)系過于復(fù)雜的問題物延,通過組合來替代繼承。它主要的作用是給原始類添加增強(qiáng)功能。
裝飾器模式有一個(gè)特點(diǎn)澎怒,那就是可以對(duì)原始類嵌套使用多個(gè)裝飾器。為了滿足這個(gè)應(yīng)用場景阶牍,在設(shè)計(jì)的時(shí)候喷面,裝飾器類需要跟原始類繼承相同的抽象類或者接口。
聊聊Java IO類
Java IO 類庫非常龐大和復(fù)雜走孽,有幾十個(gè)類惧辈,負(fù)責(zé) IO 數(shù)據(jù)的讀取和寫入。我們可以把IO類分為四類磕瓷。
字節(jié)流 | 字符流 | |
---|---|---|
輸入流 | InputStream | Reader |
輸出流 | OutputStream | Writer |
針對(duì)不同的讀取和寫入場景盒齿,Java IO 又在這四個(gè)父類基礎(chǔ)之上,擴(kuò)展出了很多子類。
相信大多數(shù)開發(fā)者在初次接觸Java IO類時(shí)面對(duì)龐大的Java IO類家族產(chǎn)生困惑:為什么會(huì)有如此多的類县昂?他們使用上有啥區(qū)別肮柜?
這里以讀取 config.properties
文件為例,InputStream 是一個(gè)抽象類倒彰,F(xiàn)ileInputStream 是專門用來讀取文件流的子類审洞。BufferedInputStream 是一個(gè)支持帶緩存功能的數(shù)據(jù)讀取類,可以提高數(shù)據(jù)讀取的效率待讳。
InputStream in = new FileInputStream("config.properties");
InputStream bin = new BufferedInputStream(in);
byte[] data = new byte[128];
while (bin.read(data) != -1) {
//...
}
Java IO 為什么不設(shè)計(jì)一個(gè)繼承 FileInputStream 并且支持緩存的BufferedFileInputStream
類呢芒澜?這樣我們就可以直接創(chuàng)建new BufferedFileInputStream()
簡單許多。
繼承的問題
事實(shí)上如果只是擴(kuò)展這一個(gè)類创淡,那還可以接受痴晦,但是InputStream
具有很多子類,如果我們繼續(xù)按照繼承的方式來實(shí)現(xiàn)的話琳彩,就需要再繼續(xù)派生出 DataFileInputStream誊酌、DataPipedInputStream 等類。如果我們還需要既支持緩存露乏、又支持按照基本類型讀取數(shù)據(jù)的類碧浊,那就要再繼續(xù)派生出 BufferedDataFileInputStream、BufferedDataPipedInputStream 等 n 多類瘟仿。這還只是附加了兩個(gè)增強(qiáng)功能箱锐,如果我們需要附加更多的增強(qiáng)功能,那就會(huì)導(dǎo)致組合爆炸劳较,類繼承結(jié)構(gòu)變得無比復(fù)雜驹止,代碼既不好擴(kuò)展,也不好維護(hù)观蜗。
所以Java的設(shè)計(jì)者沒有使用繼承的方式來進(jìn)行擴(kuò)展臊恋,而是使用組合,這里也體現(xiàn)了組合優(yōu)于繼承的設(shè)計(jì)原則嫂便。下面是簡化的JDK源碼
public abstract class InputStream {
// ...
}
public class BufferedInputStream extends InputStream {
protected volatile InputStream in;
public BufferedInputStream(InputStream in) {
this.in = in;
}
// 實(shí)現(xiàn)基于緩存的讀取...
}
public class DataInputStream extends InputStream {
protected volatile InputStream in;
public BufferedInputStream(InputStream in) {
this.in = in;
}
// 實(shí)現(xiàn)讀取基本數(shù)據(jù)類型...
}
如果去查看 JDK 的源碼捞镰,你會(huì)發(fā)現(xiàn)闸与,BufferedInputStream毙替、DataInputStream 并非繼承自 InputStream,而是另外一個(gè)叫 FilterInputStream 的類践樱。
這是因?yàn)镮nputStream是一個(gè)抽象類厂画,其中很多方法已經(jīng)有了默認(rèn)實(shí)現(xiàn)。我們想復(fù)用這些實(shí)現(xiàn)拷邢,但是我們是通過在構(gòu)造函數(shù)注入InputStream袱院,然后賦值給成員變量InputStream in
,將所有功能委托給成員變量in
來實(shí)現(xiàn)的,因此抽象類InputStream的方法即使被繼承忽洛,調(diào)用的對(duì)象也是當(dāng)前類實(shí)例腻惠,而不是委托給in
,所以需要有一個(gè)裝飾器類FilterInputStream
欲虚。
public class FilterInputStream extends InputStream {
protected volatile InputStream in;
protected FilterInputStream(InputStream in) {
this.in = in;
}
public int read() throws IOException {
return in.read();
}
public int read(byte b[]) throws IOException {
return read(b, 0, b.length);
}
public int read(byte b[], int off, int len) throws IOException {
return in.read(b, off, len);
}
public long skip(long n) throws IOException {
return in.skip(n);
}
public int available() throws IOException {
return in.available();
}
public void close() throws IOException {
in.close();
}
public synchronized void mark(int readlimit) {
in.mark(readlimit);
}
public synchronized void reset() throws IOException {
in.reset();
}
public boolean markSupported() {
return in.markSupported();
}
}
裝飾器模式是簡單的“用組合替代繼承”嗎集灌?
當(dāng)然不是。從 Java IO 的設(shè)計(jì)來看复哆,裝飾器模式有兩個(gè)比較特殊的地方:
- 裝飾器類和原始類繼承同樣的父類欣喧,這樣我們可以對(duì)原始類“嵌套”多個(gè)裝飾器類。
InputStream in = new FileInputStream("content.txt");
InputStream bin = new BufferedInputStream(in);
DataInputStream din = new DataInputStream(bin);
int data = din.readInt();
- 裝飾器類是對(duì)功能的增強(qiáng)梯找,這也是裝飾器模式應(yīng)用場景的一個(gè)重要特點(diǎn)唆阿。
跟代理模式的區(qū)別
代理模式中,代理類附加的是跟原始類無關(guān)的功能锈锤,而在裝飾器模式中驯鳖,裝飾器類附加的是跟原始類相關(guān)的增強(qiáng)功能。
類圖
代碼實(shí)現(xiàn)
Shape
public abstract class Shape {
public abstract void draw();
public abstract void doOther();
}
Circle
public class Circle extends Shape {
@Override
public void draw() {
System.out.println("You are drawing circle...");
}
@Override
public void doOther() {
System.out.println("You are doing other...");
}
}
Square
public class Square extends Shape {
@Override
public void draw() {
System.out.println("You are drawing square...");
}
@Override
public void doOther() {
System.out.println("You are doing other...");
}
}
ShapeDecorator
public class ShapeDecorator extends Shape {
protected Shape shape;
public ShapeDecorator(Shape shape) {
this.shape = shape;
}
@Override
public void draw() {
shape.draw();
}
@Override
public void doOther() {
shape.doOther();
}
}
RedShapeDecorator
public class RedShapeDecorator extends ShapeDecorator {
public RedShapeDecorator(Shape shape) {
super(shape);
}
@Override
public void draw() {
shape.draw();
System.out.println("The shape color is red");
}
}
Main
public class Main {
public static void main(String[] args) {
Shape shape;
RedShapeDecorator decorator;
shape = new Circle();
decorator = new RedShapeDecorator(shape);
decorator.draw();
decorator.doOther();
shape = new Square();
decorator = new RedShapeDecorator(shape);
decorator.draw();
decorator.doOther();
}
}