站内搜索: 请输入搜索关键词

当前页面: 开发资料首页Java 专题装饰模式(Decorator Pattern)

装饰模式(Decorator Pattern)

摘要: 装饰模式(Decorator Pattern)

</td> </tr> <tr> <td height="35" valign="top" class="ArticleTeitle"> <table width="100%" border="0" cellspacing="0" cellpadding="0"> <tr> <td width="282" height="86" align="center" valign="top"> </td> <td width="402" valign="top">

装饰模式是在不必改变原类文件和使用继承的情况下,动态的扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。

装饰模式的特点;


(1) 装饰对象和真实对象有相同的接口。这样客户端对象就可以以和真实对象相同的方式和装饰对象交互。
(2) 装饰对象包含一个真实对象的索引(reference)
(3) 装饰对象接受所有的来自客户端的请求。它把这些请求转发给真实的对象。
(4) 装饰对象可以在转发这些请求以前或以后增加一些附加功能。这样就确保了在运行时,不用修改给定对象的结构就可以在外部增加附加的功能。在面向对象的设计中,通常是通过继承来实现对给定类的功能扩展。下表格列举了装饰模式和继承的不同: </td></tr> </table>

装饰模式 VS 继承



例子:


让我们重新返回我们在工厂方法和单例模式log实用工具上,我们的模式主要由Logger 接口和两个它的实现类:FileLogger和ConsoleLogger组成,分别把信息输出到一个文件和屏幕中。另外,还有包括工厂方法的LoggerFactory 类。
LoggerFactory没有出现在下图中,主要是因为它和现在讨论的例子没有直接联系。

Figure 19.1: Logging Utility 类层次

让我们想象一些客户端需要以超出Logger Utility现在所提供的新的方式输出信息,客户端需要下面两种特征;
(1) 把输出的信息传唤为HTML文档
(2) 对输出信息进行简单加密,

在面向对象的设计中,不改变现存的类的代码,可以应用继承来增加新的功能。例如,子类化现在的类重载它的方法来增加所需要的新功能。
应用继承,我们要子类化FileLogger和ConsoleLogger类来增加新的功能,会有下面的一组新的子类:

子类 父类 功能
HTMLFileLogger FileLogger 转化输出信息为HTML文档,并存入一个Log文件
HTMLConsLogger ConsoleLogger 转化输出信息为HTML文档,并显示在屏幕上
EncFileLogger FileLogger 加密输出信息,并存入一个Log文件
EncConsLogger ConsoleLogger 加密输出信息,并显示在屏幕上

从类图可以看到,为了实现新的功能加入了一组新的子类。如果我们还有其他的Logger类型(例如:DBLogger输出信息到数据库中),这样会有更多子类。当一个新的特性需要被加入,子类的数量会有成倍数的增长,同时我们会有一个庞大的类层次。


Figure 19.2: The Resulting Class Hierarchy after Applying Inheritance to Add the New Functionality

装饰模式使我们从这种情景中解脱出来,装饰模式推荐通过对象的合成而不是继承来包装一个对象扩展它的功能。
应用装饰模式,让我们为Logger Utility定义一个有下列特征的默认根装饰类LoggerDecorator:
(1) LoggerDecorator包括一个Logger实例的引用。这个引用指向它包含的Logger对象。
(2) LoggerDecorator实现Logger接口、提供Log方法的基本的默认实现,他只是简单的 转发调用给它包含的Logger 对象。每一个LoggerDecorator子类保证定义log方法。
Listing 19.1: LoggerDecorator Class
  1. public class LoggerDecorator implements Logger {
  2. Logger logger;
  3. public LoggerDecorator(Logger inp_logger) {
  4. logger = inp_logger;
  5. }
  6. public void log(String DataLine) {
  7. /*
  8. Default implementation
  9. to be overriden by subclasses.
  10. */
  11. logger.log(DataLine);
  12. }
  13. }//end of class
????
每一个logger的装饰定义log方法是很重要的,因为装饰对象必须提供和它包装的对象相同的接口。 当客户端创建一个装饰类的实例,客户端以与装饰类交互方式和客户端与拥有相同接口原对象的交互方式是一致的。
让我们定义LoggerDecorator的两个子类,HTMLLogger和EncryptLogger。
,

