10. 外观模式(Facade)
10. 外观模式(Facade)
问题:子系统接口复杂,调用者不想了解内部细节。
核心:提供一个高层简化接口,内部协调子系统。
class Projector {
on(): void { console.log('投影仪开机'); }
setInput(): void { console.log('切换到 HDMI'); }
}
class SoundSystem {
on(): void { console.log('音响开机'); }
setVolume(v: number): void { console.log(`音量设为 ${v}`); }
}
class Lights {
dim(): void { console.log('灯光调暗'); }
}
// 外观
class HomeTheaterFacade {
#projector = new Projector();
#sound = new SoundSystem();
#lights = new Lights();
watchMovie(): void {
this.#lights.dim();
this.#projector.on();
this.#projector.setInput();
this.#sound.on();
this.#sound.setVolume(50);
}
}
const theater = new HomeTheaterFacade();
theater.watchMovie();package facade
import "fmt"
type Projector struct{}
func (p Projector) On() { fmt.Println("投影仪开机") }
func (p Projector) SetInput() { fmt.Println("切换到 HDMI") }
type SoundSystem struct{}
func (s SoundSystem) On() { fmt.Println("音响开机") }
func (s SoundSystem) SetVolume(v int) { fmt.Printf("音量设为 %d\n", v) }
type Lights struct{}
func (l Lights) Dim() { fmt.Println("灯光调暗") }
type HomeTheaterFacade struct {
projector Projector
sound SoundSystem
lights Lights
}
func NewHomeTheaterFacade() *HomeTheaterFacade {
return &HomeTheaterFacade{}
}
func (f *HomeTheaterFacade) WatchMovie() {
f.lights.Dim()
f.projector.On()
f.projector.SetInput()
f.sound.On()
f.sound.SetVolume(50)
}实战场景
- 结账流程:下单、锁库存、计算优惠、创建支付单、写订单日志、发通知都藏在
checkout()后面。 - 文件上传:调用者只调用
upload(),内部完成压缩、鉴权、分片、重试、写元数据和返回 URL。 - 用户注册:注册接口内部串起参数校验、密码加密、创建用户、发送邮件、初始化默认设置。
- 前端 API Client:页面只调用
api.posts.create(),不用关心 fetch、headers、错误转换、刷新 token。 - 复杂 SDK 包装:第三方 SDK 初始化参数多、调用链长时,项目内部提供一个更小的门面方法。
- 报表生成:外部只传报表类型和时间范围,内部负责拉数据、聚合、渲染模板、导出文件。
- 音视频播放控制:播放、暂停、音量、字幕、清晰度、缓冲状态背后有多个子系统,UI 只接触播放器门面。
- 部署脚本:一个
deploy()封装构建、上传、迁移、清缓存、健康检查和回滚入口。 - 权限判断:业务代码调用
can(user, action, resource),内部再组合角色、策略、资源归属、租户边界。 - 微前端宿主:应用加载、沙箱、样式隔离、路由同步、通信机制可以由统一门面隐藏。