一、引言:從平面設(shè)計(jì)的“圖層”說(shuō)起
在計(jì)算機(jī)平面設(shè)計(jì)軟件(如Photoshop、Figma)中,一個(gè)核心概念是“圖層”。你可以從一個(gè)基礎(chǔ)的形狀或圖像開始,然后不斷添加新的圖層——比如加上一個(gè)顏色疊加、一個(gè)描邊、一個(gè)投影或者一個(gè)濾鏡。每一個(gè)新添加的圖層都“裝飾”了下方所有圖層的綜合效果,最終呈現(xiàn)出復(fù)雜的視覺效果。這個(gè)思想,與軟件工程中的裝飾者模式不謀而合。
裝飾者模式是一種結(jié)構(gòu)型設(shè)計(jì)模式,它允許你動(dòng)態(tài)地為對(duì)象添加新功能,而無(wú)需修改其結(jié)構(gòu)。它通過(guò)將對(duì)象放入包含行為的特殊包裝類中,以“裝飾”原始對(duì)象。
二、裝飾者模式的核心結(jié)構(gòu)與圖解
讓我們通過(guò)一個(gè)平面設(shè)計(jì)場(chǎng)景來(lái)拆解這個(gè)模式的核心組件。
UML圖解示意(文字描述):
- 組件接口: 這是所有對(duì)象的根基,定義了最基礎(chǔ)的操作。
- 對(duì)應(yīng)設(shè)計(jì):
Graphic(圖形接口),聲明一個(gè)draw()方法。
- 具體組件: 實(shí)現(xiàn)了組件接口的基礎(chǔ)對(duì)象。
- 對(duì)應(yīng)設(shè)計(jì):
Circle(圓形類)、Rectangle(矩形類)。它們實(shí)現(xiàn)了draw()方法,繪制一個(gè)基礎(chǔ)的、無(wú)修飾的圖形。
- 裝飾者基類: 它也實(shí)現(xiàn)了
Graphic接口,并持有一個(gè)Graphic對(duì)象的引用。這是所有裝飾器的父類。
- 關(guān)鍵點(diǎn): 它不改變核心行為,只是將調(diào)用轉(zhuǎn)發(fā)給持有的組件對(duì)象。
- 具體裝飾者: 繼承自裝飾者基類,負(fù)責(zé)添加新的功能。
- 對(duì)應(yīng)設(shè)計(jì):
BorderDecorator(邊框裝飾器):在調(diào)用draw()前/后,為圖形添加一個(gè)邊框。
ColorFillDecorator(顏色填充裝飾器):在繪制圖形前,為其填充特定顏色。
ShadowDecorator(陰影裝飾器):為圖形添加一個(gè)投影效果。
模式流程(類比Photoshop操作):
1. 你創(chuàng)建了一個(gè) Circle 對(duì)象(一個(gè)空白圓形圖層)。
2. 你想要紅色填充,于是將 circle 對(duì)象傳入 new ColorFillDecorator(circle, "RED")。現(xiàn)在你得到一個(gè)具有填充功能的“包裝”對(duì)象。
3. 你想要加粗邊框,于是再將上一步的結(jié)果傳入 new BorderDecorator(filledCircle, "BLACK", 2)。
4. 你還想添加陰影,于是得到 shadowedGraphic = new ShadowDecorator(borderedCircle, 45)。
5. 調(diào)用 shadowedGraphic.draw() 時(shí),它會(huì)像棧一樣從外到內(nèi)執(zhí)行:
* 先計(jì)算陰影位置(裝飾器A),
- 然后繪制邊框(裝飾器B),
- 接著填充紅色(裝飾器C),
- 最后繪制基礎(chǔ)的圓形(核心組件)。
整個(gè)過(guò)程,你沒(méi)有修改 Circle 類的任何代碼,卻通過(guò)層層“裝飾”,動(dòng)態(tài)地組合出了復(fù)雜的功能。
三、Java代碼示例:構(gòu)建可裝飾的圖形
`java
// 1. 組件接口
interface Graphic {
void draw();
}
// 2. 具體組件
class Circle implements Graphic {
@Override
public void draw() {
System.out.println("繪制一個(gè)基礎(chǔ)圓形");
}
}
// 3. 裝飾者基類(抽象)
abstract class GraphicDecorator implements Graphic {
protected Graphic decoratedGraphic; // 持有一個(gè)組件對(duì)象的引用
public GraphicDecorator(Graphic graphic) {
this.decoratedGraphic = graphic;
}
@Override
public void draw() {
decoratedGraphic.draw(); // 默認(rèn)行為:轉(zhuǎn)發(fā)調(diào)用
}
}
// 4. 具體裝飾者
class BorderDecorator extends GraphicDecorator {
private String color;
private int width;
public BorderDecorator(Graphic graphic, String color, int width) {
super(graphic);
this.color = color;
this.width = width;
}
@Override
public void draw() {
// 先執(zhí)行額外功能
System.out.println("添加" + width + "px寬," + color + "色的邊框");
// 再調(diào)用被裝飾對(duì)象的原始方法
super.draw();
}
}
class ColorFillDecorator extends GraphicDecorator {
private String fillColor;
public ColorFillDecorator(Graphic graphic, String fillColor) {
super(graphic);
this.fillColor = fillColor;
}
@Override
public void draw() {
System.out.println("填充顏色:" + fillColor);
super.draw();
}
}
// 5. 客戶端使用
public class DecoratorDemo {
public static void main(String[] args) {
System.out.println("=== 基礎(chǔ)圖形 ===");
Graphic circle = new Circle();
circle.draw();
System.out.println("\n=== 裝飾后的圖形 ===");
// 動(dòng)態(tài)組合裝飾:一個(gè)帶紅色填充和黑色邊框的圓形
Graphic fancyCircle = new BorderDecorator(
new ColorFillDecorator(
new Circle(), "紅色"),
"黑色", 2);
fancyCircle.draw();
// 輸出順序:
// 添加2px寬,黑色的邊框
// 填充顏色:紅色
// 繪制一個(gè)基礎(chǔ)圓形
}
}`
四、裝飾者模式在計(jì)算機(jī)平面設(shè)計(jì)中的優(yōu)勢(shì)
- 開閉原則的典范: 無(wú)需修改現(xiàn)有圖形類(如
Circle),就能通過(guò)創(chuàng)建新的裝飾器類(如GradientDecorator漸變裝飾器)來(lái)無(wú)限擴(kuò)展功能。 - 動(dòng)態(tài)組合優(yōu)于靜態(tài)繼承: 使用繼承來(lái)為圖形添加所有可能的特性組合(如“紅色帶邊框圓形”、“藍(lán)色帶陰影矩形”)會(huì)導(dǎo)致類爆炸。而裝飾者模式通過(guò)運(yùn)行時(shí)組合,靈活且優(yōu)雅。
- 分離核心功能與修飾功能: 圖形類只關(guān)心最基礎(chǔ)的繪制邏輯,所有修飾性功能(樣式、特效)由獨(dú)立的裝飾器類負(fù)責(zé),職責(zé)清晰。
- 順序靈活性: 裝飾的順序可以自由調(diào)整,就像調(diào)整圖層順序會(huì)產(chǎn)生不同視覺效果一樣。
五、
裝飾者模式將軟件設(shè)計(jì)中的“圖層”思想發(fā)揮得淋漓盡致。它通過(guò)嵌套包裝的方式,將核心對(duì)象與可選的修飾功能解耦,提供了一種極其靈活的動(dòng)態(tài)擴(kuò)展對(duì)象功能的途徑。在Java I/O流庫(kù)(如BufferedReader包裝FileReader)、Java GUI開發(fā)以及任何需要?jiǎng)討B(tài)、透明地添加對(duì)象職責(zé)的場(chǎng)景中,都能看到它的身影。理解了這個(gè)模式,下次當(dāng)你使用設(shè)計(jì)軟件的圖層功能時(shí),或許會(huì)對(duì)代碼的抽象之美有更深的體會(huì)。