您的位置:首页 > 编程语言 > Java开发

《Java设计模式》之外观模式

2015-08-16 08:00 507 查看
 外观模式(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_S***E = "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_S***E);

  …

  …

 }

 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
 * 电子邮箱:feiwong8@126.com
 * */

public interface ServiceA {
	/**
	 * ServiceA 的A方法 
	 * */
	public void methodA() ;
}

package design.facade;

/**
 * 文件名称:ServiceAImpl.java
 * 创建人:Fei Wong
 * 创建时间: Jun 29, 2012
 * 电子邮箱:feiwong8@126.com
 * */

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
 * 电子邮箱:feiwong8@126.com
 * */

public interface ServiceB {
	/**
	 * ServiceB 的B方法
	 * */
	public void methodB() ;
}

package design.facade;

/**
 * 文件名称:ServiceAImpl.java
 * 创建人:Fei Wong
 * 创建时间: Jun 29, 2012
 * 电子邮箱:feiwong8@126.com
 * */

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
 * 电子邮箱:feiwong8@126.com
 * */

public interface ServiceC {
	/**
	 * ServiceC 的C方法
	 * */
	public void methodC() ;  
}

package design.facade;

/**
 * 文件名称:ServiceAImpl.java
 * 创建人:Fei Wong
 * 创建时间: Jun 29, 2012
 * 电子邮箱:feiwong8@126.com
 * */

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
 * 电子邮箱:feiwong8@126.com
 * 
 * 外观模式 核心类
 * */

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
 * 电子邮箱:feiwong8@126.com
 * */

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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: