浅析 MVC
MVC 是一种软件设计模式,它将应用程序分为三个部分:模型(Model)、视图(View)和控制器(Controller)。MVC 的目的是实现关注点分离,即每个部分只负责自己的功能,而不干涉其他部分的逻辑。这样可以提高代码的可维护性、可复用性和可测试性。
模型(Model)
模型是应用程序的核心部分,它负责处理数据和业务逻辑。模型通常与数据库或其他数据源交互,提供数据的增删改查等操作。模型也可以定义一些事件,当数据发生变化时,通知其他部分更新。
例如,一个用户模型可能有以下属性和方法:
class User {
constructor(name, age, email) {
this.name = name;
this.age = age;
this.email = email;
}
// 保存用户到数据库
save() {
// 调用数据库 API
db.insert(this);
// 触发事件
EventBus.emit('userSaved', this);
}
// 从数据库删除用户
delete() {
// 调用数据库 API
db.delete(this);
// 触发事件
EventBus.emit('userDeleted', this);
}
// 更新用户信息
update(data) {
// 调用数据库 API
db.update(this, data);
// 触发事件
EventBus.emit('userUpdated', this);
}
// 查询用户信息
static find(query) {
// 调用数据库 API
return db.find(query);
}
}
视图(View)
视图是应用程序的界面部分,它负责展示数据和接收用户输入。视图通常由 HTML、CSS 和 JavaScript 组成,可以使用各种框架或库来实现。视图可以订阅模型的事件,当数据变化时,更新界面。视图也可以触发控制器的方法,当用户操作时,执行相应的逻辑。
例如,一个用户列表视图可能有以下代码:
<!-- HTML -->
<ul id="user-list">
<!-- 动态生成用户列表 -->
</ul>
<button id="add-user">添加用户</button>
/* CSS */
ul {
list-style: none;
}
li {
margin: 10px;
}
button {
margin: 10px;
}
// JavaScript
// 获取 DOM 元素
const userList = document.getElementById('user-list');
const addUser = document.getElementById('add-user');
// 渲染用户列表
function renderUserList(users) {
// 清空原有内容
userList.innerHTML = '';
// 遍历用户数组
for (let user of users) {
// 创建 li 元素
let li = document.createElement('li');
// 设置 li 的内容
li.textContent = `${user.name} (${user.age}) - ${user.email}`;
// 添加删除按钮
let deleteButton = document.createElement('button');
deleteButton.textContent = '删除';
deleteButton.addEventListener('click', () => {
// 调用控制器方法
controller.deleteUser(user);
});
li.appendChild(deleteButton);
// 添加 li 到 ul 中
userList.appendChild(li);
}
}
// 订阅模型事件
EventBus.on('userSaved', (user) => {
// 获取当前用户列表
let users = User.find();
// 渲染用户列表
renderUserList(users);
});
EventBus.on('userDeleted', (user) => {
// 获取当前用户列表
let users = User.find();
// 渲染用户列表
renderUserList(users);
});
EventBus.on('userUpdated', (user) => {
// 获取当前用户列表
let users = User.find();
// 渲染用户列表
renderUserList(users);
});
// 添加用户按钮的点击事件
addUser.addEventListener('click', () => {
// 调用控制器方法
controller.addUser();
});
控制器(Controller)
控制器是应用程序的中间部分,它负责协调模型和视图之间的交互。控制器通常定义一些方法,根据用户输入或其他条件,调用模型的操作或更新视图的显示。控制器可以实现一些业务逻辑,例如验证、过滤、转换等。
例如,一个用户控制器可能有以下方法:
class UserController {
// 添加用户
addUser() {
// 获取用户输入
let name = prompt('请输入姓名');
let age = prompt('请输入年龄');
let email = prompt('请输入邮箱');
// 验证输入
if (name && age && email) {
// 创建用户对象
let user = new User(name, age, email);
// 调用模型方法
user.save();
} else {
// 提示错误
alert('请输入完整信息');
}
}
// 删除用户
deleteUser(user) {
// 确认操作
if (confirm(`确定要删除 ${user.name} 吗?`)) {
// 调用模型方法
user.delete();
}
}
}
// 创建控制器实例
const controller = new UserController();
EventBus
EventBus 是一个事件总线,它提供了一种发布-订阅模式的通信机制。EventBus 可以让不同的组件之间解耦,通过事件来传递数据和消息。EventBus 有以下几个常用的 API:
emit(event, data):触发一个事件,并传递一些数据。on(event, callback):订阅一个事件,并指定一个回调函数。off(event, callback):取消订阅一个事件,并移除一个回调函数。once(event, callback):订阅一个事件,并指定一个只执行一次的回调函数。
例如,我们可以使用 EventBus 来实现一个简单的计数器:
// 创建 EventBus 实例
const EventBus = new EventBus();
// 定义计数器模型
class Counter {
constructor() {
this.count = 0;
}
// 增加计数
increase() {
this.count++;
// 触发事件
EventBus.emit('countChanged', this.count);
}
// 减少计数
decrease() {
this.count--;
// 触发事件
EventBus.emit('countChanged', this.count);
}
}
// 创建计数器实例
const counter = new Counter();
// 定义计数器视图
class CounterView {
constructor() {
// 获取 DOM 元素
this.display = document.getElementById('display');
this.increaseButton = document.getElementById('increase');
this.decreaseButton = document.getElementById('decrease');
// 添加按钮事件监听器
this.increaseButton.addEventListener('click', () => {
// 调用模型方法
counter.increase();
});
this.decreaseButton.addEventListener('click', () => {
// 调用模型方法
counter.decrease();
});
// 订阅事件
EventBus.on('countChanged', (count) => {
// 更新显示
this.display.textContent = count;
});
}
}
// 创建视图实例
const view = new CounterView();
表驱动编程
表驱动编程是一种编程技巧,它将一些逻辑或数据从代码中抽象出来,放到一个表格(数组、对象、哈希表等)中,然后通过查表来执行相应的操作。表驱动编程可以使代码更简洁、更易读、更易维护,也可以避免重复和硬编码。
例如,我们可以使用表驱动编程来实现一个简单的计算器:
// 定义操作符和对应的函数的映射表
const operators = {
'+': (a, b) => a + b,
'-': (a, b) => a - b,
'*': (a, b) => a * b,
'/': (a, b) => a / b,
};
// 定义一个计算函数,接受两个数字和一个操作符
function calculate(a, b, op) {
// 查表获取对应的函数
let func = operators[op];
// 判断是否存在该函数
if (func) {
// 调用函数并返回结果
return func(a, b);
} else {
// 抛出异常
throw new Error('Invalid operator');
}
}
// 测试
console.log(calculate(1, 2, '+')); // 3
console.log(calculate(3, 4, '*')); // 12
console.log(calculate(5, 6, '%')); // Error: Invalid operator
模块化的理解
模块化是一种编程思想,它将一个复杂的程序分解为多个相互独立的模块,每个模块只负责一部分功能,而不影响其他模块。模块化可以提高代码的可读性、可复用性、可扩展性和可测试性,也可以降低代码的耦合度和复杂度。
个人认为,模块化有以下几个要点:
- 模块应该有明确的职责和边界,不要做过多或过少的事情。
- 模块应该有清晰的接口,定义好输入和输出,以及可能抛出的异常。
- 模块应该尽量遵循单一职责原则、开闭原则、里氏替换原则等设计原则。
- 模块应该尽量减少对外部的依赖,使用依赖注入、抽象工厂等方式来解决依赖问题。
- 模块应该有良好的文档和注释,说明模块的功能、用法、参数、返回值等信息。