Java設(shè)計(jì)模式之策略模式篇(摘)

          Java設(shè)計(jì)模式之策略模式篇
          作者:馮睿 本文選自:賽迪網(wǎng) 2003年02月27日

           

          策略模式(Strategy Pattern)中體現(xiàn)了兩個非常基本的面向?qū)ο笤O(shè)計(jì)的基本原則:封裝變化的概念;編程中使用接口,而不是對接口實(shí)現(xiàn)。策略模式的定義如下:

          定義一組算法,將每個算法都封裝起來,并且使它們之間可以互換。策略模式使這些算法在客戶端調(diào)用它們的時候能夠互不影響地變化。

          策略模式使開發(fā)人員能夠開發(fā)出由許多可替換的部分組成的軟件,并且各個部分之間是弱連接的關(guān)系。弱連接的特性使軟件具有更強(qiáng)的可擴(kuò)展性,易于維護(hù);更重要的是,它大大提高了軟件的可重用性。

          為了說明策略模式,我們將首先討論一下在Swing中是如何利用策略模式來繪制組件邊界的,然后討論在Swing中使用策略模式帶來的好處,最后討論如何在軟件中實(shí)現(xiàn)策略模式。

          Swing邊框


          對所有的Swing組件,例如按鈕、列表單等,都還可以繪制邊框。在Swing中提供了各種邊框類型,例如bevel、etched、line、titled等。Swing組件的邊框是通過JComponent類來繪制的,該類是所有Swing組件的基類,實(shí)現(xiàn)了所有Swing組件公共的功能。在JComponent中有一個paintBorder()方法,該方法為組件繪制邊框。Swing的開發(fā)人員可以象下面的例子中所示那樣來繪制邊框:

          
          // 一段實(shí)現(xiàn)paintBorder()方法代碼
          protected void paintBorder(Graphics g) {
             switch(getBorderType()) {
                case LINE_BORDER:   paintLineBorder(g);
                                    break;
                case ETCHED_BORDER: paintEtchedBorder(g);
                                    break;
                case TITLED_BORDER: paintTitledBorder(g);
                                    break;
                ...
             }
          }
          


          請注意上面的代碼只是一種假設(shè),事實(shí)上Swing的開發(fā)人員并沒有這樣實(shí)現(xiàn)paintBorder()方法。在上面的代碼中,在JComponent中繪制邊框的代碼被直接寫入了paintBorder()方法中,這意味著JComponent和繪制邊框的功能被緊密地結(jié)合在了一起。很自然地大家會聯(lián)想到如果需要實(shí)現(xiàn)一種新的邊框類型,開發(fā)人員必須修改至少三處代碼:首先增加一個常量,該常量代表新添加的邊框的類型值;其次需要在Switch語句中增加一個case語句;最后開發(fā)人員需要實(shí)現(xiàn)paintXXXBorder()方法,其中XXX代表新邊框的名稱。

          很顯然要擴(kuò)展上面paintBorder()方法的功能是一件很困難的事情,不僅僅是因?yàn)殚_發(fā)人員需要增加一種新的邊框類型,更麻煩的是開發(fā)人員很難修改JComponent類。JComponent類已經(jīng)被編譯到了Swing的開發(fā)工具中,如果開發(fā)人員想修改它的話,必須獲得Swing的源代碼,修改后重新編譯Swing。同時在用戶的計(jì)算機(jī)上與需要使用新編譯的Swing API。另外所有的Swing組件都可以使用開發(fā)人員新添加的邊框類型。有可能開發(fā)人員只希望新的邊框被某些組件使用,但是現(xiàn)在開發(fā)人員無法對使用該邊框的組件進(jìn)行限制。

          開發(fā)人員有更好的實(shí)現(xiàn)方法嗎?答案就是策略模式。通過策略模式,可以將JComponent和實(shí)現(xiàn)繪制邊框的代碼分離開來,這樣開發(fā)人員在增加或修改繪制邊框的代碼使就不需要修改JComponent的代碼。通過應(yīng)用策略模式,開發(fā)人員將變化的概念(在這個例子中是繪制邊框)封裝起來,然后通過一個Border接口,使程序能夠重用繪制邊框的功能。下面讓我們來看JComponent是如何利用策略模式來實(shí)現(xiàn)繪制邊框的功能的:

          
          // Swing中paintBorder()方法的源代碼
          protected void paintBorder(Graphics g) {
             Border border = getBorder();
             if (border != null) {
                border.paintBorder(this, g, 0, 0, getWidth(), getHeight());
             }
          }
          


          上面的paintBorder()方法通過一個border對象繪制了組件的邊框。這樣border對象替代了前一個例子中的JComponent封裝了邊框繪制的功能。我們還應(yīng)該注意到JComponent將一個對自己的引用傳遞給了Border.paintBorder()方法,這是因?yàn)锽order的實(shí)例必須知道它對應(yīng)的組件的信息,這種方式通常被稱為委托。通過這種方式,一個對象可以將功能委托給另一個對象來實(shí)現(xiàn)。

          在JComponent類中引用了一個Border對象,通過JComponent.getBorder()方法可以獲得該Border對象。下面的代碼演示了如何設(shè)定和獲得Border對象:

          
          ...
          private Border border;
          ...
          public void setBorder(Border border) {
             Border oldBorder = this.border;
             this.border = border;
             firePropertyChange("border", oldBorder, border);
             if (border != oldBorder) {
                if (border == null || oldBorder == null || !(border.getBorderInsets(this).
                                              equals(oldBorder.getBorderInsets(this)))) {
                   revalidate();
                }       
                repaint();
             }
          }
          ...
          public Border getBorder() {
             return border;
          }
          


          當(dāng)開發(fā)人員通過JComponent.setBorder()方法設(shè)定了一個組件的邊框后,JComponent類發(fā)出一個屬性更新事件。如果新的邊框和以前的邊框不同的話,setBorder()方法就重新繪制邊框。getBorder()方法僅僅返回對Border對象的引用。圖1顯示了Border的類結(jié)構(gòu)圖:



          圖1 Border的類結(jié)構(gòu)圖


          通過類結(jié)構(gòu)圖我們可以看到,JComponent類中保存了一個對Border對象的引用。由于Border是一個接口,Swing組件可以使用任何一個實(shí)現(xiàn)了Border接口的類。

          現(xiàn)在我們已經(jīng)知道了JComponent是如何利用策略模式來繪制組件的邊框的。下面讓我們通過實(shí)現(xiàn)一個新的邊框類型來測試一下它的可擴(kuò)展性。

          實(shí)現(xiàn)一個新的邊框類型


          圖2中是一個有三個JPanel對象的小程序,每個JPanel對象有各自不同的邊框,每個邊框?qū)?yīng)一個HandleBorder實(shí)例。



          圖2 新的邊框類型


          
          // HandleBorder.java
          import java.awt.*;
          import javax.swing.*;
          import javax.swing.border.*;
          public class HandleBorder extends AbstractBorder {
             protected Color lineColor;
             protected int thick;
             public HandleBorder() {
                this(Color.black, 6);
             }
             public HandleBorder(Color lineColor, int thick) {
                this.lineColor = lineColor;
                this.thick = thick;
             }
             public void paintBorder(Component component, 
                                            Graphics g, int x, int y, int w, int h) {
                Graphics copy = g.create();
                if(copy != null) {
                   try {
                      copy.translate(x,y);
                      paintRectangle(component,copy,w,h);
                      paintHandles(component,copy,w,h);
                   }
                   finally {
                      copy.dispose();
                   }
                }
             }
             public Insets getBorderInsets() {
                return new Insets(thick,thick,thick,thick);
             }
             protected void paintRectangle(Component c, Graphics g,
                                     int w, int h) {
                g.setColor(lineColor);
                g.drawRect(thick/2,thick/2,w-thick-1,h-thick-1);
             }
             protected void paintHandles(Component c, Graphics g,
                                     int w, int h) {
                g.setColor(lineColor);
                g.fillRect(0,0,thick,thick); 
                g.fillRect(w-thick,0,thick,thick); 
                g.fillRect(0,h-thick,thick,thick); 
                g.fillRect(w-thick,h-thick,thick,thick); 
                g.fillRect(w/2-thick/2,0,thick,thick); 
                g.fillRect(0,h/2-thick/2,thick,thick); 
                g.fillRect(w/2-thick/2,h-thick,thick,thick); 
                g.fillRect(w-thick,h/2-thick/2,thick,thick); 
             }   
          }
          


          HandleBorder類繼承了javax.swing.border.AbstractBorder類并重寫了paintBorder()和getBorderInsets()。HandleBorder是如何實(shí)現(xiàn)的其實(shí)并不重要,重要的是由于Swing使用了策略模型,開發(fā)人員能夠很方便地增加新的邊框類型。下面的代碼顯示了如何使用HandleBorder類。在這個例子中創(chuàng)建了三個JPanel對象,并對每個JPanel對象設(shè)定一個HandleBorder實(shí)例作為邊框。

          
          // Test.java
          import javax.swing.*;
          import javax.swing.border.*;
          import java.awt.*;
          import java.awt.event.*;
          public class Test extends JFrame {
             public static void main(String[] args) {
                JFrame frame = new Test();
                frame.setBounds(100, 100, 500, 200);
                frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
                frame.show();
             }
             public Test() {
                super("實(shí)現(xiàn)一個新的邊框類型");
                Container contentPane = getContentPane();
                JPanel[] panels = { new JPanel(), 
                               new JPanel(), new JPanel() };
                Border[] borders = { new HandleBorder(),
                               new HandleBorder(Color.red, 8),
                               new HandleBorder(Color.blue, 10) };
                contentPane.setLayout(
                         new FlowLayout(FlowLayout.CENTER,20,20));
                for(int i=0; i < panels.length; ++i) {
                   panels[i].setPreferredSize(new Dimension(100,100));
                   panels[i].setBorder(borders[i]);
                   contentPane.add(panels[i]);
                }
             }
          }
          


          還記得在上面的例子中曾提到在有些情況下,對組件的引用會作為參數(shù)傳遞給Border.paintBorder()方法。雖然上面的HandleBorder類沒有保存對組件的引用,但是有些情況下Border接口的實(shí)現(xiàn)類會使用到對組件的引用并從中獲得關(guān)于組件的信息。例如在EtchedBorder中,paintBorder()方法通過對組件的引用獲得它對應(yīng)的組件的陰影和高光色:

          
          // 下面的代碼截取自javax.swing.border.EtchedBorder
          public void paintBorder(Component component, Graphics g, int x, int y, 
                                   int width, int height) {
             int w = width;
             int h = height;
             g.translate(x, y);
             g.setColor(etchType == LOWERED? getShadowColor(component) : 
          getHighlightColor(component));
             g.drawRect(0, 0, w-2, h-2);
             g.setColor(etchType == LOWERED? getHighlightColor(component) : 
          getShadowColor(component));
             g.drawLine(1, h-3, 1, 1);
             g.drawLine(1, 1, w-3, 1);
             g.drawLine(0, h-1, w-1, h-1);
             g.drawLine(w-1, h-1, w-1, 0);
             g.translate(-x, -y);
          }
          


          如何實(shí)現(xiàn)策略模型


          通過以下步驟,開發(fā)人員可以很容易地在軟件中實(shí)現(xiàn)策略模型:

          1.對策略對象定義一個公共接口。

          2.編寫策略類,該類實(shí)現(xiàn)了上面的公共接口。

          3.在使用策略對象的類中保存一個對策略對象的引用。

          4.在使用策略對象的類中,實(shí)現(xiàn)對策略對象的set和get方法。

          在Swing邊框的例子中,公共接口是javax.swing.Border。策略類是LineBorder、EtchedBorder、HandleBorder等。而使用策略對象的類是JComponent。

          posted on 2005-12-24 10:09 beyondduke 閱讀(576) 評論(0)  編輯  收藏 所屬分類: 我的收藏


          只有注冊用戶登錄后才能發(fā)表評論。


          網(wǎng)站導(dǎo)航:
           
          <2005年12月>
          27282930123
          45678910
          11121314151617
          18192021222324
          25262728293031
          1234567

          導(dǎo)航

          統(tǒng)計(jì)

          公告


          MSN聯(lián)系

          常用鏈接

          留言簿(2)

          隨筆分類

          隨筆檔案

          我的連接

          搜索

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 阿尔山市| 南昌市| 新津县| 剑川县| 高雄县| 三穗县| 农安县| 美姑县| 个旧市| 琼结县| 赫章县| 新野县| 积石山| 那曲县| 阳东县| 通河县| 北川| 鹰潭市| 阿拉善左旗| 开远市| 永吉县| 长顺县| 黑河市| 冕宁县| 贵德县| 绍兴市| 汝州市| 临桂县| 平定县| 三河市| 上虞市| 庄河市| 金寨县| 平远县| 镇沅| 海原县| 广汉市| 资中县| 周口市| 克什克腾旗| 江都市|