软件设计也是这样,当一个别系的功能越来越强,子系统会越来越多,客户对系统的访问也变得越来越繁芜。这时如果系统内部发生改变,客户端也要随着改变,这违背了“开闭原则”,也违背了“迪米特法则”,以是有必要为多个子系统供应一个统一的接口,从而降落系统的耦合度,这便是外不雅观模式的目标。
图 1 给出了客户去当地房产局办理房产证过户要碰着的干系部门。
图1 办理房产证过户的干系部门

外不雅观(Facade)模式又叫作门面模式,是一种通过为多个繁芜的子系统供应一个同等的接口,而使这些子系统更加随意马虎被访问的模式。该模式对外有一个统一接口,外部运用程序不用关心内部子系统的详细细节,这样会大大降落运用程序的繁芜度,提高了程序的可掩护性。
在日常编码事情中,我们都在故意无意的大量利用外不雅观模式。只假如高层模块须要调度多个子系统(2个以上的类工具),我们都会自觉地创建一个新的类封装这些子系统,供应精简的接口,让高层模块可以更加随意马虎地间接调用这些子系统的功能。尤其是现阶段各种第三方SDK、开源类库,很大概率都会利用外不雅观模式。
外不雅观(Facade)模式是“迪米特法则”的范例运用,它有以下紧张优点。
降落了子系统与客户端之间的耦合度,使得子系统的变革不会影响调用它的客户类。对客户屏蔽了子系统组件,减少了客户处理的工具数目,并使得子系统利用起来更加随意马虎。降落了大型软件系统中的编译依赖性,简化了系统在不同平台之间的移植过程,由于编译一个子系统不会影响其他的子系统,也不会影响外不雅观工具。外不雅观(Facade)模式的紧张缺陷如下。
不能很好地限定客户利用子系统类,很随意马虎带来未知风险。增加新的子系统可能须要修正外不雅观类或客户真个源代码,违背了“开闭原则”。外不雅观模式的构造与实现外不雅观(Facade)模式的构造比较大略,紧张是定义了一个高层接口。它包含了对各个子系统的引用,客户端可以通过它访问各个子系统的功能。现在来剖析其基本构造和实现方法。
1. 模式的构造外不雅观(Facade)模式包含以下紧张角色。
外不雅观(Facade)角色:为多个子系统对外供应一个共同的接口。子系统(Sub System)角色:实现系统的部分功能,客户可以通过外不雅观角色访问它。客户(Client)角色:通过一个外不雅观角色访问各个子系统的功能。其构造图如图 2 所示。
图2 外不雅观(Facade)模式的构造图
2. 模式的实现外不雅观模式的实当代码如下:
package facade;public class FacadePattern { public static void main(String[] args) { Facade f = new Facade(); f.method(); }}//外不雅观角色class Facade { private SubSystem01 obj1 = new SubSystem01(); private SubSystem02 obj2 = new SubSystem02(); private SubSystem03 obj3 = new SubSystem03(); public void method() { obj1.method1(); obj2.method2(); obj3.method3(); }}//子系统角色class SubSystem01 { public void method1() { System.out.println("子系统01的method1()被调用!
"); }}//子系统角色class SubSystem02 { public void method2() { System.out.println("子系统02的method2()被调用!
"); }}//子系统角色class SubSystem03 { public void method3() { System.out.println("子系统03的method3()被调用!
"); }}
程序运行结果如下:
子系统01的method1()被调用!
外不雅观模式的运用实例
子系统02的method2()被调用!
子系统03的method3()被调用!
【例1】用“外不雅观模式”设计一个婺源特产的选购界面。
剖析:本实例的外不雅观角色 WySpecialty 是 JPanel 的子类,它拥有 8 个子系统角色 Specialty1~Specialty8,它们是图标类(ImageIcon)的子类工具,用来保存该婺源特产的图)。
外不雅观类(WySpecialty)用 JTree 组件来管理婺源特产的名称,并定义一个事宜处理方法 valueClianged(TreeSelectionEvent e),当用户从树中选择特产时,该特产的图标工具保存在标签(JLabd)工具中。
客户窗体工具用分割面板来实现,左边放外不雅观角色的目录树,右边放显示所选特产图像的标签。其构造图如图 3 所示。
图3 婺源特产管理界面的构造图
程序代码如下:
package facade;import java.awt.;import javax.swing.;import javax.swing.event.;import javax.swing.tree.DefaultMutableTreeNode;public class WySpecialtyFacade { public static void main(String[] args) { JFrame f = new JFrame("外不雅观模式: 婺源特产选择测试"); Container cp = f.getContentPane(); WySpecialty wys = new WySpecialty(); JScrollPane treeView = new JScrollPane(wys.tree); JScrollPane scrollpane = new JScrollPane(wys.label); JSplitPane splitpane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, treeView, scrollpane); //分割面版 splitpane.setDividerLocation(230); //设置splitpane的分隔线位置 splitpane.setOneTouchExpandable(true); //设置splitpane可以展开或收起 cp.add(splitpane); f.setSize(650, 350); f.setVisible(true); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); }}class WySpecialty extends JPanel implements TreeSelectionListener { private static final long serialVersionUID = 1L; final JTree tree; JLabel label; private Specialty1 s1 = new Specialty1(); private Specialty2 s2 = new Specialty2(); private Specialty3 s3 = new Specialty3(); private Specialty4 s4 = new Specialty4(); private Specialty5 s5 = new Specialty5(); private Specialty6 s6 = new Specialty6(); private Specialty7 s7 = new Specialty7(); private Specialty8 s8 = new Specialty8(); WySpecialty() { DefaultMutableTreeNode top = new DefaultMutableTreeNode("婺源特产"); DefaultMutableTreeNode node1 = null, node2 = null, tempNode = null; node1 = new DefaultMutableTreeNode("婺源四大特产(红、绿、黑、白)"); tempNode = new DefaultMutableTreeNode("婺源荷包红鲤鱼"); node1.add(tempNode); tempNode = new DefaultMutableTreeNode("婺源绿茶"); node1.add(tempNode); tempNode = new DefaultMutableTreeNode("婺源龙尾砚"); node1.add(tempNode); tempNode = new DefaultMutableTreeNode("婺源江湾雪梨"); node1.add(tempNode); top.add(node1); node2 = new DefaultMutableTreeNode("婺源其它土特产"); tempNode = new DefaultMutableTreeNode("婺源酒糟鱼"); node2.add(tempNode); tempNode = new DefaultMutableTreeNode("婺源糟米子糕"); node2.add(tempNode); tempNode = new DefaultMutableTreeNode("婺源清明果"); node2.add(tempNode); tempNode = new DefaultMutableTreeNode("婺源油煎灯"); node2.add(tempNode); top.add(node2); tree = new JTree(top); tree.addTreeSelectionListener(this); label = new JLabel(); } public void valueChanged(TreeSelectionEvent e) { if (e.getSource() == tree) { DefaultMutableTreeNode node = (DefaultMutableTreeNode) tree.getLastSelectedPathComponent(); if (node == null) return; if (node.isLeaf()) { Object object = node.getUserObject(); String sele = object.toString(); label.setText(sele); label.setHorizontalTextPosition(JLabel.CENTER); label.setVerticalTextPosition(JLabel.BOTTOM); sele = sele.substring(2, 4); if (sele.equalsIgnoreCase("荷包")) label.setIcon(s1); else if (sele.equalsIgnoreCase("绿茶")) label.setIcon(s2); else if (sele.equalsIgnoreCase("龙尾")) label.setIcon(s3); else if (sele.equalsIgnoreCase("江湾")) label.setIcon(s4); else if (sele.equalsIgnoreCase("酒糟")) label.setIcon(s5); else if (sele.equalsIgnoreCase("糟米")) label.setIcon(s6); else if (sele.equalsIgnoreCase("清明")) label.setIcon(s7); else if (sele.equalsIgnoreCase("油煎")) label.setIcon(s8); label.setHorizontalAlignment(JLabel.CENTER); } } }}class Specialty1 extends ImageIcon { private static final long serialVersionUID = 1L; Specialty1() { super("src/facade/WyImage/Specialty11.jpg"); }}class Specialty2 extends ImageIcon { private static final long serialVersionUID = 1L; Specialty2() { super("src/facade/WyImage/Specialty12.jpg"); }}class Specialty3 extends ImageIcon { private static final long serialVersionUID = 1L; Specialty3() { super("src/facade/WyImage/Specialty13.jpg"); }}class Specialty4 extends ImageIcon { private static final long serialVersionUID = 1L; Specialty4() { super("src/facade/WyImage/Specialty14.jpg"); }}class Specialty5 extends ImageIcon { private static final long serialVersionUID = 1L; Specialty5() { super("src/facade/WyImage/Specialty21.jpg"); }}class Specialty6 extends ImageIcon { private static final long serialVersionUID = 1L; Specialty6() { super("src/facade/WyImage/Specialty22.jpg"); }}class Specialty7 extends ImageIcon { private static final long serialVersionUID = 1L; Specialty7() { super("src/facade/WyImage/Specialty23.jpg"); }}class Specialty8 extends ImageIcon { private static final long serialVersionUID = 1L; Specialty8() { super("src/facade/WyImage/Specialty24.jpg"); }}
程序运行结果如图 4 所示。
图4 婺源特产管理界面的运行结果
外不雅观模式的运用处景常日在以下情形下可以考虑利用外不雅观模式。
对分层构造系统构建时,利用外不雅观模式定义子系统中每层的入口点可以简化子系统之间的依赖关系。当一个繁芜系统的子系统很多时,外不雅观模式可以为系统设计一个大略的接口供外界访问。当客户端与多个子系统之间存在很大的联系时,引入外不雅观模式可将它们分离,从而提高子系统的独立性和可移植性。外不雅观模式的扩展在外不雅观模式中,当增加或移除子系统时须要修正外不雅观类,这违背了“开闭原则”。如果引入抽象外不雅观类,则在一定程度上办理了该问题,其构造图如图 5 所示。
图5 引入抽象外不雅观类的外不雅观模式的构造图