最近我給女朋友買了一款可以更換外殼的手機
現在的外殼是紅色的
如果我想用這款手機的時候
會更換成銀灰色的外殼
但是我不能隨意更換天線或者話筒
因為這些功能模塊在手機生產的時候就已經被固定了
軟件中的修飾者(decorator)
和手機的外殼一樣
封裝了一些可以替換的功能
例如下面是一段替換Swing中表模型的代碼
TableSortDecorator sortDecorator = new TableSortDecorator(table
getModel());
table
setModel(sortDecorator);
在這段代碼中
程序首先將表模型包裝在一個修飾對象中
以後當表對它的模型進行操作的時候
它實際上操作的是排序修飾對象(sortDecorator)
該修飾對象在表模型中加入了排序功能
而將其他基本的功能委托給缺省的表模型
在修飾模型中
這個缺省的表模型又被稱為真實對象(real subject)
在Java的編程中
基類和子類的繼承關系在編譯的時候就被固定了
就像手機的天線和話筒一樣
由於繼承關系是靜態的
開發人員無法在程序運行時改變對象的行為
但是通過修飾者開發人員可以在運行時拼裝對象
因此修飾模式提供了一種比繼承更靈活的功能擴充模式
修飾模式(Decorator Pattern)
在運行時將特定的功能綁定在對象上
這就是修飾模式的核心
修飾模式比繼承更加靈活
因為後者是在編譯時就將特定的功能綁定到類上
下面然我們來看一個簡單的I/O例子
FileReader frdr = new FileReader(filename);
LineNumberReader lrdr = new LineNumberReader(frdr);
這段代碼中創建了一個Reader
lrdr
它從一個文件中讀取數據並跟蹤文件的行號
在第一行創建的frdr對象能夠從文件中讀取數據
而第二行給lrdr增加了跟蹤行號的功能
在運行時(runtime)
修飾者將方法調用傳遞給它所修飾的真實對象
在上面的例子中
lrdr將方法調用傳遞給它修飾的真實對象frdr
修飾者除了能夠進行方法傳遞外
還能夠增加類的功能
例如在上面的例子中
lrdr能夠跟蹤當前的文件流讀入數據的行號
而下面的例子顯示了如何在程序中使用修飾者lrdr
程序將數據按行從文件中讀出後
加上行號輸出到屏幕上
try {
LineNumberReader lrdr = new LineNumberReader(new FileReader(filename));
for(String line; (line = lrdr
readLine()) != null;)rticle
txt {
System
out
print(lrdr
getLineNumber() +
:\t
+ line);
}
}
catch(java
io
FileNotFoundException fnfx) {
fnfx
printStackTrace();
}
catch(java
io
IOException iox) {
iox
printStackTrace();
}
修飾者的靜態和動態特性
工程學上經常提到靜態和動態的概念
靜態方法研究那些變化或位移相對較小的對象
例如橋梁或建築
而動態方法研究那些變化和移動較快的對象
例如發動機
在軟件工程中也有相應的概念
靜態方法研究在編譯時類之間的關系
而動態方法研究在運行時類參與的一些的事件
在這一節中
我將用UML類圖來展示修飾者的靜態特性
用UML時序圖來展示修飾者的動態特性
修飾者的靜態特性
修飾者通過增加功能來修飾被修飾對象(Decorated
也就是真實對象)
下面的UML類圖展示了修飾者和真實對象之間的關系
圖
修飾者和被修飾者的關系
修飾者繼承了被修飾者或者實現了被修飾者的接口
同時修飾者還保存了對被修飾者實例的引用
這個實例就是修飾者修飾的對象
為了說明這些類在到底是如何關聯的
圖
中舉了一個Java SDK的java
io
package中的實際例子
圖
一個真實的修飾模型例子
BufferedReader和FilterReader就是圖
中演示的抽象類
他們都繼承了抽象類Reader
並且將方法調用傳遞給Reader對象
由於繼承了修飾者類
因此LineNumberReader和PushbackReader也是修飾者類
修飾者的動態特性
在運行時
修飾者將方法調用傳遞給被修飾者
如圖
所示
圖
修飾者的動態特性
修飾者通常將對被修飾者的調用包裝起來
圖
描述了這種特性
圖
描述了上面的I/O例子中修飾者的動態特性
圖
I/O例子中修飾者的動態特性
現在大家對修飾模式以及它的靜態和動態特性有一個比較明確的認識了
讓我們通過一個完整的例子來說明如何在代碼中實現修飾模式
排序和過濾修飾
修飾者主要是用於給被修飾者增加功能
在下面的例子中
我們會給Swing中的表增加排序和過濾的功能
在介紹例子之前
先簡單介紹一下如何使用Swing中的JTable類
import javax
swing
*;
import javax
swing
table
*;
public class Test extends JFrame {
public static void main(String args[]) {
Test frame = new Test();
frame
setTitle(
Swing表的例子
);
frame
setBounds(
);
frame
setDefaultCloseOperation(JFrame
DISPOSE_ON_CLOSE);
frame
show();
}
public Test() {
TableModel model = new TestModel();
getContentPane()
add(new JScrollPane(new JTable(model)));
}
private static class TestModel extends AbstractTableModel {
final int rows =
cols =
;
public int getRowCount() { return rows; }
public int getColumnCount() { return cols; }
public Object getValueAt(int row
int col) {
return
(
+ row +
+ col +
)
;
}
}
}
該程序創建了一個
×
的表
表對象由三個部分組成
表模型
視圖和事件控制器
表中的數據保存在表模型中
視圖控制數據的顯示
而事件控制器控制對事件的響應
圖
是運行這個程序的結果
圖
Swing表的例子
排序修飾者
圖
中的應用程序包含了一張兩列的表
一列是貨物名稱
一列是價格
通過單擊列頭可以根據貨物的價格對表進行排序
下面是這個程序的代碼
圖
排序修飾者的例子
//Test
java
import java
awt
*;
import java
awt
event
*;
import java
util
Locale;
import java
util
ResourceBundle;
import javax
swing
*;
import javax
swing
table
*;
public class Test extends JFrame {
public static void main(String args[]) {
SwingApp
launch(new Test()
排序修飾者
);
}
public Test() {
// 生成修飾者的實例
該實例用於修飾Swing Table原有的表模型
// 該實例必須是final的
因為它會被內嵌類引用
final TableSortDecorator decorator =
new TableBubbleSortDecorator(table
getModel());
// 將表的模型設定為修飾者
因為修飾者實現了TableModel接口
// 因此Swing Table對象不知道修飾者和真實對象之間的差別
table
setModel(decorator);
getContentPane()
add(new JScrollPane(table)
BorderLayout
CENTER);
// 在界面中添加一個狀態區
getContentPane()
add(SwingApp
getStatusArea()
BorderLayout
SOUTH);
SwingApp
showStatus(
進行排序前
);
// 獲得對表中列頭的引用
JTableHeader hdr = (JTableHeader)table
getTableHeader();
// 當單擊鼠標單擊列頭時
調用修飾者的sort()方法
hdr
addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
TableColumnModel tcm = table
getColumnModel();
int vc = tcm
getColumnIndexAtX(e
getX());
int mc = nvertColumnIndexToModel(vc);
// 進行排序
decorator
sort(mc);
// 更新狀態區
SwingApp
showStatus(headers[mc] +
排序中
);
}
});
}
final String[] headers = {
品名
價格/每斤
};
JTable table = new JTable(new Object[][] {
{
蘋果
}
{
芒果
}
{
檸檬
}
{
香蕉
}
{
桔子
}
From:http://tw.wingwit.com/Article/program/Java/gj/201311/27419.html