Figure 19.3: The Decorator Class Structure for the Logging Utility to Add the New Functionality

具体的Logger 装饰类


HTMLLogger

HTMLLogger重载了log方法的默认实现。在log方法中,装饰类把输出信息转化为HTML文档,并且发送给可以输出的Logger实例。
Listing 19.2: HTMLLogger Class
  1. public class HTMLLogger extends LoggerDecorator {
  2. public HTMLLogger(Logger inp_logger) {
  3. super(inp_logger);
  4. }
  5. public void log(String DataLine) {
  6. /*
  7. Added functionality
  8. */
  9. DataLine = makeHTML(DataLine);
  10. /*
  11. Now forward the encrypted text to the FileLogger
  12. for storage
  13. */
  14. logger.log(DataLine);
  15. }
  16. public String makeHTML(String DataLine) {
  17. /*
  18. Make it into an HTML document.
  19. */
  20. DataLine = "<body>" + "" + DataLine +
  21. "" + "</body>";
  22. return DataLine;
  23. }
  24. }//end of class

EncryptLogger

与HTMLLogger相似,EncryptLogger重载了log方法,在log方法中,EncryptLogger通过简单的将字符位置向右转移一位实现了加密逻辑,并且发送给可以输出的Logger实例。

Listing 19.3: EncryptLogger Class
  1. public class EncryptLogger extends LoggerDecorator {
  2. public EncryptLogger(Logger inp_logger) {
  3. super(inp_logger);
  4. }
  5. public void log(String DataLine) {
  6. /*
  7. Added functionality
  8. */
  9. DataLine = encrypt(DataLine);
  10. /*
  11. Now forward the encrypted text to the FileLogger
  12. for storage
  13. */
  14. logger.log(DataLine);
  15. }
  16. public String encrypt(String DataLine) {
  17. /*
  18. Apply simple encryption by Transposition…
  19. Shift all characters by one position.
  20. */
  21. DataLine = DataLine.substring(DataLine.length() ? 1) +
  22. DataLine.substring(0, DataLine.length() ? 1);
  23. return DataLine;
  24. }
  25. }//end of class

当应用装饰模式的类图:


Figure 19.4: Association between Different Logger Classes and Logger Decorators

为了使用新设计装饰对象,客户端需要


(1) 使用LoggerFactory工厂方法创建一个合适的Logger实例(FileLogger/ConsoleLogger)。
(2) 把第一步中创建的Logger实例作为参数转递给新创建的合适的LoggerDecorator实例的构造函数。
(3) 调用LoggerDecorator实例上的方法,
Listing 19.4: Client DecoratorClient Class
  1. class DecoratorClient {
  2. public static void main(String[] args) {
  3. LoggerFactory factory = new LoggerFactory();
  4. Logger logger = factory.getLogger();
  5. HTMLLogger hLogger = new HTMLLogger(logger);
  6. //the decorator object provides the same interface.
  7. hLogger.log("A Message to Log");
  8. EncryptLogger eLogger = new EncryptLogger(logger);
  9. eLogger.log("A Message to Log");
  10. }
  11. }//End of class

增加新的信息输出类型


在Logging Utility实例中, 应用装饰模式对比使用继承不会因为类层次的增长而导致大量的子类,我们还可以有另外的Logger类型:DBLogger,它输出信息到数据库中。为了将信息转化为HTML格式或在输出到数据库以前对信息进行加密,客户端只需遵从上面提到的步骤,因为DBLogger是一种Logger类型,它可以作为构造函数的参数传递给HTMLLogger或EncryptLogger中任何一个类。

增加新的装饰


从例子中可以看到,LoggerDecorator实例包含了 一个Logger类型了对象实例,在转发请求给Logger对象实例以前或以后,增加新的功能。因为LoggerDecorator类实现了Logger 接口,LoggerDecorator实例或它的任何一个子类都可以作为一个Logger类型。因此LoggerDecortator包含它的任何子类的 一个实例,并且将请求转发给它。一般的一个装饰对象可以包含另一个装饰对象,并且可以向它转发请求。通过这种方式,新的装饰类,新的功能可以通过</td> </tr> <tr>


↑返回目录
前一篇: LOG4J快速入门及参考资料
后一篇: JDK1.5枚举类型