您可於此 design_pattern repo 下載 Design Pattern 系列程式碼。
需求
在設計一個 IoT App 整合多品牌 IPCam 的功能 時,我們需要滿足以下需求:
- 支援多種 IPCam 品牌,這些品牌的 IPCam 提供不同的串流與截圖方式:
- HIKVISION 提供 RTSP 協定,可以用通用方式播放串流與截圖。
- DAHUA 提供自家 SDK,需要依賴 SDK 提供的方法進行操作。
- App 的程式碼結構不應依賴 IPCam 品牌的實現細節,應保持開放擴展性,方便後續新增新的 IPCam 品牌。
- 避免修改 IPCam 的核心結構,因為這些品牌的實現通常由廠商提供,無法直接修改。
物件導向分析 (OOA)
理解需求後,讓我們來快速實作物件導向分析吧!
察覺 Forces
如果未套用設計模式,我們可能會遇到以下問題:
- 難以擴展新品牌
- 每新增一個品牌的 IPCam,就需要修改 App 的核心邏輯。
- 違反開放關閉原則 (OCP)
- 核心邏輯與品牌實現細節耦合,新增功能需要修改核心程式碼。
- 無法統一處理不同品牌的操作
- 每個品牌的串流與截圖方式不同,導致程式碼混亂,難以維護。
套用 Visitor Pattern (Solution) 得到新的 Context (Resulting Context)
做完 OOA,察覺 Forces,看清楚整個 Context 後,就可以來套用 Visitor Pattern 解決這個問題。
先來看一下 Visitor Pattern 的 UML
Visitor Pattern 的組件
訪問者模式的核心組件包括:
-
Visitor (訪問者介面)
- 定義對每種類型物件的操作方法。
-
ConcreteVisitor (具體訪問者)
- 實現特定操作邏輯。
-
Element (元素介面)
- 定義接受訪問者的方法 (
accept
),並將訪問者傳遞給自己。
- 定義接受訪問者的方法 (
-
ConcreteElement (具體元素)
- 實現接受訪問者的方法,讓訪問者能夠訪問並操作具體元素。
將 Visitor Pattern 套用到我們的應用吧
物件導向設計 (OOP)
[Element: IPCam]
interface IPCam {
fun accept(visitor: IPCamVisitor)
}
[ConcreteElements: HikvisionIPCam, DahuaIPCam]
class HikvisionIPCam : IPCam {
override fun accept(visitor: IPCamVisitor) {
visitor.visitHikvision(this)
}
fun getRTSPStream(): String {
return "rtsp://hikvision/stream"
}
fun captureSnapshot(): String {
return "Hikvision Snapshot"
}
}
class DahuaIPCam : IPCam {
override fun accept(visitor: IPCamVisitor) {
visitor.visitDahua(this)
}
fun startSDKStream(): String {
return "Dahua SDK Stream"
}
fun takeSDKSnapshot(): String {
return "Dahua Snapshot"
}
}
[Visitor: IPCamVisitor]
interface IPCamVisitor {
fun visitHikvision(ipCam: HikvisionIPCam)
fun visitDahua(ipCam: DahuaIPCam)
}
[ConcreteVisitors: IPCamStreamingVisitor, IPCamSnapshotVisitor]
class IPCamStreamingVisitor : IPCamVisitor {
override fun visitHikvision(ipCam: HikvisionIPCam) {
println("Streaming: ${ipCam.getRTSPStream()}")
}
override fun visitDahua(ipCam: DahuaIPCam) {
println("Streaming: ${ipCam.startSDKStream()}")
}
}
class IPCamSnapshotVisitor : IPCamVisitor {
override fun visitHikvision(ipCam: HikvisionIPCam) {
println("Snapshot: ${ipCam.captureSnapshot()}")
}
override fun visitDahua(ipCam: DahuaIPCam) {
println("Snapshot: ${ipCam.takeSDKSnapshot()}")
}
}
[Client]
fun main() {
val ipCams: List<IPCam> = listOf(HikvisionIPCam(), DahuaIPCam())
val streamingVisitor = IPCamStreamingVisitor()
val snapshotVisitor = IPCamSnapshotVisitor()
for (ipCam in ipCams) {
ipCam.accept(streamingVisitor)
ipCam.accept(snapshotVisitor)
}
}
[Output]
Streaming: rtsp://hikvision/stream
Snapshot: Hikvision Snapshot
Streaming: Dahua SDK Stream
Snapshot: Dahua Snapshot
結論
透過 Visitor Pattern,我們成功將不同品牌 IPCam 的操作邏輯與物件結構分離,並實現以下優勢:
-
易於擴展新品牌
- 新增品牌只需實作新的
ConcreteElement
類別,並在訪問者中新增相應的操作方法。
- 新增品牌只需實作新的
-
操作邏輯集中
- 不同品牌的操作邏輯集中於訪問者中,便於維護與管理。
-
符合設計原則
- 單一職責原則 (SRP):操作邏輯與物件結構分離。
- 開放關閉原則 (OCP):允許新增功能而不修改既有程式碼。
訪問者模式非常適合處理以下場景:
- 多種類型物件需要執行不同操作。
- 物件結構穩定,但操作邏輯經常變化。
訪問者模式為多變操作提供了一個優雅的解決方案,確保系統具有高擴展性與靈活性。
Leave a comment