外观模式(Facade),为子系统中的一组接口提供统一的接口。此模式定义了一个高层接口,这个接口使子系统更易于使用。简而言之,外观模式提供了到复杂子系统的简化接口。
外观模式解析
角色介绍
- Facade:外观类,知道哪些子系统负责处理请求,将客户的请求代理给适当的子系统。
- SubSystemOne/SubSystemTwo:子系统类,实现子系统的功能,处理 Facade 对象指派的任务。
外观模式基本代码
- Facade 类:外观类
1 | /** |
2 | * Facade |
3 | */ |
4 | public class Facade { |
5 | |
6 | private SubSystemOne subSystemOne; |
7 | |
8 | private SubSystemTwo subSystemTwo; |
9 | |
10 | public Facade() { |
11 | this.subSystemOne = new SubSystemOne(); |
12 | this.subSystemTwo = new SubSystemTwo(); |
13 | } |
14 | |
15 | public void operationA() { |
16 | this.subSystemOne.operation1(); |
17 | this.subSystemTwo.operation2(); |
18 | } |
19 | |
20 | public void operationB() { |
21 | this.subSystemTwo.operation2(); |
22 | } |
23 | } |
- SubSystemOne 类:子系统一
1 | /** |
2 | * SubSystemOne |
3 | */ |
4 | public class SubSystemOne { |
5 | |
6 | public void operation1() { |
7 | System.out.println("operation1"); |
8 | } |
9 | } |
- SubSystemTwo 类:子系统二
1 | /** |
2 | * SubSystemTwo |
3 | */ |
4 | public class SubSystemTwo { |
5 | |
6 | public void operation2() { |
7 | System.out.println("operation2"); |
8 | } |
9 | } |
示列
假设我们有一个带有一组接口的应用程序,以使用 MySQL / SQLServer 数据库并生成不同类型的报告,比如 HTML 报告,PDF 报告等。
因此,我们将使用不同的接口集来处理不同类型的数据库。 然后,客户端应用程序可以使用这些接口来获取所需的数据库连接并生成报告。
使用外观设计模式实现
首先,创建两个帮助程序接口,分别是 MySQLHelper 和 SQLServerHelper。
1 | /** |
2 | * MySQLHelper |
3 | */ |
4 | public class MySQLHelper { |
5 | |
6 | public static Connection getMySQLDBConnection(){ |
7 | // 获取 MySQL 数据库连接对象 |
8 | return null; |
9 | } |
10 | |
11 | public void generateMySQLPDFReport(String tableName, Connection con){ |
12 | // 从数据库中获取数据并生成 PDF 报告 |
13 | } |
14 | |
15 | public void generateMySqlHTMLReport(String tableName, Connection con){ |
16 | // 从数据库中获取数据并生成 HTML 报告 |
17 | } |
18 | } |
19 | |
20 | /** |
21 | * SQLServerHelper |
22 | */ |
23 | public class SQLServerHelper { |
24 | |
25 | public static Connection getSQLServerDBConnection(){ |
26 | // 获取 SQLServer 数据库连接对象 |
27 | return null; |
28 | } |
29 | |
30 | public void generateSQLServerPDFReport(String tableName, Connection con){ |
31 | // 从数据库中获取数据并生成 PDF 报告 |
32 | } |
33 | |
34 | public void generateSQLServerHTMLReport(String tableName, Connection con){ |
35 | // 从数据库中获取数据并生成 HTML 报告 |
36 | } |
37 | |
38 | } |
然后,创建 Facade 类,为 MySQLHelper 和 SQLServerHelper 提供统一的界面。
1 | /** |
2 | * HelperFacade |
3 | */ |
4 | public class HelperFacade { |
5 | |
6 | public static void generateReport(DBTypes dbType, ReportTypes reportType, String tableName){ |
7 | Connection con = null; |
8 | switch (dbType){ |
9 | case MYSQL: |
10 | con = MySQLHelper.getMySQLDBConnection(); |
11 | MySQLHelper mySqlHelper = new MySQLHelper(); |
12 | switch(reportType){ |
13 | case HTML: |
14 | mySqlHelper.generateMySQLHTMLReport(tableName, con); |
15 | break; |
16 | case PDF: |
17 | mySqlHelper.generateMySQLPDFReport(tableName, con); |
18 | break; |
19 | } |
20 | break; |
21 | case ORACLE: |
22 | con = SQLServerHelper.getOracleDBConnection(); |
23 | SQLServerHelper sqlServerHelper = new SQLServerHelper(); |
24 | switch(reportType){ |
25 | case HTML: |
26 | sqlServerHelper.generateSQLServerHTMLReport(tableName, con); |
27 | break; |
28 | case PDF: |
29 | sqlServerHelper.generateSQLServerPDFReport(tableName, con); |
30 | break; |
31 | } |
32 | break; |
33 | } |
34 | |
35 | } |
36 | |
37 | public static enum DBTypes{ |
38 | MYSQL, SQLSERVER; |
39 | } |
40 | |
41 | public static enum ReportTypes{ |
42 | HTML, PDF; |
43 | } |
44 | } |
最后,创建客户端程序,分别使用 Facade 模式和不使用 Facade 模式生成报告。
1 | public class Client { |
2 | |
3 | public static void main(String[] args) { |
4 | String tableName = "Student"; |
5 | |
6 | // 不使用 Facade 模式生成报告 |
7 | Connection con = MySqlHelper.getMySqlDBConnection(); |
8 | MySqlHelper mySqlHelper = new MySqlHelper(); |
9 | mySqlHelper.generateMySqlHTMLReport(tableName, con); |
10 | |
11 | Connection con1 = OracleHelper.getOracleDBConnection(); |
12 | OracleHelper oracleHelper = new OracleHelper(); |
13 | oracleHelper.generateOraclePDFReport(tableName, con1); |
14 | |
15 | // 使用 Facade 模式生成报告 |
16 | HelperFacade.generateReport(HelperFacade.DBTypes.MYSQL, HelperFacade.ReportTypes.HTML, tableName); |
17 | HelperFacade.generateReport(HelperFacade.DBTypes.ORACLE, HelperFacade.ReportTypes.PDF, tableName); |
18 | } |
19 | } |
从程序中可知,使用 Facade 模式界面是一种避免客户端逻辑过多的简便方法。获得数据库连接的 JDBC Driver Manager 类是外观设计模式的一个很好的例子。
我们还可以将工厂模式与外观模式结合使用,改善上面的代码,使程序更加易用。
小结
外观设计模式更像是客户端应用程序的助手,它不会对客户端隐藏子系统接口,是否使用Facade 完全取决于客户端代码。
当客户端与抽象的实现类之间存在许多依赖关系,可以引入外观模式让子系统与客户端和其他子系统分离,从而提高子系统的独立性和可移植性。
外观设计模式应应用于相似类型的接口,其目的是提供一个单一的接口,而不是提供多个执行相似类型工作的接口。