1. 程式人生 > >設計模式:裝飾器(Decorator)模式

設計模式:裝飾器(Decorator)模式

讓我 分享圖片 底部 .com 一件事 輸出 PE 新的 int

設計模式:裝飾器(Decorator)模式

一、前言

裝飾器模式也是一種非常重要的模式,在Java以及程序設計中占據著重要的地位。比如Java的數據流處理,我們可能看到數據流經過不同的類的包裝和包裹,最終形成了我們需要的流,比如說從二進制到字節流再到字符流,這中間其實就是經過了裝飾器的處理,在不改變原來的數據的基礎上,加入屬於自己的特征,就像是在一塊蛋糕上加上一些水果等裝飾品,這樣輸出的結果就不同了,我們將這種能產生類似於洋蔥一樣層層包裹的數據格式的設計模式成為裝飾器模式。

那麽為什麽裝飾器這樣神奇呢,幾乎可以讓我們無窮盡的包裹自身,在計算機中凡是能夠無窮盡的重復某一件事物,必然不能設定固定的數據,只能按照用戶的隨意設置來計算,那麽遞歸

的魅力就在此彰顯出來了,通過遞歸可以實現這一點,那麽如何遞歸呢,我們想到一層套一層的數據結構(鏈表、二叉樹等),就必須在“自身”中包含“自身”;前一個“自身”可以是我們的子類,因為子類有著父類的所有特點,後一個“自身”就是具有最抽象資格的“自身”,正是因為足夠抽象使得任何繼承與這個抽象類的類都能通過裏氏代換原則轉換到這個抽象類。實現了遞歸,我們只需要停止條件就可以了,停止條件就是用戶在Main中對原數據包裹的層數,肯定是有限的,因此停止是必然的。這樣我們仿似看到一個原始數據,被一層層的進行包裹,加入新的內容,最終使用最頂層的輸出方法將這個結果呈現給我們。同樣的我們還可以反著看,當遞歸開始的時候,在最頂層等待下一層的數據,然後使用頂層的方式來封裝,而下一層被啟動執行到關鍵步驟時會等待下下一層的數據返回給自身,然後是用自己的方式來封裝,就這樣一直等待下去,直到最底層的數據(本來就有)得到之後,然後一步步的返回過來,在相應的層次進行相應的封裝,最後得到了最終的數據。這就是裝飾器模式,所有的類其實最終都是同源(一致性)的,有最終的祖先,如下圖所示。

技術分享圖片

二、代碼

上圖中,StringDisplay是保存原始數據的,而Border中將父類的引用組合進入自身,形成了遞歸的必然條件,之後讓子類(SideBorder和FullBorder)來使用這個引用,從而根據自身的實際情況(要怎樣裝飾)來進行包裝,將原始的數據getRowText(rowID)進行包裹,最終通過同源祖先類的show()方法來現實,這裏祖先類Display使用了面向抽象編程的模板方法,相信大家都能看出來。對比與上一個組合模式,我們可以看到上面的部分還是很相似的,但是在composite中,都實現了add()方法,通過容器來進行組織,並且使用委托機制來存儲所有的有著共同父類的元素,在顯示的時候使用了樹的深度優先遍歷。而在裝飾器模式中,我們使用的遞歸從上到下,沿著display的指向一點點的走到了底部,並且最終返回了過來。遍歷的方式有著相似之處也有著不同之處。這裏要說明的是,關於裝飾器的分析是原理上的,在實現的時候,其實在new這個過程中

就一點點的將對象的數值更改了,而不是調用show()的時候才開始遞歸的,這點一定要搞清楚。

Display 類:

 1 package zyr.dp.decorator;
 2 
 3 public abstract class Display {
 4     
 5     public abstract int getColunmns();
 6     public abstract int getRows();
 7     public abstract String getTowText(int rowID);
 8     public  void  show(){
 9         for(int i=0;i<getRows();i++){
10             System.out.println(getTowText(i));
11         }
12     }
13 
14 }

StringDisplay類:

 1 package zyr.dp.decorator;
 2 
 3 public class StringDisplay extends Display {
 4 
 5     String name;
 6     
 7     public StringDisplay(String name){
 8         this.name=name;
 9     }
10     
11     public int getColunmns() {
12         return name.getBytes().length;
13     }
14     
15     public int getRows() {
16         return 1;
17     }
18 
19     public String getTowText(int rowID) {
20         if(rowID==0){
21             return name;
22         }else{
23             return null;
24         }
25     }
26 
27 }

Border類:

1 package zyr.dp.decorator;
2 
3 public abstract class Border extends Display {
4 
5     protected Display display;
6     public Border(Display display){
7         this.display=display;
8     }
9 }

SideBorder類:

 1 package zyr.dp.decorator;
 2 
 3 public class SideBorder extends Border {
 4 
 5     String ch;
 6     protected SideBorder(Display display,String ch) {
 7         super(display);
 8         this.ch=ch;
 9     }
10 
11     public int getColunmns() {
12         return display.getColunmns()+2;
13     }
14 
15     public int getRows() {
16         return display.getRows();
17     }
18 
19     public String getTowText(int rowID) {
20         return ch+display.getTowText(rowID)+ch;
21     }
22 
23 }

FullBorder類:

 1 package zyr.dp.decorator;
 2 
 3 public class FullBorder extends Border {
 4 
 5     public  FullBorder(Display display) {
 6         super(display);
 7     }
 8 
 9     public int getColunmns() {
10         return display.getColunmns()+2;
11     }
12 
13     public int getRows() {
14         return display.getRows()+2;
15     }
16 
17     public String getTowText(int rowID) {
18         if(rowID==0){
19             return "+"+makeLine("-",display.getColunmns())+"+";
20         }else if(rowID==display.getRows()+1){
21             return "+"+makeLine("-",display.getColunmns())+"+";
22         }else{
23             return "|"+display.getTowText(rowID-1)+"|";
24         }
25     }
26     private String makeLine(String ch,int count){
27         StringBuffer sb=new StringBuffer();
28         for(int i=0;i<count;i++){
29             sb.append(ch);
30         }
31         return sb.toString();
32     }
33 
34 }

Main類:

 1 package zyr.dp.decorator;
 2 
 3 public class Main {
 4 
 5     public static void main(String[] args) {
 6 
 7         Display d1=new StringDisplay("朱彥榮");
 8         d1.show();
 9         System.out.println("\n");
10         Display d2=new SideBorder(d1,"*");
11         d2.show();
12         System.out.println("\n");
13         Display d3=new FullBorder(d2);
14         d3.show();
15         System.out.println("\n");
16         Display d4=new SideBorder(
17                      new FullBorder(
18                        new FullBorder(
19                           new SideBorder(
20                                new FullBorder(
21                                        new StringDisplay("李山秀")
22                                ),
23                                "#"
24                           )
25                         )
26                      ),
27                      "*"
28                     );
29         d4.show();
30     }
31 
32 }

運行結果:

技術分享圖片

三、總結

可以看到裝飾模式中,保證了裝飾邊框與被裝飾物體的一致性(有共同父類),使用了模板方法,這個方法幾乎無處不在呀,同樣使用了委托(組合),通過在原始數據上面一層層的包裹,最終得到了我們想要的輸出,有著非常廣泛的用處。

設計模式:裝飾器(Decorator)模式