天天看點

Decorator裝飾者模式講解

作者:不秃頭程式員
Decorator裝飾者模式講解

裝飾者模式是結構型設計模式之一。

何時使用?

當需要動态地為對象添加和移除行為時,在運作時,裝飾者模式是擴充類的替代方案,選擇了組合而不是繼承。它通常也被稱為Wrapper(包裝器),因為它的名字更能說明它實際上的功能。

Decorator裝飾者模式講解

從類圖中可以看出,有一個Component類,它是預設的行為實作,但也有DecoratorA和DecoratorB,它們都實作了Decorator并在此基礎上進行了擴充。這些都是額外的行為類,通常不獨立工作,需要額外的Decorator,像添加日志、緩存、壓縮等。

一個例子:

如編寫一個處理伺服器請求和響應的通用類。你想要在Debug建構中添加日志記錄行為,并根據請求添加緩存行為

Decorator裝飾者模式講解

在例子中,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 中執行個體的順序無關的裝飾器。
    • 初始化代碼可能看起來不太美觀。

繼續閱讀