在软件开发中,我们经常会遇到需要与复杂子系统交互的场景。外观模式通过提供一个统一的简化接口,隐藏系统的复杂性,让客户端能够更轻松地使用系统功能。
什么是外观模式?
外观模式(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、网页、语音等多种客户端
适用场景:
-
复杂子系统需要提供简单接口
-
构建分层系统结构
-
需要解耦客户端和子系统
-
为遗留系统提供现代化接口