在軟件開發中,我們經常會遇到需要與複雜子系統交互的場景。外觀模式通過提供一個統一的簡化接口,隱藏系統的複雜性,讓客户端能夠更輕鬆地使用系統功能。
什麼是外觀模式?
外觀模式(Facade Pattern)是一種結構型設計模式,它為複雜的子系統提供一個統一的簡化接口。這個模式遵循"最少知識原則",讓客户端只需要與一個高層接口交互,而不需要了解底層系統的複雜性。
就像現實世界中的酒店前台:客人不需要直接與客房服務、餐廳、保潔等多個部門打交道,只需要通過前台就能獲得所有服務。
核心概念
- 外觀(Facade):提供簡化的統一接口
- 子系統(Subsystem):由多個相互關聯的類組成的複雜系統
- 客户端(Client):通過外觀接口與系統交互
實戰案例:智能家居控制系統
讓我們通過一個完整的智能家居系統來理解外觀模式的實際應用。
1. 定義子系統組件
// 燈光子系統
class LightingSystem {
private val lights = mutableMapOf(
"living_room" to false,
"bedroom" to false,
"kitchen" to false
)
fun turnOn(light: String) {
lights[light] = true
println("💡 $light 燈光已打開")
}
fun turnOff(light: String) {
lights[light] = false
println("💡 $light 燈光已關閉")
}
fun dim(light: String, level: Int) {
println("💡 $light 燈光調至 $level%")
}
fun getStatus(): Map<String, Boolean> {
return lights.toMap()
}
}
// 空調子系統
class AirConditioningSystem {
private var isOn = false
private var temperature = 26.0
private var mode = "cool" // cool, heat, fan
fun turnOn() {
isOn = true
println("❄️ 空調已開啓,温度: ${temperature}℃,模式: $mode")
}
fun turnOff() {
isOn = false
println("❄️ 空調已關閉")
}
fun setTemperature(temp: Double) {
temperature = temp
if (isOn) {
println("❄️ 空調温度設置為 ${temp}℃")
}
}
fun setMode(newMode: String) {
mode = newMode
if (isOn) {
println("❄️ 空調模式設置為 $newMode")
}
}
fun getStatus(): String {
return if (isOn) "運行中 - ${temperature}℃ $mode" else "關閉"
}
}
// 娛樂子系統
class EntertainmentSystem {
private var tvOn = false
private var soundSystemOn = false
private var currentSource = "TV"
private var volume = 50
fun turnOnTV() {
tvOn = true
println("📺 電視已開啓")
}
fun turnOffTV() {
tvOn = false
println("📺 電視已關閉")
}
fun turnOnSoundSystem() {
soundSystemOn = true
println("🔊 音響系統已開啓")
}
fun turnOffSoundSystem() {
soundSystemOn = false
println("🔊 音響系統已關閉")
}
fun setSource(source: String) {
currentSource = source
println("🔊 信號源切換到 $source")
}
fun setVolume(level: Int) {
volume = level.coerceIn(0, 100)
println("🔊 音量設置為 $volume")
}
fun getStatus(): String {
return "TV: ${if (tvOn) "開" else "關"}, 音響: ${if (soundSystemOn) "開" else "關"}"
}
}
// 安防子系統
class SecuritySystem {
private var alarmArmed = false
private var doorsLocked = false
private var camerasOn = false
fun armAlarm() {
alarmArmed = true
println("🚨 報警系統已佈防")
}
fun disarmAlarm() {
alarmArmed = false
println("🚨 報警系統已撤防")
}
fun lockDoors() {
doorsLocked = true
println("🔒 所有門已上鎖")
}
fun unlockDoors() {
doorsLocked = false
println("🔒 所有門已解鎖")
}
fun startSurveillance() {
camerasOn = true
println("📹 監控系統已啓動")
}
fun stopSurveillance() {
camerasOn = false
println("📹 監控系統已停止")
}
fun getStatus(): String {
return "報警: ${if (alarmArmed) "佈防" else "撤防"}, 門鎖: ${if (doorsLocked) "鎖定" else "未鎖"}, 監控: ${if (camerasOn) "開啓" else "關閉"}"
}
}
// 窗簾子系統
class CurtainSystem {
private val curtains = mutableMapOf(
"living_room" to 0, // 0-100 表示打開百分比
"bedroom" to 0
)
fun open(curtain: String) {
curtains[curtain] = 100
println("🪟 $curtain 窗簾已完全打開")
}
fun close(curtain: String) {
curtains[curtain] = 0
println("🪟 $curtain 窗簾已完全關閉")
}
fun setOpenPercentage(curtain: String, percentage: Int) {
curtains[curtain] = percentage.coerceIn(0, 100)
println("🪟 $curtain 窗簾打開 ${percentage}%")
}
fun getStatus(): Map<String, Int> {
return curtains.toMap()
}
}
2. 創建智能家居外觀類
// 智能家居外觀類 - 統一入口
class SmartHomeFacade(
private val lighting: LightingSystem = LightingSystem(),
private val ac: AirConditioningSystem = AirConditioningSystem(),
private val entertainment: EntertainmentSystem = EntertainmentSystem(),
private val security: SecuritySystem = SecuritySystem(),
private val curtains: CurtainSystem = CurtainSystem()
) {
// 場景模式:離家模式
fun leaveHome() {
println("\n🏠 啓動離家模式...")
// 關閉所有燈光
lighting.getStatus().keys.forEach { light ->
if (lighting.getStatus()[light] == true) {
lighting.turnOff(light)
}
}
// 關閉空調
ac.turnOff()
// 關閉娛樂系統
entertainment.turnOffTV()
entertainment.turnOffSoundSystem()
// 關閉窗簾
curtains.getStatus().keys.forEach { curtain ->
curtains.close(curtain)
}
// 啓動安防系統
security.lockDoors()
security.armAlarm()
security.startSurveillance()
println("✅ 離家模式設置完成")
}
// 場景模式:回家模式
fun arriveHome() {
println("\n🏠 啓動回家模式...")
// 撤防安防系統
security.disarmAlarm()
security.unlockDoors()
// 打開入口燈光
lighting.turnOn("living_room")
// 打開窗簾
curtains.open("living_room")
// 設置舒適温度
ac.setTemperature(24.0)
ac.turnOn()
// 背景音樂
entertainment.turnOnSoundSystem()
entertainment.setSource("Music")
entertainment.setVolume(30)
println("✅ 回家模式設置完成")
}
// 場景模式:影院模式
fun startMovieNight() {
println("\n🎬 啓動影院模式...")
// 調暗燈光
lighting.getStatus().keys.forEach { light ->
lighting.dim(light, 20)
}
// 關閉窗簾
curtains.getStatus().keys.forEach { curtain ->
curtains.close(curtain)
}
// 開啓娛樂系統
entertainment.turnOnTV()
entertainment.turnOnSoundSystem()
entertainment.setSource("TV")
entertainment.setVolume(60)
// 調整空調温度
ac.setTemperature(22.0)
println("✅ 影院模式設置完成")
}
// 場景模式:睡眠模式
fun startSleepMode() {
println("\n😴 啓動睡眠模式...")
// 關閉所有燈光
lighting.getStatus().keys.forEach { light ->
lighting.turnOff(light)
}
// 關閉娛樂系統
entertainment.turnOffTV()
entertainment.turnOffSoundSystem()
// 關閉客廳窗簾,打開卧室窗簾50%
curtains.close("living_room")
curtains.setOpenPercentage("bedroom", 50)
// 設置睡眠温度
ac.setTemperature(26.0)
ac.setMode("fan")
// 啓動夜間安防
security.armAlarm()
security.startSurveillance()
println("✅ 睡眠模式設置完成")
}
// 單獨控制方法
fun controlLighting(light: String, action: String, level: Int? = null) {
when (action) {
"on" -> lighting.turnOn(light)
"off" -> lighting.turnOff(light)
"dim" -> level?.let { lighting.dim(light, it) }
}
}
fun controlTemperature(temp: Double) {
ac.setTemperature(temp)
if (!ac.getStatus().contains("運行中")) {
ac.turnOn()
}
}
// 獲取整體狀態
fun getHomeStatus(): Map<String, Any> {
return mapOf(
"lighting" to lighting.getStatus(),
"ac" to ac.getStatus(),
"entertainment" to entertainment.getStatus(),
"security" to security.getStatus(),
"curtains" to curtains.getStatus()
)
}
// 節能模式
fun enableEnergySaving() {
println("\n🌱 啓動節能模式...")
// 關閉不必要的設備
lighting.getStatus().forEach { (light, isOn) ->
if (isOn && light != "living_room") {
lighting.turnOff(light)
}
}
// 調整空調設置
ac.setTemperature(28.0)
curtains.close("living_room")
println("✅ 節能模式設置完成")
}
}
3. 客户端使用
// 移動APP界面
class SmartHomeApp(private val smartHome: SmartHomeFacade) {
fun demonstrateScenarios() {
println("=" * 50)
println("🏠 智能家居控制系統演示")
println("=" * 50)
// 演示回家模式
smartHome.arriveHome()
displayStatus()
Thread.sleep(2000)
// 演示影院模式
smartHome.startMovieNight()
displayStatus()
Thread.sleep(2000)
// 演示睡眠模式
smartHome.startSleepMode()
displayStatus()
Thread.sleep(2000)
// 演示離家模式
smartHome.leaveHome()
displayStatus()
Thread.sleep(2000)
// 演示節能模式
smartHome.enableEnergySaving()
displayStatus()
// 演示單獨控制
println("\n🎛️ 單獨控制演示")
smartHome.controlLighting("bedroom", "on")
smartHome.controlTemperature(22.0)
}
fun displayStatus() {
println("\n📊 當前家居狀態:")
val status = smartHome.getHomeStatus()
status.forEach { (system, state) ->
println(" $system: $state")
}
println()
}
// 語音控制接口
fun voiceCommand(command: String) {
when (command.toLowerCase()) {
"我回家了" -> smartHome.arriveHome()
"我要看電影" -> smartHome.startMovieNight()
"我要睡覺了" -> smartHome.startSleepMode()
"我要出門了" -> smartHome.leaveHome()
"節能模式" -> smartHome.enableEnergySaving()
else -> println("❌ 無法識別的指令: $command")
}
displayStatus()
}
}
// 網頁控制界面
class WebControlPanel(private val smartHome: SmartHomeFacade) {
fun renderDashboard() {
val status = smartHome.getHomeStatus()
println("""
<div class="dashboard">
<h1>智能家居控制面板</h1>
<div class="status">
<h2>當前狀態</h2>
<p>燈光: ${status["lighting"]}</p>
<p>空調: ${status["ac"]}</p>
<p>娛樂: ${status["entertainment"]}</p>
<p>安防: ${status["security"]}</p>
<p>窗簾: ${status["curtains"]}</p>
</div>
<div class="controls">
<button onclick="arriveHome()">回家模式</button>
<button onclick="leaveHome()">離家模式</button>
<button onclick="movieNight()">影院模式</button>
<button onclick="sleepMode()">睡眠模式</button>
</div>
</div>
""".trimIndent())
}
}
4. 測試代碼
fun main() {
// 創建智能家居系統
val smartHome = SmartHomeFacade()
// 移動APP演示
val app = SmartHomeApp(smartHome)
app.demonstrateScenarios()
// 語音控制測試
println("\n🎤 語音控制測試:")
app.voiceCommand("我回家了")
app.voiceCommand("我要看電影")
app.voiceCommand("節能模式")
// 網頁面板演示
println("\n💻 網頁控制面板:")
val webPanel = WebControlPanel(smartHome)
webPanel.renderDashboard()
// 性能測試:直接操作 vs 使用外觀模式
println("\n⏱️ 性能對比測試:")
testDirectAccess()
testFacadeAccess()
}
// 測試直接操作子系統的複雜性
fun testDirectAccess() {
val startTime = System.currentTimeMillis()
// 直接操作各個子系統實現回家模式
val lighting = LightingSystem()
val ac = AirConditioningSystem()
val entertainment = EntertainmentSystem()
val security = SecuritySystem()
val curtains = CurtainSystem()
// 實現回家模式需要調用多個方法
security.disarmAlarm()
security.unlockDoors()
lighting.turnOn("living_room")
curtains.open("living_room")
ac.setTemperature(24.0)
ac.turnOn()
entertainment.turnOnSoundSystem()
entertainment.setSource("Music")
entertainment.setVolume(30)
val duration = System.currentTimeMillis() - startTime
println("直接操作耗時: ${duration}ms (需要了解所有子系統細節)")
}
// 測試使用外觀模式的簡便性
fun testFacadeAccess() {
val startTime = System.currentTimeMillis()
val smartHome = SmartHomeFacade()
smartHome.arriveHome() // 一行代碼完成複雜操作
val duration = System.currentTimeMillis() - startTime
println("外觀模式耗時: ${duration}ms (簡單易用)")
}
外觀模式的進階應用
1. 分層外觀模式
// 基礎外觀
open class BasicHomeFacade(
protected val lighting: LightingSystem,
protected val ac: AirConditioningSystem
) {
open fun basicControl() {
println("基礎家居控制")
}
}
// 高級外觀繼承基礎外觀
class AdvancedHomeFacade(
lighting: LightingSystem,
ac: AirConditioningSystem,
private val entertainment: EntertainmentSystem
) : BasicHomeFacade(lighting, ac) {
override fun basicControl() {
super.basicControl()
println("增強的基礎控制")
}
fun advancedScenarios() {
startMovieNight()
startPartyMode()
}
private fun startPartyMode() {
println("🎉 啓動派對模式")
// 複雜的場景設置
}
}
2. 動態外觀模式
// 支持動態配置的外觀
class DynamicFacade {
private val subsystems = mutableMapOf<String, Any>()
fun registerSubsystem(name: String, subsystem: Any) {
subsystems[name] = subsystem
}
fun executeScenario(scenario: Map<String, List<String>>) {
scenario.forEach { (subsystemName, actions) ->
val subsystem = subsystems[subsystemName]
actions.forEach { action ->
executeAction(subsystem, action)
}
}
}
private fun executeAction(subsystem: Any?, action: String) {
// 使用反射動態調用方法
try {
val method = subsystem!!::class.java.getMethod(action)
method.invoke(subsystem)
} catch (e: Exception) {
println("無法執行操作: $action on $subsystem")
}
}
}
外觀模式在Android開發中的應用
1. 多媒體播放器外觀
class MediaPlayerFacade(
private val audioManager: AudioManager,
private val mediaPlayer: MediaPlayer,
private val notificationManager: NotificationManager
) {
fun playMedia(mediaUri: Uri, playInBackground: Boolean = false) {
// 管理音頻焦點
audioManager.requestAudioFocus(
audioFocusRequestBuilder.build()
)
// 配置媒體播放器
mediaPlayer.setDataSource(mediaUri.toString())
mediaPlayer.prepareAsync()
// 管理通知
if (playInBackground) {
showMediaNotification()
}
mediaPlayer.setOnPreparedListener {
it.start()
}
}
fun stopMedia() {
mediaPlayer.stop()
mediaPlayer.release()
audioManager.abandonAudioFocus(null)
hideMediaNotification()
}
private fun showMediaNotification() {
// 創建媒體播放通知
}
private fun hideMediaNotification() {
// 隱藏通知
}
}
2. 網絡請求外觀
class NetworkFacade(
private val okHttpClient: OkHttpClient,
private val gson: Gson,
private val cacheManager: CacheManager
) {
suspend fun <T> request(
url: String,
method: String = "GET",
body: Any? = null,
responseType: Class<T>
): Result<T> {
return try {
// 檢查緩存
val cached = cacheManager.get<T>(url)
if (cached != null) {
return Result.success(cached)
}
// 構建請求
val request = buildRequest(url, method, body)
// 執行網絡請求
val response = okHttpClient.newCall(request).execute()
// 解析響應
val result = parseResponse<T>(response, responseType)
// 緩存結果
result.onSuccess { data ->
cacheManager.put(url, data)
}
result
} catch (e: Exception) {
Result.failure(e)
}
}
private fun <T> parseResponse(response: Response, responseType: Class<T>): Result<T> {
// 簡化實現
return try {
val json = response.body?.string() ?: ""
val result = gson.fromJson(json, responseType)
Result.success(result)
} catch (e: Exception) {
Result.failure(e)
}
}
}
外觀模式的優缺點
✅ 優點
- 簡化接口:為複雜系統提供簡單統一的接口
- 解耦合:客户端與子系統解耦,提高獨立性
- 易於使用:降低學習成本,提高開發效率
- 提高可維護性:子系統變化不影響客户端
❌ 缺點
- 不夠靈活:可能無法滿足所有特殊需求
- 增加層數:多了一個抽象層
- 可能成為上帝對象:如果外觀類過於龐大,違背單一職責原則
最佳實踐
- 合理劃分:根據業務領域劃分外觀,避免上帝對象
- 保持簡潔:外觀接口應該簡單明瞭
- 分層設計:複雜系統可以使用分層外觀
- 適度使用:不是所有系統都需要外觀模式
總結
外觀模式通過提供統一的簡化接口,有效降低了複雜系統的使用難度。在智能家居案例中,我們看到:
- 🎯 簡化複雜性:將多個子系統的複雜操作封裝成簡單場景
- 🔄 提高可用性:用户無需瞭解底層技術細節
- 🏗️ 良好封裝:子系統變化不影響客户端代碼
- 📱 多客户端支持:同時支持APP、網頁、語音等多種客户端
適用場景:
- 複雜子系統需要提供簡單接口
- 構建分層系統結構
- 需要解耦客户端和子系統
- 為遺留系統提供現代化接口
本文章為轉載內容,我們尊重原作者對文章享有的著作權。如有內容錯誤或侵權問題,歡迎原作者聯繫我們進行內容更正或刪除文章。