描述:
外观模式(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
闽公网安备 35060202000074号