Facade外观模式

xiaoxiao2026-04-17  1

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代码 package app.facade;      import javax.swing.*;       import java.awt.*;       import com.oozinoz.ui.SwingFacade;  //此类下载的源码文件中有       public class ShowCircle extends JPanel {        public static void main(String[] args) {            ShowCircle sc = new ShowCircle();             sc.setPreferredSize(new Dimension(300300));            SwingFacade.launch(sc, "Circle");        }          protected void paintComponent(Graphics g) {            super.paintComponent(g);            int nPoint = 101;            double w = getWidth() - 1;            double h = getHeight() - 1;            double r = Math.min(w, h) / 2.0;            int[] x = new int[nPoint];            int[] y = new int[nPoint];            for (int i = 0; i < nPoint; i++) {                double t = ((double) i) / (nPoint - 1);                double theta = Math.PI * 2.0 * t;                x[i] = (int) (w / 2 + r * Math.cos(theta));                y[i] = (int) (h / 2 - r * Math.sin(theta));            }           g.drawPolyline(x, y, nPoint);       }   }  

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

Java代码 package app.facade;      /*   * Copyright (c) 2001, 2005. Steven J. Metsker.   *    * Steve Metsker makes no representations or warranties about   * the fitness of this software for any particular purpose,    * including the implied warranty of merchantability.   *   * Please use this software as you wish with the sole   * restriction that you may not claim that you wrote it.   */     import java.awt.Color;    import java.awt.Dimension;    import java.awt.Font;    import java.awt.Graphics;       import javax.swing.BorderFactory;    import javax.swing.JFrame;    import javax.swing.JPanel;    import javax.swing.border.BevelBorder;    import javax.swing.border.TitledBorder;       public class ShowFlight extends JPanel {        /**       * Show the flight path of a nonexploding aerial shell. main()方法为用于包含应用程序控件的表单对象增加了间隙。       */       public static void main(String[] args) {            ShowFlight fp = new ShowFlight();            fp.setPreferredSize(new Dimension(300200));            JPanel fp_titled = createTitledPanel("Flight Path", fp);               JFrame frame = new JFrame("Flight Path for Shell Duds");            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);           frame.getContentPane().add(fp_titled);             frame.pack();            frame.setVisible(true);        }          /**       * Create a titled border with the given title.       */       public static TitledBorder createTitledBorder(String title) {            TitledBorder tb = BorderFactory.createTitledBorder(BorderFactory                   .createBevelBorder(BevelBorder.RAISED), title, TitledBorder.LEFT, TitledBorder.TOP);           tb.setTitleColor(Color.black);           tb.setTitleFont(getStandardFont());            return tb;        }          /**       * Create a new panel that wraps a titled border around a given panel.此方法将其提供的控件隐藏在一个斜边框里,以提供小的间隙,使飞行轨迹不会触到边界。       */       public static JPanel createTitledPanel(String title, JPanel in) {            JPanel out = new JPanel();            out.add(in);           out.setBorder(createTitledBorder(title));            return out;        }          /**       * @return a standard font for GUI use       */       public static Font getStandardFont() {            return new Font("Dialog", Font.PLAIN, 18);        }          protected void paintComponent(Graphics g) {            super.paintComponent(g); // paint the background            int nPoint = 101;            double w = getWidth() - 1;            double h = getHeight() - 1;            int[] x = new int[nPoint];            int[] y = new int[nPoint];            for (int i = 0; i < nPoint; i++) {                // t goes 0 to 1                double t = ((double) i) / (nPoint - 1);                // x goes 0 to w                x[i] = (int) (t * w);                // y is h at t = 0 and t = 1, and y is 0 at t = .5                y[i] = (int) (4 * h * (t - .5) * (t - .5));            }           g.drawPolyline(x, y, nPoint);       }   }  

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

 

相关资源:敏捷开发V1.0.pptx
转载请注明原文地址: https://www.6miu.com/read-5047528.html

最新回复(0)