描述:
外观模式(Fa?ade pattern)涉及到子系统的一些类。所谓子系统,是为提供一系列相关的特征(功能)而紧密关联的一组类。例如,一个Account类、Address类和CreditCard类相互关联,成为子系统的一部分,提供在线客户的特征。
在真实的应用系统中,一个子系统可能由很多类组成。子系统的客户为了它们的需要,需要和子系统中的一些类进行交互。客户和子系统的类进行直接的交互会导致客户端对象和子系统(Figure 22.1)之间高度耦合。任何的类似于对子系统中类的接口的修改,会对依赖于它的所有的客户类造成影响。
Figure 22.1: Client Interaction with Subsystem Classes before Applying the Fa?ade Pattern
外观模式(Fa?ade pattern)很适用于在上述情况。外观模式(Fa?ade pattern)为子系统提供了一个更高层次、更简单的接口,从而降低了子系统的复杂度和依赖。这使得子系统更易于使用和管理。
外观是一个能为子系统和客户提供简单接口的类。当正确的应用外观,客户不再直接和子系统中的类交互,而是与外观交互。外观承担与子系统中类交互的责任。实际上,外观是子系统与客户的接口,这样外观模式降低了子系统和客户的耦合度(Figure 22.2).
Figure 22.2: Client Interaction with Subsystem Classes after Applying the Fa?ade Pattern
从Figure 22.2中我们可以看到:外观对象隔离了客户和子系统对象,从而降低了耦合度。当子系统中的类进行改变时,客户端不会像以前一样受到影响。
尽管客户使用由外观提供的简单接口,但是当需要的时候,客户端还是可以视外观不存在,直接访问子系统中的底层次的接口。这种情况下,它们之间的依赖/耦合度和原来一样。
例子: 让我们建立一个应用:
(1) 接受客户的详细资料(账户、地址和信用卡信息)
(2) 验证输入的信息
(3) 保存输入的信息到相应的文件中。
这个应用有三个类:Account、Address和CreditCard。每一个类都有自己的验证和保存数据的方法。
Listing 22.1: 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;
}
}
Listing 22.2: 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;
}
}
Listing 22.3: 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;
}
}
让我们建立一个客户AccountManager,它提供用户输入数据的用户界面。
Listing 22.4: 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运行的时候,展示的用户接口如下:
Figure 22.4: User Interface to Enter the Customer Data
为了验证和保存输入的数据,客户AccountManager需要:
(1) 建立Account、Address和CreditCard对象。
(2) 用这些对象验证输入的数据
(3) 用这些对象保存输入的数据。
下面是对象间的交互顺序图:
Figure 22.5: How a Client Would Normally Interact (Directly) with Subsystem Classes to Validate and Save the Customer Data
在这个例子中应用外观模式是一个很好的设计,它可以降低客户和子系统组件(Address、Account和CreditCard)之间的耦合度。应用外观模式,让我们定义一个外观类CustomerFacade (Figure 22.6 and Listing 22.5)。它为由客户数据处理类(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()
Figure 22.6: Fa?ade Class to Be Used by the Client in the Revised Design
Listing 22.5: CustomerFacade Cl