1、Java 设计模式之外观模式研究外观模式(Facade pattern)涉及到子系统的一些类。所谓子系统,是为提供一系列相关的特征(功能)而紧密关联的一组类。例如,一个 Account 类、Address 类和 CreditCard 类相互关联,成为子系统的一部分,提供在线客户的特征。在真实的应用系统中,一个子系统可能由很多类组成。子系统的客户为了它们的需要,需要和子系统中的一些类进行交互。客户和子系统的类进行直接的交互会导致客户端对象和子系统(Figure1)之间高度耦合。任何的类似于对子系统中类的接口的修改,会对依赖于它的所有的客户类造成影响。Figure1: Client Interac
2、tion with Subsystem Classes before Applying the Facade Pattern 外观模式(Facade pattern)很适用于在上述情况。外观模式( Facade pattern)为子系统提供了一个更高层次、更简单的接口,从而降低了子系统的复杂度和依赖。这使得子系统更易于使用和管理。外观是一个能为子系统和客户提供简单接口的类。当正确的应用外观,客户不再直接和子系统中的类交互,而是与外观交互。外观承担与子系统中类交互的责任。实际上,外观是子系统与客户的接口,这样外观模式降低了子系统和客户的耦合度(Figure2). Figure2: Client
3、Interaction with Subsystem Classes after Applying the Facade Pattern 从 Figure2 中我们可以看到:外观对象隔离了客户和子系统对象,从而降低了耦合度。当子系统中的类进行改变时,客户端不会像以前一样受到影响。尽管客户使用由外观提供的简单接口,但是当需要的时候,客户端还是可以视外观不存在,直接访问子系统中的底层次的接口。这种情况下,它们之间的依赖/耦合度和原来一样。 例子:让我们建立一个应用:(1) 接受客户的详细资料(账户、地址和信用卡信息)(2) 验证输入的信息(3) 保存输入的信息到相应的文件中。这个应用有三个类:Ac
4、count、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() /* Lets go with s
5、impler 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 getLast
6、Name() 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
7、algorithm could be complex in real-world applications. Lets 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() + ”,“ + get
8、State(); 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;
9、 final String CC_DATA_FILE = “CC.txt“; public CreditCard(String ccType, String ccNumber, String ccExpDate) cardType = ccType; cardNumber = ccNumber; cardExpDate = ccExpDate; public boolean isValid() /* Lets go with simpler validation here to keep the example simpler. */ if (getCardType().equals(Acco
10、untManager.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
11、 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
12、 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 fina
13、l String VALIDATE_SAVE = “Validate public AccountManager() super(“ Facade Pattern - Example “); cmbCardType = new JComboBox(); cmbCardType.addItem(AccountManager.VISA); cmbCardType.addItem(AccountManager.MASTER); cmbCardType.addItem(AccountManager.DISCOVER); /Create buttons JButton validateSaveButto
14、n = 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) 用
15、这些对象验证输入的数据(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)。它为由客户数据处理类(Ad
16、dress 、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) setCard
17、Number(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
18、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)
19、 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
20、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 Acco
21、unt(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(cardTy
22、pe, cardNumber, cardExpDate); if (objCreditCard.isValid() = false) validData = false; errorMessage = “Invalid CreditCard Info“; if (!validData) System.out.println(errorMessage); return false; if (objAddress.save() else return false; CustomerFacade 类以 saveCustomData 方法的形式提供了业务层次上的服务。客户 AccountManager
23、 不是直接和子系统的每一个组件交互,而是使用了由 CustomFacade 对象提供的验证和保存客户数据的更高层次、更简单的接口(Figure7).Figure7: Class Association with the Fa?ade Class in Place 。在新的设计中,为了验证和保存客户数据,客户需要:(1) 建立或获得外观对象 CustomFacade 的一个实例。(2) 传递数据给 CustomFacade 实例进行验证和保存。(3) 调用 CustomFacade 实例上的 saveCustomData 方法。CustomFacade 处理创建子系统中必要的对象并且调用这些对象上相应的验证、保存客户数据的方法这些细节问题。客户不再需要直接访问任何的子系统中的对象。Figure8 展示了新的设计的消息流图: