Facade外观模式

1.Facade模式就是一个功能介于包和完整应用程序之间的类,可提供包或子系统中类的简化功能。Facade模式的目的在于提供一个接口,使子系统更加容易使用

2.外观类、工具类和示例类:

   外观类的方法可能都是静态方法,这种情况下,外观类在UML中被称作工具类示例类就是演示如何使用类或子系统的例子。外观类所能提供的诸多使得之处,示例类也能提供。

3.示例类与外观类的几点区别:

   (1)示例类通常是一个能够独立运行的应用程序,而外观类通常不是;

   (2)示例类通常会包含示例数据,而外观类不会;

   (3)外观类通常是可配置的,而示例类不是;

   (4)外观类旨在提供复用,而示例类不是;

   (5)外观类旨在应用于实际环境中,而示例类不是。

4.javax.swing包中有个JOptionPane类,利用它可以很容易地弹出一个标准对话框。例如,下面的代码会不断地显示一个对话框,直到单击Yes按钮为止。

 
package app.facade;

import javax.swing.*;
import java.awt.Font;

public class ShowOptionPane
{
   public static void main(String[] args)
   {
         Font font = new Font("Dialog",Font.PLAIN,18);
         UIManager.put("Button.font",font);
         UIManager.put("Label.font",font);
         
         int option;
         do
         {
              option = JOptionPane.showConfirmDialog(
                               null,"Had enough?",
                               "A Stubborn Dialog",
                               JOptionPane.YES_NO_OPTION);
         }while(option == JOptionPane.NO_OPION);
   }
}

5. JOptionPane类是一个外观类。该类可配置、可复用,在实际应用中非常易于使用。由于该类通过提供一个简单的接口方便了用户使用JDialog类,因而JOptionPane类符合Facade模式设计意图。有人可能认为外观类是用于简化一个子系统,而单一的JDialog类并不能算是一个子系统。但是正是由于JDialog类具有丰富的特性使得为它创建一个外观类非常有价值。Sun公司在JDK中附带提供了许多示例类。但从它们所在包的名称并没有以java为前缀,我们可以看出这些类并不属于Java类库的一部分。外观类可能属于Java类库的一部分,但示例类不属于Java类库。严格地讲,JOptionPane类并不符合UML关于工具类的定义,因为UML定义一个工具类仅处理静态方法。

6.Java类库中很少有外观类,原因可能源自以下几个方面,这些方面可能截然不同:

  (1)很多人建议Java程序员应该好好研究一下Java类库,但外观类却限制了我们对任何系统的深入使用。它们常常会分散开发人员对Java类库的注意力,并且会引起误会。

  (2)外观类通常介于丰富的包和特定的应用之间。为了创建外观类,开发人员必须事先了解应用程序的类型。但由于Java类库的用户极多,因而这样做几乎是不可能的。

  (3)Java类库提供的外观类极少,这可能是Java类库的一个缺陷。

7.重构为Facade模式

   Facade模式起源于普通的程序开发。当从多个不同类中分离你的代码时,你可能要通过提取访问子系统的类来重构系统

8.下面是利用参数方程画圆的程序代码:

Java代码 复制代码
  1. package app.facade;   
  2.   
  3. import javax.swing.*;   
  4.   
  5. import java.awt.*;   
  6.   
  7. import com.oozinoz.ui.SwingFacade;  //此类下载的源码文件中有   
  8.   
  9. public class ShowCircle extends JPanel {   
  10.     public static void main(String[] args) {   
  11.         ShowCircle sc = new ShowCircle();    
  12.         sc.setPreferredSize(new Dimension(300300));   
  13.         SwingFacade.launch(sc, "Circle");   
  14.     }   
  15.   
  16.     protected void paintComponent(Graphics g) {   
  17.         super.paintComponent(g);   
  18.         int nPoint = 101;   
  19.         double w = getWidth() - 1;   
  20.         double h = getHeight() - 1;   
  21.         double r = Math.min(w, h) / 2.0;   
  22.         int[] x = new int[nPoint];   
  23.         int[] y = new int[nPoint];   
  24.         for (int i = 0; i < nPoint; i++) {   
  25.             double t = ((double) i) / (nPoint - 1);   
  26.             double theta = Math.PI * 2.0 * t;   
  27.             x[i] = (int) (w / 2 + r * Math.cos(theta));   
  28.             y[i] = (int) (h / 2 - r * Math.sin(theta));   
  29.         }   
  30.         g.drawPolyline(x, y, nPoint);   
  31.     }   
  32. }  

 9.假如现在有一个ShowFlight类存在一个问题:它混合了三个目的。其主要目的是作为面板显示焰火弹飞行轨迹。另一个目的是作为一个完整的应用程序,将飞行轨迹面板封装在窗口范围内。第三个目的是计算飞行抛物线。ShowFlight类目前在paintComponent()方法中完成这个计算。

