7.3 装饰器
装饰器设计模式(也被称为“包装器”)是一种鼓励API跨层重复的模式。装饰器对象采用一个现有的对象并扩展其功能;它提供与底层对象相似或相同的API,其方法调用底层对象的方法。在第四章的Java I/O例子中,BufferedInputStream
类是一个装饰器:给定一个InputStream
对象,它提供相同的API,但引入了缓冲功能。例如,当它的read
方法被调用来读取一个字符时,它会在底层的InputStream
上调用read
来读取一个更大的块,并保存额外的字符以满足未来的read
调用。另一个例子发生在窗口系统中:一个Window
类实现了一种简单的不可滚动的窗口形式,而一个ScrollableWindow
类通过添加水平和垂直滚动条装饰了Window
类。
装饰器的动机是将类的特殊用途的扩展与一个更通用的核心分开。然而,装饰器类往往是浅的:它们为少量的新功能引入大量的样板。装饰器类通常包含许多传递式方法。很容易过度使用装饰器模式,为每个小的新功能创建一个新的类。这就导致了浅类的爆炸,例如Java I/O的例子。
在创建装饰器类之前,请考虑如下的替代方案:
你能否将新的功能直接添加到底层类中,而不是创建一个装饰器类?如果新功能是相对通用的,或者如果它在逻辑上与底层类相关,或者底层类的大多数用途也将使用新功能,那么这是有道理的。例如,几乎所有创建Java
InputStream
的人也会创建一个BufferedInputStream
,而缓冲是 I/O 的自然组成部分,所以这些类应该被合并。如果新功能是专门针对某个特定用例的,那么将其与用例合并,而不是创建一个单独的类,这样做是否有意义?
你能否将新功能与现有的装饰器合并,而不是创建一个新的装饰器?这将导致一个更深的装饰器类,而不是多个浅的装饰器类。
最后,问问自己,新的功能是否真的需要包装现有的功能:你能否将其实现为不依赖于基类的独立类?在窗口的例子中,滚动条可能能够与主窗口分开实现,而无需包装其所有现有功能。
有时装饰器是有意义的,但通常有更好的选择。
Last updated