裝飾者模式是結構型設計模式之一。
何時使用?
當需要動态地為對象添加和移除行為時,在運作時,裝飾者模式是擴充類的替代方案,選擇了組合而不是繼承。它通常也被稱為Wrapper(包裝器),因為它的名字更能說明它實際上的功能。
從類圖中可以看出,有一個Component類,它是預設的行為實作,但也有DecoratorA和DecoratorB,它們都實作了Decorator并在此基礎上進行了擴充。這些都是額外的行為類,通常不獨立工作,需要額外的Decorator,像添加日志、緩存、壓縮等。
一個例子:
如編寫一個處理伺服器請求和響應的通用類。你想要在Debug建構中添加日志記錄行為,并根據請求添加緩存行為
在例子中,ResponseReader将是Component,這意味着它将能夠讀取響應,而CacheDecorator和LoggerDecorator不能獨立工作,是以它們需要聚合在Reader上。
編寫Reader以及Request和Response:
data class Request(val data: String)
data class Response(val body: String)
interface Reader {
fun read(request: Request): Response
}
建立一個ResponseReader,處理請求和響應。
class ResponseReader : Reader {
override fun read(request: Request): Response {
// TODO 根據請求擷取真實響應
return Response("成功")
}
}
因為它是一個必備的類,将被每個Reader使用,添加Cache和Logger:
class CacheReader(private val reader: Reader) : Reader {
private val cache = mutableMapOf<Request, Response>()
override fun read(request: Request): Response {
val response = cache[request]
return response ?: run {
val readerResponse = reader.read(request)
cache[request] = readerResponse
readerResponse
}
}
}
class LoggerReader(private val reader: Reader) : Reader {
override fun read(request: Request): Response {
val response = reader.read(request)
// TODO 在真實項目中使用某種日志記錄工具而不是println
println("請求: $request || 響應: $response")
return response
}
}
另一方面,這兩個類不能獨立工作。它們需要下一個Reader才能正确工作。是以它們在構造函數中被添加。如果Reader可以獨自處理,但在特殊情況下需要一些功能,可能把Reader設定為可空的。
實際應用中如何使用?
fun main() {
val responseReader: Reader = ResponseReader()
val logReader: Reader = LoggerReader(responseReader)
val cacheReader: Reader = CacheReader(logReader)
val request = Request("示例")
responseReader.read(request) // 沒有列印任何内容
logReader.read(request) // 請求: Request(data=示例) || 響應: Response(body=成功)
// cacheReader也将記錄資料,因為LoggerReader是在其構造函數中傳入的
cacheReader.read(request) // 請求: Request(data=示例) || 響應: Response(body=成功)
// 所有傳回的響應都是相同的
}
正如你所見,在項目中添加或移除行為是非常靈活的方式。
- 優點
- 組合多種行為
- 組合優于繼承
- 在運作時添加和移除行為
- 缺點
- 很難編寫一個與 Stack 中執行個體的順序無關的裝飾器。
- 初始化代碼可能看起來不太美觀。