外观模式:复杂系统的统一入口

在软件开发中,我们经常会遇到需要与复杂子系统交互的场景。外观模式通过提供一个统一的简化接口,隐藏系统的复杂性,让客户端能够更轻松地使用系统功能。

什么是外观模式?

外观模式(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)
        }
    }
}

外观模式的优缺点

✅ 优点

  1. 简化接口​:为复杂系统提供简单统一的接口

  2. 解耦合​:客户端与子系统解耦,提高独立性

  3. 易于使用​:降低学习成本,提高开发效率

  4. 提高可维护性​:子系统变化不影响客户端

❌ 缺点

  1. 不够灵活​:可能无法满足所有特殊需求

  2. 增加层数​:多了一个抽象层

  3. 可能成为上帝对象​:如果外观类过于庞大,违背单一职责原则

最佳实践

  1. 合理划分​:根据业务领域划分外观,避免上帝对象

  2. 保持简洁​:外观接口应该简单明了

  3. 分层设计​:复杂系统可以使用分层外观

  4. 适度使用​:不是所有系统都需要外观模式

总结

外观模式通过提供统一的简化接口,有效降低了复杂系统的使用难度。在智能家居案例中,我们看到:

  • 🎯 ​简化复杂性​:将多个子系统的复杂操作封装成简单场景

  • 🔄 ​提高可用性​:用户无需了解底层技术细节

  • 🏗️ ​良好封装​:子系统变化不影响客户端代码

  • 📱 ​多客户端支持​:同时支持APP、网页、语音等多种客户端

适用场景​:

  • 复杂子系统需要提供简单接口

  • 构建分层系统结构

  • 需要解耦客户端和子系统

  • 为遗留系统提供现代化接口

相关推荐
Yeniden20 小时前
【设计模式】# 外观模式(Facade)大白话讲解!
java·设计模式·外观模式
杯莫停丶8 天前
设计模式之:外观模式
java·设计模式·外观模式
WaWaJie_Ngen9 天前
【设计模式】外观模式/门面模式(Facaed)
设计模式·外观模式
Meteors.12 天前
23种设计模式——外观模式(Facade Pattern)详解
设计模式·外观模式
胖虎112 天前
iOS中的设计模式(九)- 外观模式 用外观模式点一份外卖:Swift 实战讲解
设计模式·外观模式
czy878747512 天前
用C语言实现外观模式
c语言·外观模式
Deschen13 天前
设计模式-外观模式
java·设计模式·外观模式
Mr_WangAndy19 天前
C++设计模式_结构型模式_外观模式Facade
c++·设计模式·外观模式
代码萌新知1 个月前
设计模式学习(五)装饰者模式、桥接模式、外观模式
java·学习·设计模式·桥接模式·装饰器模式·外观模式