码迷,mamicode.com
首页 > 编程语言 > 详细

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

时间:2015-08-16 08:14:07      阅读:254      评论:0      收藏:0      [点我收藏+]

标签:

 外观模式(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_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运行的时候,展示的用户接口如下:

技术分享  
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

版权声明:欢迎转载,希望在你转载的同时,添加原文地址,谢谢配合

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

标签:

原文地址:http://blog.csdn.net/u011225629/article/details/47699233

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!