外观模式(Facade pattern)涉及到子系统的一些类。所谓子系统,是为提供一系列相关的特征(功能)而紧密关联的一组类。例如,一个Account类、Address类和CreditCard类相互关联,成为子系统的一部分,提供在线客户的特征。
在真实的应用系统中,一个子系统可能由很多类组成。子系统的客户为了它们的需要,需要和子系统中的一些类进行交互。客户和子系统的类进行直接的交互会导致客户端对象和子系统(Figure1)之间高度耦合。任何的类似于对子系统中类的接口的修改,会对依赖于它的所有的客户类造成影响。
Figure1: Client Interaction with Subsystem Classes before Applying the Facade Pattern |
外观模式(Facade pattern)很适用于在上述情况。外观模式(Facade pattern)为子系统提供了一个更高层次、更简单的接口,从而降低了子系统的复杂度和依赖。这使得子系统更易于使用和管理。
外观是一个能为子系统和客户提供简单接口的类。当正确的应用外观,客户不再直接和子系统中的类交互,而是与外观交互。外观承担与子系统中类交互的责任。实际上,外观是子系统与客户的接口,这样外观模式降低了子系统和客户的耦合度(Figure2).
Figure2: Client Interaction with Subsystem Classes after Applying the Facade Pattern |
从Figure2中我们可以看到:外观对象隔离了客户和子系统对象,从而降低了耦合度。当子系统中的类进行改变时,客户端不会像以前一样受到影响。
尽管客户使用由外观提供的简单接口,但是当需要的时候,客户端还是可以视外观不存在,直接访问子系统中的底层次的接口。这种情况下,它们之间的依赖/耦合度和原来一样。
例子:
让我们建立一个应用:
(1) 接受客户的详细资料(账户、地址和信用卡信息)
(2) 验证输入的信息
(3) 保存输入的信息到相应的文件中。
这个应用有三个类:Account、Address和CreditCard。每一个类都有自己的验证和保存数据的方法。
Listing1: AccountClass
public class Account {
String firstName; String lastName; final String ACCOUNT_DATA_FILE = "AccountData.txt"; public Account(String fname, String lname) { firstName = fname; lastName = lname; } public boolean isValid() { /* Let‘s go with simpler validation here to keep the example simpler. */ … … } public boolean save() { FileUtil futil = new FileUtil(); String dataLine = getLastName() + ”," + getFirstName(); return futil.writeToFile(ACCOUNT_DATA_FILE, dataLine,true, true); } public String getFirstName() { return firstName; } public String getLastName() { return lastName; } } |
Listing2: Address Class
public class Address {
String address; String city; String state; final String ADDRESS_DATA_FILE = "Address.txt"; public Address(String add, String cty, String st) { address = add; city = cty; state = st; } public boolean isValid() { /* The address validation algorithm could be complex in real-world applications. Let‘s go with simpler validation here to keep the example simpler. */ if (getState().trim().length() < 2) return false; return true; } public boolean save() { FileUtil futil = new FileUtil(); String dataLine = getAddress() + ”," + getCity() + ”," + getState(); return futil.writeToFile(ADDRESS_DATA_FILE, dataLine,true, true); } public String getAddress() { return address; } public String getCity() { return city; } public String getState() { return state; } } |
Listing3: CreditCard Class
public class CreditCard {
String cardType; String cardNumber; String cardExpDate; final String CC_DATA_FILE = "CC.txt"; public CreditCard(String ccType, String ccNumber, String ccExpDate) { cardType = ccType; cardNumber = ccNumber; cardExpDate = ccExpDate; } public boolean isValid() { /* Let‘s go with simpler validation here to keep the example simpler. */ if (getCardType().equals(AccountManager.VISA)) { return (getCardNumber().trim().length() == 16); } if (getCardType().equals(AccountManager.DISCOVER)) { return (getCardNumber().trim().length() == 15); } if (getCardType().equals(AccountManager.MASTER)) { return (getCardNumber().trim().length() == 16); } return false; } public boolean save() { FileUtil futil = new FileUtil(); String dataLine = getCardType() + ,”" + getCardNumber() + ”," + getCardExpDate(); return futil.writeToFile(CC_DATA_FILE, dataLine, true, true); } public String getCardType() { return cardType; } public String getCardNumber() { return cardNumber; } public String getCardExpDate() { return cardExpDate; } } |
Figure3: Subsystem Classes to Provide the Necessary Functionality to Validate and Save the Customer Data |
让我们建立一个客户AccountManager,它提供用户输入数据的用户界面。
Listing4: Client AccountManager Class
public class AccountManager extends JFrame {
public static final String newline = "\n"; public static final String VALIDATE_SAVE = "Validate & Save"; … … public AccountManager() { super(" Facade Pattern - Example "); cmbCardType = new JComboBox(); cmbCardType.addItem(AccountManager.VISA); cmbCardType.addItem(AccountManager.MASTER); cmbCardType.addItem(AccountManager.DISCOVER); … … //Create buttons JButton validateSaveButton = new JButton(AccountManager.VALIDATE_SAVE); … … } public String getFirstName() { return txtFirstName.getText(); } … … }//End of class AccountManager |
当客户AccountManage运行的时候,展示的用户接口如下:
Figure4: User Interface to Enter the Customer Data |
为了验证和保存输入的数据,客户AccountManager需要:
(1) 建立Account、Address和CreditCard对象。
(2) 用这些对象验证输入的数据
(3) 用这些对象保存输入的数据。
下面是对象间的交互顺序图:
Figure5: How a Client Would Normally Interact (Directly) with Subsystem Classes to Validate and Save the Customer Data |
在这个例子中应用外观模式是一个很好的设计,它可以降低客户和子系统组件(Address、Account和CreditCard)之间的耦合度。应用外观模式,让我们定义一个外观类CustomerFacade
(Figure6 and Listing5)。它为由客户数据处理类(Address、Account和CreditCard)所组成的子系统提供一个高层次的、简单的接口。
CustomerFacade
address:String city:String state:String cardType:String cardNumber:String cardExpDate:String fname:String lname:String setAddress(inAddress:String) setCity(inCity:String) setState(inState:String) setCardType(inCardType:String) setCardNumber(inCardNumber:String) setCardExpDate(inCardExpDate:String) setFName(inFName:String) setLName(inLName:String) saveCustomerData() |
Figure6: Facade Class to Be Used by the Client in the Revised Design |
Listing5: CustomerFacade Class
public class CustomerFacade {
private String address; private String city; private String state; private String cardType; private String cardNumber; private String cardExpDate; private String fname; private String lname; public void setAddress(String inAddress) { address = inAddress; } public void setCity(String inCity) { city = inCity; } public void setState(String inState) { state = inState; } public void setFName(String inFName) { fname = inFName; } public void setLName(String inLName) { lname = inLName; } public void setCardType(String inCardType) { cardType = inCardType; } public void setCardNumber(String inCardNumber) { cardNumber = inCardNumber; } public void setCardExpDate(String inCardExpDate) { cardExpDate = inCardExpDate; } public boolean saveCustomerData() { Address objAddress; Account objAccount; CreditCard objCreditCard; /* client is transparent from the following set of subsystem related operations. */ boolean validData = true; String errorMessage = ""; objAccount = new Account(fname, lname); if (objAccount.isValid() == false) { validData = false; errorMessage = "Invalid FirstName/LastName"; } objAddress = new Address(address, city, state); if (objAddress.isValid() == false) { validData = false; errorMessage = "Invalid Address/City/State"; } objCreditCard = new CreditCard(cardType, cardNumber, cardExpDate); if (objCreditCard.isValid() == false) { validData = false; errorMessage = "Invalid CreditCard Info"; } if (!validData) { System.out.println(errorMessage); return false; } if (objAddress.save() && objAccount.save() && objCreditCard.save()) { return true; } else { return false; } } } |
CustomerFacade类以saveCustomData方法的形式提供了业务层次上的服务。客户AccountManager不是直接和子系统的每一个组件交互,而是使用了由CustomFacade对象提供的验证和保存客户数据的更高层次、更简单的接口(Figure7).
Figure7: Class Association with the Fa?ade Class in Place 。 |
在新的设计中,为了验证和保存客户数据,客户需要:
(1) 建立或获得外观对象CustomFacade的一个实例。
(2) 传递数据给CustomFacade实例进行验证和保存。
(3) 调用CustomFacade实例上的saveCustomData方法。
CustomFacade处理创建子系统中必要的对象并且调用这些对象上相应的验证、保存客户数据的方法这些细节问题。客户不再需要直接访问任何的子系统中的对象。
Figure8展示了新的设计的消息流图:
Figure 22.8: In the Revised Design, Clients Interact with the Fa?ade Instance to Interface with the Subsystem |
重要提示:
下面是应用外观模式的注意事项:
(1) 在设计外观时,不需要增加额外的功能。
(2) 不要从外观方法中返回子系统中的组件给客户。例如:有一个下面的方法:
CreditCard getCreditCard()
会报漏子系统的细节给客户。应用就不能从应用外观模式中取得最大的好处。
(3)应用外观的目的是提供一个高层次的接口。因此,外观方法最适合提供特定的高层次的业务服务,而不是进行底层次的单独的业务执行。
以上是一个比较全面的例子,另外,为了加深理解我们继续学习下面的内容。
相关角色:
1.外观(Facade)角色:客户端可以调用这个角色的方法。此角色知晓相关的子系统的功能和责任。
2.子系统角色:可以同时有一个或者多个子系统。每一个子系统都不是一个单独的类,而是一个类的集合。每一个子系统都可以被客户端直接调用,或者被外观角色调用。
适用情况:
1.为复杂的子系统提供一个简单的接口;
2.客户程序与抽象类的实现部分之间存在着很大的依赖性;
3.构建一个层次结构的子系统时,适用外观模式定义子系统中每层的入口点。
外观模式的简单实现:
代码:
Camara.java
package facade; public class Camara { public void turnOn() { System.out.println("开启摄像头!"); } public void turnOff() { System.out.println("关闭摄像头!"); } }
Light.java
package facade; public class Light { public void turnOn() { System.out.println("开灯!"); } public void turnOff() { System.out.println("关灯!"); } }
Sensor.java
package facade; public class Sensor { public void activate() { System.out.println("开启感应器!"); } public void deactivate() { System.out.println("关闭感应器!"); } }
MyFacade.java
package facade; public class MyFacade { private static Camara c1, c2; private static Light l1, l2, l3; private static Sensor s; static { c1 = new Camara(); c2 = new Camara(); l1 = new Light(); l2 = new Light(); l3 = new Light(); s = new Sensor(); } public static void activate() { c1.turnOn(); c2.turnOn(); l1.turnOn(); l2.turnOn(); l3.turnOn(); s.activate(); } public static void deactivate() { c1.turnOff(); c2.turnOff(); l1.turnOff(); l2.turnOff(); l3.turnOff(); s.deactivate(); } }
ClientTest.java
package facade; public class ClientTest { /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub //打开 MyFacade.activate(); //关闭 MyFacade.deactivate(); } }
实际应用中,我们在对付一些老旧的code(尤其是将C的代码转成C++代码)或者即便不是老旧code,但涉及多个子系统时,除了重写全部代码
(对于老旧code而言),我们还可能采用这样一种策略:重新进行类的设计,将原来分散在源码中的类/结构及方法重新组合,形成新的、统一的接口,
供上层应用使用。
这在某种意义上与Adapter及Proxy有类似之处,但是,Proxy(代理)注重在为Client-Subject提供一个访问的中间层,如CORBA可为应
用程序提供透明访问支持,使应用程序无需去考虑平台及网络造成的差异及其它诸多技术细节;Adapter(适配器)注重对接口的转换与调整;而
Facade所面对的往往是多个类或其它程序单元,通过重新组合各类及程序单元,对外提供统一的接口/界面。
Facade模式应用
在遇到以下情况使用Facade模式:
1、当你要为一个复杂子系统提供一个简单接口时。子系统往往因为不断演化而变得越来越复杂。大多数模式使用时都会产生更多更小的类。这使得子系
统更具可重用性,也更容易对子系统进行定制,但这也给那些不需要定制子系统的用户带来一些使用上的困难。
Facade可以提供一个简单的缺省视图,这一视图对大多数用户来说已经足够,而那些需要更多的可定制性的用户可以越过Facade层。
2、客户程序与抽象类的实现部分之间存在着很大的依赖性。引入Facade将这个子系统与客户以及其他的子系统分离,可以提高子系统的独立性和可移
植性。
3、当你需要构建一个层次结构的子系统时,使用Facade模式定义子系统中每层的入口点,如果子系统之间是相互依赖的,你可以让它们仅通过Facade
进行通讯,从而简化了它们之间的依赖关系。
Facade模式优缺点
Facade模式有下面一些优点:
1、它对客户屏蔽子系统组件,因而减少了客户处理的对象的数目并使得子系统使用起来更加方便。
2、它实现了子系统与客户之间的松耦合关系,而子系统内部的功能组件往往是紧耦合的。
松耦合关系使得子系统的组件变化不会影响到它的客户。Facade模式有助于建立层次结构系统,也有助于对对象之间的依赖关系分层。Facade模式可以
消除复杂的循环依赖关系。这一点在客户程序与子系统是分别实现的时候尤为重要。
在大型软件系统中降低编译依赖性至关重要。在子系统类改变时,希望尽量减少重编译工作以节省时间。用Facade可以降低编译依赖性,限制重要系统中较
小的变化所需的重编译工作。Facade模式同样也有利于简化系统在不同平台之间的移植过程,因为编译一个子系统一般不需要编译所有其他的子系统。
3、如果应用需要,它并不限制它们使用子系统类。因此你可以在系统易用性和通用性之间加以选择。
图实例:
package design.facade; /** * 文件名称:ServiceA.java * 创建人:Fei Wong * 创建时间: Jun 29, 2012 * 电子邮箱:[email protected] * */ public interface ServiceA { /** * ServiceA 的A方法 * */ public void methodA() ; } package design.facade; /** * 文件名称:ServiceAImpl.java * 创建人:Fei Wong * 创建时间: Jun 29, 2012 * 电子邮箱:[email protected] * */ public class ServiceAImpl implements ServiceA { /* (non-Javadoc) * @see design.facade.ServiceA#methodA() */ @Override public void methodA() { System.out.println( "methodA--> is runing" ); } } package design.facade; /** * 文件名称:ServiceB.java * 创建人:Fei Wong * 创建时间: Jun 29, 2012 * 电子邮箱:[email protected] * */ public interface ServiceB { /** * ServiceB 的B方法 * */ public void methodB() ; } package design.facade; /** * 文件名称:ServiceAImpl.java * 创建人:Fei Wong * 创建时间: Jun 29, 2012 * 电子邮箱:[email protected] * */ public class ServiceBImpl implements ServiceB { /* (non-Javadoc) * @see design.facade.ServiceA#methodA() */ @Override public void methodB() { System.out.println( "methodB--> is runing" ); } } package design.facade; /** * 文件名称:ServiceC.java * 创建人:Fei Wong * 创建时间: Jun 29, 2012 * 电子邮箱:[email protected] * */ public interface ServiceC { /** * ServiceC 的C方法 * */ public void methodC() ; } package design.facade; /** * 文件名称:ServiceAImpl.java * 创建人:Fei Wong * 创建时间: Jun 29, 2012 * 电子邮箱:[email protected] * */ public class ServiceCImpl implements ServiceC { /* (non-Javadoc) * @see design.facade.ServiceA#methodA() */ @Override public void methodC() { System.out.println( "methodC--> is runing" ); } } package design.facade; /** * 文件名称:Facade.java * 创建人:Fei Wong * 创建时间: Jun 29, 2012 * 电子邮箱:[email protected] * * 外观模式 核心类 * */ public class Facade { ServiceA sa; ServiceB sb; ServiceC sc; public Facade() { sa = new ServiceAImpl(); sb = new ServiceBImpl(); sc = new ServiceCImpl(); } public void methodA() { sa.methodA(); sb.methodB(); } public void methodB() { sb.methodB(); sc.methodC(); } public void methodC() { sc.methodC(); sa.methodA(); } } package design.facade; /** * 文件名称:Client.java * 创建人:Fei Wong * 创建时间: Jun 29, 2012 * 电子邮箱:[email protected] * */ public class Client { /** * @param args */ public static void main(String[] args) { ServiceA sa = new ServiceAImpl(); ServiceB sb = new ServiceBImpl(); sa.methodA(); sb.methodB(); System.out.println("====================="); Facade f = new Facade(); f.methodA(); f.methodB(); f.methodC() ; } }
本文借鉴文章:
http://dev.yesky.com/203/2175203.shtml
http://blog.csdn.net/hfmbook/article/details/7702642
http://liyf155.iteye.com/blog/1189789
版权声明:欢迎转载,希望在你转载的同时,添加原文地址,谢谢配合