复合模式:就是将多种设计模式融合在一起,实现一个更高阶的功能,一个非常经典的复合模式就是MVC。
MVC模式在过去的十几年里,使用频率非常高,尤其是前后端尚未分离的时候,类似的JSP、PHP、ASP.Net MVC技术,都使用MVC模式,当前后端分离越来越常见后,衍生出很多模式,MVVM、MVP等等,如React、Vue等等框架;
MVC模式是由策略模式 + 观察者模式组合而成,详细如下:
- Model(模型):
模型用于获取数据,保存页面的状态; - View(视图):
不同状态下的应用,呈现的视图是不一样的,比如登录页面、个人页面、列表页面;
接收用户的行为事件,比如点击登录、点击退出、删除等等; - Controller(控制器):
控制器主要是根据应用状态(策略)来切换页面显示;
M、V、C的依赖场景:
C根据应用状态,返回不同的V;
V根据用户的操作产生事件,传递给C;
C接收事件,做出反应,更新M,返回不同的V给前端;
代码:
// 模型(Model)
interface IUserModelAbserver {
onLoginStateChanged: (isLogin: boolean) => void,
}
class UserModel {
private isLogin: boolean = false
public constructor(observer: IUserModelAbserver) {
this.observer = observer
}
private observer: IUserModelAbserver
public userLogin() {
return this.isLogin
}
public setIsLogin(isLogin: boolean) {
this.isLogin = isLogin
this.observer.onLoginStateChanged(isLogin)
}
}
// 视图(View)
enum ViewEvent {
Login,
LoginOut
}
interface IEventHandler {
onEmitEvent: (event: ViewEvent) => void
}
abstract class View {
public handler: IEventHandler
constructor(handler: IEventHandler) {
this.handler = handler
}
abstract render(): string
}
class LoginView extends View {
public onClickLogin() {
this.handler.onEmitEvent(ViewEvent.Login)
}
public render() {
return '<登录页面的HTML><点击登录></html>'
}
}
class UserProfileView extends View {
public onClickLogout() {
this.handler.onEmitEvent(ViewEvent.LoginOut)
}
public render() {
return '<用户个人页面的HTML><点击退出></html>'
}
}
// 控制器(Controller)
class Controller implements IUserModelAbserver, IEventHandler {
private model: UserModel
private view: View
public constructor() {
this.model = new UserModel(this)
console.log('初始为登录界面')
this.view = new LoginView(this)
}
public onLoginStateChanged(isLogin: boolean) {
console.log('登录状态变更...')
if (this.model.userLogin()) {
console.log('用户已登录成功 -> 切换为个人页面')
this.view = new UserProfileView(this)
} else {
console.log('用户退出登录 -> 切换为登录页面')
this.view = new LoginView(this)
}
}
public onEmitEvent(evt: ViewEvent) {
console.log('用户操作界面,发生事件...')
if (evt === ViewEvent.Login) {
console.log('点击登录...')
this.model.setIsLogin(true)
} else if (evt === ViewEvent.LoginOut) {
console.log('点击退出...')
this.model.setIsLogin(false)
}
}
public render() {
console.log('渲染页面内容...')
return this.view.render()
}
}
// Test
const controller = new Controller()
console.log(controller.render())
// 模拟点击登录
let v: any = controller['view']
v.onClickLogin()
console.log(controller.render())
// 模拟退出登录
v = controller['view']
v.onClickLogout()
console.log(controller.render())
输出结果:
[LOG]: 初始为登录界面
[LOG]: 渲染页面内容...
[LOG]: <登录页面的HTML><点击登录>
[LOG]: 用户操作界面,发生事件...
[LOG]: 点击登录...
[LOG]: 登录状态变更...
[LOG]: 用户已登录成功 -> 切换为个人页面
[LOG]: 渲染页面内容...
[LOG]: <用户个人页面的HTML><点击退出>
[LOG]: 用户操作界面,发生事件...
[LOG]: 点击退出...
[LOG]: 登录状态变更...
[LOG]: 用户退出登录 -> 切换为登录页面
[LOG]: 渲染页面内容...
[LOG]: <登录页面的HTML><点击登录>