1、门面模式的定义:
门面模式(Facade Pattern)也叫做外观模式,是一种比较常用的封装模式,其定义如下:
要求一个子系统的外部与其内部的通信必须通过一个统一的对象进行。门面模式提供一个高层次的接口,使得子系统更易于使用。
2、门面模式注重“统一的对象”,也就是提供一个访问子系统的接口,除了这个接口不允许有任何访问子系统的行为发生,其通用类图如下:
Subsystem Classes是子系统所有类的简称,它可能代表一个类,也可能代表几十个对象的集合。
3、角色定义:
● Facade门面角色
客户端可以调用这个角色的方法。此角色知晓子系统的所有功能和责任。一般情况下,本角色会将所有从客户端发来的请求委派到相应的子系统去,也就说该角色没有实际的业务逻辑,只是一个委托类。
● subsystem
子系统角色可以同时有一个或者多个子系统。每一个子系统都不是一个单独的类,而是一个类的集合。子系统并不知道门面的存在。对于子系统而言,门面仅仅是另外一个客户端而已。
4、门面模式的优点
● 减少系统的相互依赖
想想看,如果我们不使用门面模式,外界访问直接深入到子系统内部,相互之间是一种强耦合关系,你死我就死,你活我才能活,这样的强依赖是系统设计所不能接受的,门面模式的出现就很好地解决了该问题,所有的依赖都是对门面对象的依赖,与子系统无关。
● 提高了灵活性
依赖减少了,灵活性自然提高了。不管子系统内部如何变化,只要不影响到门面对象,任你自由活动。
● 提高安全性
想让你访问子系统的哪些业务就开通哪些逻辑,不在门面上开通的方法,你休想访问到。
5、门面模式的缺点
门面模式最大的缺点就是不符合开闭原则,对修改关闭,对扩展开放。
6、通用代码如下:
子系统:
public class Class A { public void do Something A(){ //业务逻辑 } } public class Class B { public void do Something B(){ //业务逻辑 } } public class Class C { public void do Something C(){ //业务逻辑 } }
门面对象:
public class Facade { //被委托的对象 private Class A a = new Class A(); private Class B b = new Class B(); private Class C c = new Class C(); //提供给外部访问的方法 public void method A(){ this.a.do Something A(); } public void method B(){ this.b.do Something B(); } public void method C(){ this.c.do Something C(); } }
案例分析如下:
假设有一个登录系统,与数据库有关的操作相当于子系统,登录界面则相当于客户端,我们在中间使用一个统一的对象,即门面来使两者不直接产生交互。
Java代码如下:
客户端类Login.java
/* * 功能:学生成绩管理系统 * 步骤1、登录界面的静态实现 * 步骤2:实现界面的切换 * 步骤3:使用数据库来验证用户名和密码 * 步骤4:对代码进行优化。增加专门用来与数据库进行连接的类 * 步骤5:优化代码,增加判断条件。 * 步骤6:使用数据库进行查询时,优化查询方法和判断条件。数据库的表中可有多个数据。引入不同的表来查询。 * author:ywq */ package facade_test; import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; public class Login6 extends JFrame implements ActionListener { //定义登录界面的组件 JButton jb1,jb2,jb3=null; JRadioButton jrb1,jrb2=null; JPanel jp1,jp2,jp3,jp4=null; JTextField jtf=null; JLabel jlb1,jlb2,jlb3=null; JPasswordField jpf=null; ButtonGroup bg=null; //菜单项 JMenuBar jmb=null; JMenu jm=null; JMenuItem jmi1,jmi2=null; Facade fcd=new Facade(); //从数据库取得的密码pwd String pwd=null; public static void main(String[] args) { // TODO Auto-generated method stub Login6 ms=new Login6(); } //构造函数 public Login6() { //创建组件 jb1=new JButton("登录"); jb2=new JButton("重置"); jb3=new JButton("退出"); //设置监听 jb1.addActionListener(this); jb2.addActionListener(this); jb3.addActionListener(this); jmb=new JMenuBar(); //JMenuBar指菜单栏 jm=new JMenu("选项"); //JMenu是菜单栏中的选项栏 jmi1=new JMenuItem("开始"); //JMenuItem指选项栏中的选项 jmi2=new JMenuItem("退出系统"); jm.add(jmi1); jm.add(jmi2); jmb.add(jm); jrb1=new JRadioButton("教师",true); jrb2=new JRadioButton("学生"); bg=new ButtonGroup(); bg.add(jrb1); bg.add(jrb2); // jrb2.setSelected(true); jp1=new JPanel(); jp2=new JPanel(); jp3=new JPanel(); jp4=new JPanel(); jlb1=new JLabel("用户名:"); jlb2=new JLabel("密 码:"); jlb3=new JLabel("权 限:"); jtf=new JTextField(10); jpf=new JPasswordField(10); //加入到JPanel中 jp1.add(jlb1); jp1.add(jtf); jp2.add(jlb2); jp2.add(jpf); jp3.add(jlb3); jp3.add(jrb1); jp3.add(jrb2); jp4.add(jb1); jp4.add(jb2); jp4.add(jb3); //加入JFrame中 this.setJMenuBar(jmb); this.add(jp1); this.add(jp2); this.add(jp3); this.add(jp4); //设置布局管理器 this.setLayout(new GridLayout(4,1)); //给窗口设置标题 this.setTitle("学生成绩管理系统"); //设置窗体大小 this.setSize(300,250); //设置窗体初始位置 this.setLocation(200, 150); //设置当关闭窗口时,保证JVM也退出 this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //显示窗体 this.setVisible(true); this.setResizable(true); } @Override public void actionPerformed(ActionEvent e) { if(e.getActionCommand()=="退出") { System.exit(0); }else if(e.getActionCommand()=="登录") { if(!jtf.getText().isEmpty() && !jpf.getText().isEmpty()) { //当点击登录按钮时,首先与数据库建立连接 fcd.facade_connect(); //如果选中教师登录 if(jrb1.isSelected()) { pwd=fcd.facade_querytea("教师",jtf.getText()); // System.out.println(pwd); //首先判断是否存在该用户,即是否得到了密码 if(pwd ==null) { this.clear(); }else { //调用登录方法 this.tealogin(); } }else if(jrb2.isSelected()) //学生在登录系统 { pwd=fcd.facade_querystu("学生",jtf.getText()); //首先判断是否存在该用户,即是否得到了密码 if(pwd ==null) { this.clear(); }else { //调用登录方法 this.stulogin(); } } }else if(jtf.getText().isEmpty()) { JOptionPane.showMessageDialog(null,"请输入用户名","提示消息",JOptionPane.WARNING_MESSAGE); this.clear(); }else if(jpf.getText().isEmpty()) { JOptionPane.showMessageDialog(null,"请输入密码","提示消息",JOptionPane.WARNING_MESSAGE); this.clear(); } }else if(e.getActionCommand()=="重置") { this.clear(); } } //清空文本框和密码框 public void clear() { jtf.setText(""); jpf.setText(""); } //学生登录判断方法 public void stulogin() { if(pwd.equals(jpf.getText())) { // System.out.println("登录成功"); JOptionPane.showMessageDialog(null,"登录成功!","提示消息",JOptionPane.WARNING_MESSAGE); this.clear(); //关闭当前界面 dispose(); //创建一个新界面 // Stu_UI6 ui=new Stu_UI6(); }else if(jtf.getText().isEmpty()&&jpf.getText().isEmpty()) { JOptionPane.showMessageDialog(null,"请输入用户名和密码!","提示消息",JOptionPane.WARNING_MESSAGE); }else if(jtf.getText().isEmpty()) { JOptionPane.showMessageDialog(null,"请输入用户名!","提示消息",JOptionPane.WARNING_MESSAGE); }else if(jpf.getText().isEmpty()) { JOptionPane.showMessageDialog(null,"请输入密码!","提示消息",JOptionPane.WARNING_MESSAGE); }else { JOptionPane.showMessageDialog(null,"用户名或者密码错误! 请重新输入","提示消息",JOptionPane.ERROR_MESSAGE); //清空输入框 this.clear(); } } //教师登录判断方法 public void tealogin() { if(pwd.equals(jpf.getText())) { // System.out.println("登录成功"); JOptionPane.showMessageDialog(null,"登录成功!","提示消息",JOptionPane.WARNING_MESSAGE); this.clear(); //关闭当前界面 dispose(); //创建一个新界面,适用于教师来管理学生 // Teacher6 t=new Teacher6(); }else if(jtf.getText().isEmpty()&&jpf.getText().isEmpty()) { JOptionPane.showMessageDialog(null,"请输入用户名和密码!","提示消息",JOptionPane.WARNING_MESSAGE); }else if(jtf.getText().isEmpty()) { JOptionPane.showMessageDialog(null,"请输入用户名!","提示消息",JOptionPane.WARNING_MESSAGE); }else if(jpf.getText().isEmpty()) { JOptionPane.showMessageDialog(null,"请输入密码!","提示消息",JOptionPane.WARNING_MESSAGE); }else { JOptionPane.showMessageDialog(null,"用户名或者密码错误! 请重新输入","提示消息",JOptionPane.ERROR_MESSAGE); //清空输入框 this.clear(); } } }
数据库类如下:
/* * 功能:用来和数据库SQLserver进行连接,以及相应的查询方法。 */ package facade_test; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import javax.swing.JOptionPane; //写一个类,用来与数据库建立连接,并且查询数据 public class GetSQL { // 设定用户名和密码 String userword; String pwd; Connection ct = null; PreparedStatement ps = null; ResultSet rs = null; // 用于连接数据库的方法,可用于子类的继承 public void ConnectSQL() { try { Class.forName("sun.jdbc.odbc.JdbcOdbcDriver"); ct = DriverManager.getConnection("jdbc:odbc:ywq"); System.out.println("The SQL is connected"); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } // 用于向数据库进行查询的方法 public String querystu(String s,String username) { // 创建火箭车 try { ps = ct.prepareStatement("select * from info where 权限=? and 用户名=? "); // 给?赋值(可防止SQL注入漏洞问题),不要直接使用拼接的方式 ps.setString(1, s); ps.setString(2, username); // ResultSet结果集,大家可以把ResultSet理解成返回一张表行的结果集 rs = ps.executeQuery(); // 循环取出 if (rs.next()) { // 将教师的用户名和密码取出 userword = rs.getString(2); pwd = rs.getString(3); System.out.println("成功获取到密码和用户名from数据库"); System.out.println(userword + " " + pwd + " "); //调用登录方法 }else { JOptionPane.showMessageDialog(null, "没有此用户,请重新输入!", "提示消息", JOptionPane.WARNING_MESSAGE); } } catch (Exception e1) { // TODO Auto-generated catch block e1.printStackTrace(); } return pwd; } //在教师表中进行查询 public String querytea(String s,String name ) { // 创建火箭车 try { ps = ct.prepareStatement("select * from info_tea where 权限=? and 用户名=? "); // 给?赋值(可防止SQL注入漏洞问题),不要直接使用拼接的方式 ps.setString(1, s); ps.setString(2, name); // ResultSet结果集,大家可以把ResultSet理解成返回一张表行的结果集 rs = ps.executeQuery(); // 循环取出 if (rs.next()) { // 将教师的用户名和密码取出 userword = rs.getString(2); pwd = rs.getString(3); System.out.println("成功获取到密码和用户名from数据库"); System.out.println(userword + " " + pwd + " "); }else { JOptionPane.showMessageDialog(null, "没有此用户,请重新输入!", "提示消息", JOptionPane.WARNING_MESSAGE); } } catch (Exception e1) { // TODO Auto-generated catch block e1.printStackTrace(); } return pwd; } }
统一的对象即门面类如下:
package facade_test; /* * 门面模式,将操作数据库的类与其它客户端分开,不直接发生关系。 */ public class Facade { String pwd=null; private GetSQL Sql=new GetSQL(); public void facade_connect(){ Sql.ConnectSQL(); } public String facade_querytea(String s,String name){ pwd=Sql.querytea(s, name); return pwd; } public String facade_querystu(String s,String username){ pwd=Sql.querystu(s, username); return pwd; } }
是的,门面模式就是这么简单。