Java代码 复制代码
  1. package app.facade;   
  2.   
  3. /*  
  4.  * Copyright (c) 2001, 2005. Steven J. Metsker.  
  5.  *   
  6.  * Steve Metsker makes no representations or warranties about  
  7.  * the fitness of this software for any particular purpose,   
  8.  * including the implied warranty of merchantability.  
  9.  *  
  10.  * Please use this software as you wish with the sole  
  11.  * restriction that you may not claim that you wrote it.  
  12.  */  
  13.   
  14. import java.awt.Color;   
  15. import java.awt.Dimension;   
  16. import java.awt.Font;   
  17. import java.awt.Graphics;   
  18.   
  19. import javax.swing.BorderFactory;   
  20. import javax.swing.JFrame;   
  21. import javax.swing.JPanel;   
  22. import javax.swing.border.BevelBorder;   
  23. import javax.swing.border.TitledBorder;   
  24.   
  25. public class ShowFlight extends JPanel {   
  26.     /**  
  27.      * Show the flight path of a nonexploding aerial shell. main()方法为用于包含应用程序控件的表单对象增加了间隙。  
  28.      */  
  29.     public static void main(String[] args) {   
  30.         ShowFlight fp = new ShowFlight();   
  31.         fp.setPreferredSize(new Dimension(300200));   
  32.         JPanel fp_titled = createTitledPanel("Flight Path", fp);   
  33.   
  34.         JFrame frame = new JFrame("Flight Path for Shell Duds");   
  35.         frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);   
  36.         frame.getContentPane().add(fp_titled);   
  37.   
  38.         frame.pack();   
  39.         frame.setVisible(true);   
  40.     }   
  41.   
  42.     /**  
  43.      * Create a titled border with the given title.  
  44.      */  
  45.     public static TitledBorder createTitledBorder(String title) {   
  46.         TitledBorder tb = BorderFactory.createTitledBorder(BorderFactory   
  47.                 .createBevelBorder(BevelBorder.RAISED), title, TitledBorder.LEFT, TitledBorder.TOP);   
  48.         tb.setTitleColor(Color.black);   
  49.         tb.setTitleFont(getStandardFont());   
  50.         return tb;   
  51.     }   
  52.   
  53.     /**  
  54.      * Create a new panel that wraps a titled border around a given panel.此方法将其提供的控件隐藏在一个斜边框里,以提供小的间隙,使飞行轨迹不会触到边界。  
  55.      */  
  56.     public static JPanel createTitledPanel(String title, JPanel in) {   
  57.         JPanel out = new JPanel();   
  58.         out.add(in);   
  59.         out.setBorder(createTitledBorder(title));   
  60.         return out;   
  61.     }   
  62.   
  63.     /**  
  64.      * @return a standard font for GUI use  
  65.      */  
  66.     public static Font getStandardFont() {   
  67.         return new Font("Dialog", Font.PLAIN, 18);   
  68.     }   
  69.   
  70.     protected void paintComponent(Graphics g) {   
  71.         super.paintComponent(g); // paint the background   
  72.         int nPoint = 101;   
  73.         double w = getWidth() - 1;   
  74.         double h = getHeight() - 1;   
  75.         int[] x = new int[nPoint];   
  76.         int[] y = new int[nPoint];   
  77.         for (int i = 0; i < nPoint; i++) {   
  78.             // t goes 0 to 1   
  79.             double t = ((double) i) / (nPoint - 1);   
  80.             // x goes 0 to w   
  81.             x[i] = (int) (t * w);   
  82.             // y is h at t = 0 and t = 1, and y is 0 at t = .5   
  83.             y[i] = (int) (4 * h * (t - .5) * (t - .5));   
  84.         }   
  85.         g.drawPolyline(x, y, nPoint);   
  86.     }   
  87. }  

 10.可以将上面的ShowFlight类重构为互不关联的类,使得它更具可维护性和可复用性。假设有一个设计评审,并且决定做如下变动:

  (1)引入一个拥有f()方法的Function类,f()方法接受一个double值(时间值),并且返回一个double值(此函数的返回结果)。

  (2)将ShowFlight中绘图代码移植到PlotPanel类中,但是做一些调整,使用Function对象来获取x和y值。定义PlotPanel构造器来接受两个Function实例,以及绘图所需的点的数量。

  (3)从当前的UI工具类中移除createTitledPanel()方法,构造一个像当前的ShowFlight类那样带有标题的面板。

11.将ShowFlight类重构成三个类:Function类,PlotPanel类,UI外观类。在重新设计时,使得ShowFlight2类创建一个获得y值的函数,并且从main()方法开始执行该应用程序。

     注:该图显示了飞行轨迹绘制应用程序被重构为多个类,其中每个类完成一项任务

  

     值得注意的是:createTitledPanel()方法不是静态方法。为了获取标准的用户界面,本设计引用单例NORMAL对象。

请参看UI的相关代码。

     重构之后,Function类定义了参数议程的样子。假设你创建了一个包含Function类及其他类型的com.oozinoz.function包,则Function.java的核心代码可能是:

public abstract double f(double t);

PlotPanel类在重构之后成为具有单独功能的类,用来显示一对参数方程;

      注意PlotPanel类现在是com.oozinoz.ui包的一部分,即和UI类处于同一位置上。ShowFlight类经过重构之后,UI类同样也拥有createTitledPanel()和createTitledBorder()方法。于是UI类便演化为Facade模式,从而使得Java控件的使用更为容易。

      使用这些控件的应用程序可能会是一个很小的类,其功能仅仅为布置这些控件,并且显示它们。请看ShowFlight2类的代码。

      ShowFlight2类提供用来计算哑弹飞行轨迹的YFunction类。main()方法用来布置和显示用户界面。运行这个类所产生的效果和运行最初的ShowFlight类的效果完全一样。但是现在你已经有了一个可复用的、简化Java应用程序中图形用户界面创建过程的Facade模式。

12. 通常,我们应该把子系统的类重构为一个个目的明确的类。这样做可以使代码更加容易维护,但是这样也会让子系统用户不知从何处开始。为了便于子系统用户的使用,我们可以在子系统中顺带提供一些示例类或者外观类。示例类通常是指能够独立运行但不可复用的应用程序,仅用来示范子系统的用法。外观类通常是一个可配置、可复用的类,它为方便用户使用子系统提供了一个更高层次的接口

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值