设计模式 学习笔记 之八
第10章 Facade模式
在我们编写软件时,我们常常需要与一些复杂子系统打交道。这些复杂子系统往往提供了完备而通用的API,从而满足各种客户代码的需要。站在子系统的角度看,这样做是正确的,因为它不应该对客户代码如何使用它作出太多的假定或限定,而是应该为客户代码提供尽可能全面的功能,至于客户代码怎样使用和取舍这些功能,那是客户代码所要考虑的问题了。
再站到客户代码的角度来看。特定的客户代码在使用一个功能全面的复杂子系统时,往往并不需要用到它的全部功能,而常常只需要乃至它的一小部分功能,或者只是以特定的符合自身需要的方式来使用该子系统。在这种情况下,客户代码就无需了解复杂子系统的全部细节。于是,我们可以为复杂子系统设计一个简单而专用的接口,从而简化客户代码与复杂子系统的交互。我们下面来看一个例子。
例子:使用Facade模式简化对数据库系统的访问
假设我们要编写雇员信息数据库系统。为了实现雇员信息的持久化,我们要在数据库中存储、删除和查找雇员信息。我们打算利用JDBC来完成对数据库的访问。JDBC是一个比较复杂的子系统,它被封装到java.sql包中。这个包中包含有Connection,Statement,DriverManager,ResultSet,PreparedStatement和SQLException等类。鉴于我们需要以特定于雇员信息的方式访问数据库,我们可以为这种特定的访问方式设计一个数据库门面,使得客户代码不用直接与JDBC子系统内部的各个类打交道,而只是与这个门面打交道。
这样子的系统设计是优良的设计吗?我们来分析一下。
首先,与JDBC打交道的代码都被集中到了DBFacade类中,这使得客户代码无需知道数据库访问的细节,从而降低了客户代码的复杂性,而DBFacade只向客户代码暴露一个简单而特定的接口。
其次,客户代码现在与复杂的JDBC子系统之间解耦了。也就是说,客户代码并不知道是使用JDBC来访问数据库。这种松散耦合使得以后换用其它技术来访问数据库时对客户代码的影响较小。
由以上两点看出,使用门面确实使系统的复杂性降低,同时也是松散耦合的。
简化数据库访问细节是Facade模式的一个典型应用。Facade模式的另一个典型应用就是简化GUI子系统的使用,这是因为通常GUI子系统也是一个比较复杂的子系统,而我们又常常以一些特定的方式来使用GUI子系统,因此Facade模式在这时也能派上用场。下面来看一个例子。
例子:使用JOptionPane类显示消息框
Java Swing提供了一个完备的GUI组件库,这个库中包含了几十种常用的GUI控件和相关类。假设现在我们需要一些消息对话框,向用户显示一些消息(可能是警告,确认,或者请求获得简单的输入等)。为了实现这种功能,我们需要跟Swing子系统里面的多个类打交道。但是,幸运的是,Swing已经为我们提供了一个这样的类,即JOptionPane。通过这个类,我们可以毫不费劲地创建出实用的消息对话框。实际上,这个JOptionPane就是一个Facade,它提供了一个简单而特定的接口,允许我们通过这个简单的接口就能创建消息对话框。
小结
Facade模式建议:如果只需要用到一个复杂子系统的一小部分功能,或者以特定的方式使用一个复杂子系统的时候,可以为该复杂子系统提供一个简单而特定的接口,把同繁杂子系统的交互封装到这个接口中,而让客户代码使用这个接口,而不直接与复杂子系统打交道。这样做降低了使用复杂子系统的难度,从而降低了复杂性,同时也将客户代码与复杂子系统之间解耦。
第11章 Singleton模式
类和它们的实例之间通常是一对多的关系。然而,有一些类,它们应该只有一个实例。这个实例在程序启动时被创建出来,并且只在程序结束时才被销毁。一些经常只允许有一个实例的类的例子包括:
- 应用程序的根对象(root object):通过这些对象,可以得到系统中的许多其它对象。例如,在有的程序实例化出一个Application对象,它表示了应用程序本身,因此应该只有一个实例。
- 工厂对象(factory object):用于创建系统中的其它对象。
- 主控对象(manager object):负责以恰当的方式管理和控制某些资源。
Singleton模式是一个相对简单的模式,因此在本章中我们重点关注它在Java和C++中的一些实现难点。
首先,要考虑一下在多线程环境下对单例的访问控制问题。当Singleton类中含有共享的、可变的状态时(这种情况下单例通常是某种主控对象),一定要使用恰当的同步机制来限制对Singleton类的共享的、可变的状态的并发访问。
其次,要关注Singleton类在分布式计算环境下的序列化(serialization)问题。对于无状态的Singleton类,比如工厂对象,由于它是无状态的,因此即使在分布式环境中的多个主机上存在,也没有问题,因此不需要对单例实现序列化。而对于有状态的Singleton类,这时单例通常是主控对象,负责管理和维护某种资源,因此在整个分布式环境中只能允许有一个实例存在。这时这个有状态单例是不允许被序列化的,以免它被传递到其它主机上。因此,综上所述,Singleton类通常是不被序列化的。
下面的代码显示了Java和C++中的Singleton类模板。
Java | public class Singleton /* rare option: implements Serializable*/ { public static Singleton instance() { return singleton; } /* Other methods go here. */ private static final Singleton singleton = new Singleton(); private Singleton() { init code } /* * Include readResolve() if "implements Serializable" is presnet. * But this should be very rare. */ private Object readResolve() { return singleton; } } |
C++ | class ClassName { public: static ClassName& getInstance(); private: ClassName(); ... other constructors ... ClassName( const ClassName& rhs ); }; ClassName& ClassName::getInstance() { static ClassName theSingleton( arguments ); return theSingleton } |
小结
Singleton模式是一个比较简单的模式,但是在具体实现时,应该注意多线程环境和分布环境带来的挑战。