freesky

          自由天空
          隨筆 - 8, 文章 - 0, 評論 - 0, 引用 - 0
          數據加載中……

          Java設計模式之策略模式

          策略模式(Strategy Pattern)中體現了兩個非常基本的面向對象設計的基本原則:封裝變化的概念;編程中使用接口,而不是對接口實現。策略模式的定義如下:

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

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

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

          Swing邊框

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

          // 一段實現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;
          ...
          }
          }

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

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

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

          // 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封裝了邊框繪制的功能。我們還應該注意到JComponent將一個對自己的引用傳遞給了 Border.paintBorder()方法,這是因為Border的實例必須知道它對應的組件的信息,這種方式通常被稱為委托。通過這種方式,一個對 象可以將功能委托給另一個對象來實現。

          在JComponent類中引用了一個Border對象,通過JComponent.getBorder()方法可以獲得該Border對象。下面的代碼演示了如何設定和獲得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;
          }

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

          圖1 Border的類結構圖

          通過類結構圖我們可以看到,JComponent類中保存了一個對Border對象的引用。由于Border是一個接口,Swing組件可以使用任何一個實現了Border接口的類。

          現在我們已經知道了JComponent是如何利用策略模式來繪制組件的邊框的。下面讓我們通過實現一個新的邊框類型來測試一下它的可擴展性。

          實現一個新的邊框類型

          圖2中是一個有三個JPanel對象的小程序,每個JPanel對象有各自不同的邊框,每個邊框對應一個HandleBorder實例。

          圖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是如何實現的其實并不重要,重要的是由于Swing使用了策略模型,開發人員能夠很方便地增加新的邊框類型。下面的代碼顯示了如 何使用HandleBorder類。在這個例子中創建了三個JPanel對象,并對每個JPanel對象設定一個HandleBorder實例作為邊框。

          // 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("實現一個新的邊框類型");
          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]);
          }
          }
          }

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

          // 下面的代碼截取自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);
          }

          如何實現策略模型

          通過以下步驟,開發人員可以很容易地在軟件中實現策略模型:

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

          2.編寫策略類,該類實現了上面的公共接口。

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

          4.在使用策略對象的類中,實現對策略對象的set和get方法。

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


           

          轉載:http://fly-net-cn.javaeye.com/blog/78615

          posted on 2007-05-14 10:28 freesky 閱讀(724) 評論(0)  編輯  收藏 所屬分類: 設計模式


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


          網站導航:
           
          主站蜘蛛池模板: 大城县| 遂平县| 大洼县| 南投县| 长泰县| 新营市| 延津县| 周至县| 延吉市| 宜宾县| 黔西| 北安市| 渑池县| 儋州市| 石泉县| 和政县| 正镶白旗| 杨浦区| 正阳县| 云梦县| 黄梅县| 东海县| 安庆市| 本溪市| 马公市| 屯门区| 咸阳市| 雅江县| 宁波市| 晋宁县| 丰顺县| 翁牛特旗| 甘谷县| 越西县| 张家口市| 霍邱县| 苍溪县| 江达县| 克拉玛依市| 新竹县| 凤城市|