站内搜索: 请输入搜索关键词

当前页面: 开发资料首页Java 专题Java设计模式之外观模式研究

Java设计模式之外观模式研究

摘要: Java设计模式之外观模式研究

</td> </tr> <tr> <td height="35" valign="top" class="ArticleTeitle"> <table width="100%" border="0" cellspacing="0" cellpadding="0"> <tr> <td width="282" height="86" align="center" valign="top"> </td> <td width="402" valign="top">

外观模式(Facade pattern)涉及到子系统的一些类。所谓子系统,是为提供一系列相关的特征(功能)而紧密关联的一组类。例如,一个Account类、Address类和CreditCard类相互关联,成为子系统的一部分,提供在线客户的特征。

   在真实的应用系统中,一个子系统可能由很多类组成。子系统的客户为了它们的需要,需要和子系统中的一些类进行交互。客户和子系统的类进行直接的交互会导 致客户端对象和子系统(Figure1)之间高度耦合。任何的类似于对子系统中类的接口的修改,会对依赖于它的所有的客户类造成影响。

</td></tr> </table>
 
<table align="center" border="0" width="90%"> <tr> <td>
500)this.style.width=500;">
Figure1: Client Interaction with Subsystem Classes before Applying the Facade Pattern </td> </tr> </table>
   外观模式(Facade pattern)很适用于在上述情况。外观模式(Facade pattern)为子系统提供了一个更高层次、更简单的接口,从而降低了子系统的复杂度和依赖。这使得子系统更易于使用和管理。

   外观是一个能为子系统和客户提供简单接口的类。当正确的应用外观,客户不再直接和子系统中的类交互,而是与外观交互。外观承担与子系统中类交互的责任。实际上,外观是子系统与客户的接口,这样外观模式降低了子系统和客户的耦合度(Figure2).

<table align="center" border="0" width="90%"> <tr> <td> 500)this.style.width=500;">
Figure2: Client Interaction with Subsystem Classes after Applying the Facade Pattern </td> </tr> </table>
   从Figure2中我们可以看到:外观对象隔离了客户和子系统对象,从而降低了耦合度。当子系统中的类进行改变时,客户端不会像以前一样受到影响。

   尽管客户使用由外观提供的简单接口,但是当需要的时候,客户端还是可以视外观不存在,直接访问子系统中的底层次的接口。这种情况下,它们之间的依赖/耦合度和原来一样。

   例子:

   让我们建立一个应用:

   (1) 接受客户的详细资料(账户、地址和信用卡信息)

   (2) 验证输入的信息

   (3) 保存输入的信息到相应的文件中。

   这个应用有三个类:Account、Address和CreditCard。每一个类都有自己的验证和保存数据的方法。

   Listing1: AccountClass

<table align="center" bgcolor="#e3e3e3" border="1" bordercolor="#cccccc" width="90%"> <tr> <td>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;
  }
} </td> </tr> </table>
   Listing2: Address Class

<table align="center" bgcolor="#e3e3e3" border="1" bordercolor="#cccccc" width="90%"> <tr> <td>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;
  }
} </td> </tr> </table>
   Listing3: CreditCard Class

<table align="center" bgcolor="#e3e3e3" border="1" bordercolor="#cccccc" width="90%"> <tr> <td>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;
  }
} </td> </tr> </table>
<table align="center" border="0" width="90%"> <tr> <td> 500)this.style.width=500;">
Figure3: Subsystem Classes to Provide the Necessary Functionality to Validate and Save the Customer Data </td> </tr> </table>
让我们建立一个客户AccountManager,它提供用户输入数据的用户界面。

   Listing4: Client AccountManager Class

<table align="center" bgcolor="#e3e3e3" border="1" bordercolor="#cccccc" width="90%"> <tr> <td>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 </td> </tr> </table>
   当客户AccountManage运行的时候,展示的用户接口如下:

<table align="center" border="0" width="90%"> <tr> <td>
500)this.style.width=500;">
Figure4: User Interface to Enter the Customer Data </td> </tr> </table>
   为了验证和保存输入的数据,客户AccountManager需要:

   (1) 建立Account、Address和CreditCard对象。

   (2) 用这些对象验证输入的数据

   (3) 用这些对象保存输入的数据。

   下面是对象间的交互顺序图:

<table align="center" border="0" width="90%"> <tr> <td> 500)this.style.width=500;">
Figure5: How a Client Would Normally Interact (Directly) with Subsystem Classes to Validate and Save the Customer Data </td> </tr> </table>
   在这个例子中应用外观模式是一个很好的设计,它可以降低客户和子系统组件(Address、Account和CreditCard)之间的耦合度。应用 外观模式,让我们定义一个外观类CustomerFacade (Figure6 and Listing5)。它为由客户数据处理类(Address、Account和CreditCard)所组成的子系统提供一个高层次的、简单的接口。

<table align="center" bgcolor="#e3e3e3" border="1" bordercolor="#cccccc" width="90%"> <tr> <td>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()</td> </tr> </table>
<table align="center" border="0" width="90%"> <tr> <td> 500)this.style.width=500;">
Figure6: Facade Class to Be Used by the Client in the Revised Design </td> </tr> </table>
   Listing5: CustomerFacade Class

<table align="center" bgcolor="#e3e3e3" border="1" bordercolor="#cccccc" width="90%"> <tr> <td>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;
   }
  }
} </td> </tr> </table>
   CustomerFacade类以saveCustomData方法的形式提供了业务层次上的服务。客户AccountManager不是直接和子系统 的每一个组件交互,而是使用了由CustomFacade对象提供的验证和保存客户数据的更高层次、更简单的接口(Figure7).

<table align="center" border="0" width="90%"> <tr> <td> 500)this.style.width=500;">
Figure7: Class Association with the Fa?ade Class in Place 。</td> </tr> </table>
   在新的设计中,为了验证和保存客户数据,客户需要:

   (1) 建立或获得外观对象CustomFacade的一个实例。

   (2) 传递数据给CustomFacade实例进行验证和保存。

   (3) 调用CustomFacade实例上的saveCustomData方法。

   CustomFacade处理创建子系统中必要的对象并且调用这些对象上相应的验证、保存客户数据的方法这些细节问题。客户不再需要直接访问任何的子系统中的对象。

   Figure8展示了新的设计的消息流图:

<table align="center" border="0" width="90%"> <tr> <td> 500)this.style.width=500;">
Figure 22.8: In the Revised Design, Clients Interact with the Fa?ade Instance to Interface with the Subsystem </td> </tr> </table>
   重要提示

   下面是应用外观模式的注意事项:

   (1) 在设计外观时,不需要增加额外的功能。

   (2) 不要从外观方法中返回子系统中的组件给客户。例如:有一个下面的方法:

   CreditCard getCreditCard()

   会报漏子系统的细节给客户。应用就不能从应用外观模式中取得最大的好处。

   (3)应用外观的目的是提供一个高层次的接口。因此,外观方法最适合提供特定的高层次的业务服务,而不是进行底层次的单独的业务执行。
</td> </tr> <tr>


↑返回目录
前一篇: JDK1.5枚举类型
后一篇: JAVA IO包中的Decorator模式