|
|
|
| 您的位置:首页>>设计模式>>桥模式(Bridgenbsp;Pattern) |
|
|
桥模式(Bridgenbsp;Pattern)
|
| 2007-04-13 来源:www.javaresearch.org 作者:未知 |
第24章 桥模式(Bridge Pattern)
描述: 桥模式提倡抽象的接口和它的实现分离。通常,抽象是指识别具有特殊用途的对象的一组属性和行为的过程。设计对象的这个特殊视图是为了把对象与那些不相干的属性和行为分离。这样得到的对象就是一个抽象。注意一个给定的对象因不同的用处可以有多个相关的抽象。 对于一个给定的抽象的方法(行为)可以有一个或者多个实现。根据实现,一个抽象可以设计为一个有一个或者多个实现的接口。如图24.1 Figure 24.1: Abstraction as an Interface with a Set of Concrete Implementers 在图24.1的类层次图中,抽象接口声明了一组代表从不同对象中抽取的共同特征的结果。Implementer_1和Implementer_2代表了抽象的一组实现。这种方法有下面的局限性。 (1) 由于这些原因当需要 (2) 抽象接口和它的实现之间紧密关联,因此它们不能在不影响彼此的情况下独子变化。 使用桥模式是对抽象的高效、便于管理设计。应用桥模式设计抽象可以把它的接口和它的实现相互分离,并且把抽象的接口和它的实现分别置于分离的类层次结构中。如图24.2。
Figure 24.2: Interface and Implementations in Two Separate Class Hierarchies 从24.2的类图中可以看到抽象维护了一个Implementer类型的对象引用。客户应用程序可以随意的从Abstraction类层次中选择需要的抽象类型。抽象对象可以由来自Implementer类层次中的适当实现的实例来构成。在不需要继承而扩展功能的情况下,这种动态的合并抽象接口和它的实现是很有实用的。当客户对象调用Abstraction对象上的方法时,它把这个调用转发给它所包含的Implementer对象。Abstraction对象在转发调用给Implementer对象以前可以提供很多处理。 这种类层次安排使接口和抽象的实现完全没有联系,这使得接口和它的实现类层次可以在彼此互不影响的情况下发生变化。
例子: 在讨论工厂方法模式的时候,我们为应用程序设计了消息日志功能。记录日志在应用程序的不同阶段有不同的目的,因此,组成应用程序的不同对象可以需要有出力日志消息的能力。因为在应用程序中,不同的对象需要出力日志消息,可以把日志消息功能放在一个单独的类中,这个类抽象了日志消息出力的功能。从现在开始,我们使用日志抽象来指日消息出力的功能。 信息可以被出力到不同的目标中,如文件、控制台以及其他介质上。由于目标类型的不同,需要不同的日志出力抽象的不同实现。这种需求可以设计一个共同的Logger接口,这个接口声明抽象的方法和为实现出力到不同目标类型的出力抽象的实现。让我们定义两个实现??FileLogger和ConsoleLogger??出力信息分别到一个文件和控制台上。如图24.3描述的结果类层次图。 Figure 24.3: Logger Abstraction before Applying the Bridge Pattern
不同的客户对象可以使用其中的一个实现对象(FileLogger或ConsoleLogger)以文本的方式出力信息到需要的目标。这个设计实现以后,让我们假设一个应用程序对象需要以不同的方式出力信息(例如,加密方式)。现在的信息出力功能如果不进行如下的修改是不能满足需求的: (1)修改不同的实现。 (2)扩展整个的类层次。 为了扩展功能而不得不修改现在的代码是不可取的并且违背了面向对象的开放??封闭原则(open-closed principle)。 开放??封闭原则(open-closed principle)声明在软件模块中应该: (1)开放扩展??改变模块行为或者增加模块功能的新特性是可能的。 (2)封闭修改??这样的模块不允许自己代码的修改。 在开放??封闭原则的指导下,设计软件模块需要在不修改现存代码的前提下,扩展它的功能。 这也就意味着为了不同接口类型,任何时候对Logger(JAVA)接口的改变,它的每一个实现都需要修改,这使得 出力抽象和它的实现相互依赖。 为每一种不同类型的信息出力方式而继承是不被推荐的,因为会导致大量的子类产生,很快会有一个庞大的类层次。 桥模式可以应用在这种情形上,提供增加新的信息形式和新的出力抽象的实现类型的能力。 桥模式把接口和实现分离到两个类层次中,以至于他们修改时互不影响。 应用桥模式出力信息抽象的接口和实现被安排到两个类层次结构中。 抽象实现设计(Abstraction Implementation Design) 出力抽象的实现者需要提供把信息出力到不同目标类型的真实实现。让我们定义两个 这样的实现者??FileLogger和ConsoleLogger??出力信息到一个文件和控制台。这些 抽象的实现者可以被设计成两个实现了共同接口MessageLogger的实现者。 Listing 24.1: MessageLogger Interface
- public interface MessageLogger {
- public void logMsg(String msg);
- }
共同接口MessageLogger声明了一个方法logmsg(String msg),这个方法代表了出力抽象的接口的对象所使用。图24.4描述了抽象实现的类层次
Figure 24.4: Logger Abstraction?Implementer Hierarchy 作为logMsg方法实现的一部分(List 24.2) Listing 24.2: MessageLogger Implementers?FileLogger and ConsoleLogger
- public class FileLogger implements MessageLogger {
- public void logMsg(String msg) {
- FileUtil futil = new FileUtil();
- futil.writeToFile("log.txt",msg, true, true);
- }
- }
- public class ConsoleLogger implements MessageLogger {
- public void logMsg(String msg) {
- System.out.println(msg);
- }
- }
(1)FileLogger使用帮助类FileUtil类把指定的信息写到一个log文件中。 (2)ConsoleLogger把指定的信息写到屏幕上。 注意客户对象不应该直接访问由不同logger抽象实现者提供的出力信息的服务。为了出力信息,不同的客户对象与代表logger抽象接口的实例交互。这些抽象接口对象反过来在使用抽象实现者类提供的服务。 抽象接口的设计: Logger抽象的接口可以设计成一组代表客户对象需要出力的不同信息类型的类的形式。这些类可以设计成共同接口Message的实现者。 Listing 24.3: Message Interface
- public interface Message {
- public void log(String msg);
- }
Message接口声明一个log(String msg)方法,这个方法被不同的客户对象使用出力信息。 让我们定义两个logger抽象接口类??TextMessage和EncryptedMessage??(Listing 24.4)分别表示纯文本信息和加密信息。这些抽象接口类可以设计为共同接口Message的具体实现类。 Listing 24.4: Message Implementers?TextMessage and EncryptedMessage
- public class TextMessage implements Message {
- private MessageLogger logger;
- public TextMessage(MessageLogger l) {
- logger = l;
- }
- public void log(String msg) {
- String str = preProcess(msg);
- logger.logMsg(str);
- }
- private String preProcess(String msg) {
- return msg;
- };
- }
- public class EncryptedMessage implements Message {
- private MessageLogger logger;
- public EncryptedMessage(MessageLogger l) {
- logger = l;
- }
- public void log(String msg) {
- String str = preProcess(msg);
- logger.logMsg(str);
- }
- private String preProcess(String msg) {
- msg = msg.substring(msg.length() ? 1) +
- msg.substring(0, msg.length() ? 1);
- return msg;
- };
- }
Figure 24.5 shows the resulting logger abstraction interface class hierarchy. Figure 24.5: Logger Abstraction?Interface Hierarchy 在设计抽象接口类中注意的事项: (1) logger抽象接口类??TextMessage和EncryptedMessage不能提供真正的信息出力服务的实现,前面介绍的抽象实现者类如:FileLogger和ConsoleLogger才提供真正的消息出力的实现。 (2) 客户对象不直接使用抽象实现者类暴漏的接口。 (3) 每一个抽象接口类维护一个MessageLogger(抽象实现)类型对象的引用。任何时候客户对象创建一个抽象接口对象,它都可以通过MessageLogger配置这个接口对象。 (4) 客户对象对抽象接口对象上的log方法的任何调用,接口对象需要一些前期的处理,然后在使用它所包含MessageLogger对象的出力消息的服务。 (5) 前期的处理功能仅在抽象接口对象内部使用,而对于客户对象是无效的。为了确保这点,在TextMessage和EncryptedMessage抽象类中的preProcess方法都被设计为私有的方法。作为preProcess方法实现的一部分,EncryptedMessage通过把信息的所有字符向右移动了一位作为加密方法。 把logger抽象的接口和它的实现放在分离的类层次上,以至于logger抽象的接口和它的实现完全没有耦合。 当客户需要出力信息时(Listing 24.5) Listing 24.5: Test Client Class
- public class Client {
- public static void main(String[] args) {
- //Create an appropriate implementer object
- MessageLogger logger = new FileLogger();
- //Choose required interface object and
- //configure it with the implementer object
- Message msg = new EncryptedMessage(logger);
- msg.log("Test Message");
- }
- }
(1) 它创建合适的MessageLogger实现者类的一个实例,例如:FileLogger或ConsoleLogger. (2) 它创建需要的Message实现者类的一个实例,例如:TextMessage或EncryptedMessage。 (3) 通过在(1)步中创建的MessageLogger实现者类对象,它配置Message实现者对象。这个对象在内部维护一个Message实现者对象。 (4) 它调用在(2)步中创建的Message实现者对向上的log(String)方法。 (5) Message实现者对象执行需要的处理,把输入信息转化为需要的形式(在EncryptedMessage中,加密输入信息)然后把转化的信息传递给MessageLogger实现者对象并调用logMsg(String)方法。这种接口和它的实现之间类层次关系可以看作为桥。 图24.6出示了全部的类的关联关系。 Figure 24.6: Logger Abstraction after Applying the Bridge Pattern 应用程序对象使用logger抽象出力加密的信息时,信息流图如24.7所描述:
Figure 24.7: Message Flow When an Application Logs an Encrypted Message logger抽象接口和它的实现分离使得它们彼此独立的进行修改。 设计实现以后,如果客户对象需要以新的形式如HTML出力信息,这可以通过设计一个实现了Message接口的新的logger抽象接口类HTMLMessage来达到。HTMLMessage类需要转化信息为HTML文本,然后使用抽象实现者类出力转化的信息。这个附加的新的接口类不会影响现在抽象的实现。另外,为每一个新的出力信息类型增加新的类保证了类的线性增长。 同样,增加一个新的logger抽象实现例如DBLogger把信息出力到数据库中,不需要修改或者继承类层次。 桥模式和适配器模式
相似点: 桥模式和适配器模式是相似的,因为它们都向客户隐藏了底层的具体实现。
不同点: (1)适配器模式的目标是使因为接口不兼容不能交互的类可以联系在一起。 一个适配器可以改变现存对象的接口。在我们讨论适配器模式时已经看到,一个适配器 需要一个适配者(现存),这显示了适配器模式在系统设计初期很实用。 (2)桥模式更偏重于是一个设计期模式,在设计者想控制系统的类时可以使用。 它适用于系统已经被实现以前,可以让抽象接口和它的实现在互不影响的情况下可以独立的 改变。 (3)在桥模式中,不存在互不兼容的接口。客户对象总是使用抽象接口类所暴楼的 接口。因此桥模式和适配器模式适用于解决不同的设计问题。
附件为原文和代码: 附件:Chapter_24.doc(233K) 附件:28.rar(7K)
|
|
|
| |