软件设计原则 (Software Design Principles)
这些原则的共同目标:写出易读、易维护、易扩展的代码
一、基础原则
KISS (Keep It Simple, Stupid)
保持简单
- 代码越简单越好,避免不必要的复杂性
- 能用 3 行代码解决的问题,不要写 30 行
- 简单的方案通常更容易维护和调试
// Bad - 过度设计
const isEven = (n) => new Promise((resolve) =>
setTimeout(() => resolve(n % 2 === 0), 0));
// Good - 简单直接
const isEven = (n) => n % 2 === 0;
YAGNI (You Aren't Gonna Need It)
你不会需要它
- 不要为"将来可能需要"的功能写代码
- 只实现当前确实需要的功能
- 预测未来需求往往是错的,白费功夫
// Bad - 预设了很多用不到的配置
function createUser(name, options = {
enableNotifications: true,
theme: 'dark',
language: 'zh',
timezone: 'Asia/Shanghai',
// ... 20 个可能永远用不到的选项
}) { ... }
// Good - 只做需要的
function createUser(name) { ... }
DRY (Don't Repeat Yourself)
不要重复自己
- 相同逻辑只写一次,避免复制粘贴代码
- 重复代码意味着修改时要改多处,容易遗漏
// Bad - 重复代码
function validateEmail(email) {
const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return regex.test(email);
}
function validateUserEmail(email) {
const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return regex.test(email);
}
// Good - 复用
function validateEmail(email) {
const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return regex.test(email);
}
// 其他地方直接调用 validateEmail()
WET (Write Everything Twice)
写两遍再抽象 - DRY 的平衡
- 过早抽象比重复更糟糕
- 等代码重复 2-3 次再考虑抽象
- 避免为了 DRY 而创建不合理的抽象
二、SOLID 原则(面向对象五大原则)
S - Single Responsibility Principle (SRP)
单一职责原则
一个类/函数只做一件事,只有一个修改的理由。
// Bad - 一个类做太多事
class User {
saveToDatabase() { ... }
sendEmail() { ... }
generateReport() { ... }
}
// Good - 职责分离
class User { ... }
class UserRepository { saveToDatabase() { ... } }
class EmailService { sendEmail() { ... } }
class ReportGenerator { generateReport() { ... } }
O - Open/Closed Principle (OCP)
开闭原则
对扩展开放,对修改关闭。添加新功能时,不应修改现有代码。
// Bad - 每次加新类型都要改这个函数
function calculateArea(shape) {
if (shape.type === 'circle') {
return Math.PI * shape.radius ** 2;
} else if (shape.type === 'rectangle') {
return shape.width * shape.height;
}
// 新增类型要改这里...
}
// Good - 通过多态扩展
class Shape {
area() { throw new Error('Must implement'); }
}
class Circle extends Shape {
area() { return Math.PI * this.radius ** 2; }
}
class Rectangle extends Shape {
area() { return this.width * this.height; }
}
L - Liskov Substitution Principle (LSP)
里氏替换原则
子类可以替换父类,程序行为不变。
// Bad - 子类破坏了父类的契约
class Bird {
fly() { return 'flying'; }
}
class Penguin extends Bird {
fly() { throw new Error('Cannot fly'); } // 违反 LSP
}
// Good - 重新设计继承关系
class Bird { ... }
class FlyingBird extends Bird {
fly() { return 'flying'; }
}
class Penguin extends Bird {
swim() { return 'swimming'; }
}
I - Interface Segregation Principle (ISP)
接口隔离原则
接口要小而专一,不要强迫实现不需要的方法。
// Bad - 臃肿的接口
interface Worker {
work(): void;
eat(): void;
sleep(): void;
}
// Good - 拆分接口
interface Workable {
work(): void;
}
interface Eatable {
eat(): void;
}
// 按需实现
class Robot implements Workable {
work() { ... }
}
D - Dependency Inversion Principle (DIP)
依赖倒置原则
依赖抽象而非具体实现。高层模块不应依赖低层模块。
// Bad - 直接依赖具体实现
class UserService {
constructor() {
this.db = new MySQLDatabase(); // 紧耦合
}
}
// Good - 依赖抽象
class UserService {
constructor(database) { // 注入抽象
this.db = database;
}
}
// 可以传入任何实现了 Database 接口的对象
new UserService(new MySQLDatabase());
new UserService(new PostgreSQLDatabase());
三、其他重要原则
LoD / Law of Demeter
迪米特法则 / 最少知识原则
对象只与直接朋友通信,不要链式调用陌生对象。
// Bad - 知道太多内部结构
order.getCustomer().getAddress().getCity();
// Good - 只问直接对象
order.getShippingCity();
Separation of Concerns (SoC)
关注点分离
不同功能的代码分开放,各司其职。
典型应用:MVC 模式
- Model: 数据和业务逻辑
- View: 界面展示
- Controller: 处理用户输入
SLAP (Single Level of Abstraction Principle)
单一抽象层次原则
一个函数内的代码应该在同一抽象层级。
// Bad - 混合抽象层次
function processOrder(order) {
// 高层抽象
validateOrder(order);
// 突然变成低层细节
const connection = mysql.createConnection({...});
connection.query('INSERT INTO orders...');
// 又回到高层
sendConfirmationEmail(order);
}
// Good - 保持同一层次
function processOrder(order) {
validateOrder(order);
saveOrder(order);
sendConfirmationEmail(order);
}
四、速查表
| 原则 | 缩写 | 一句话总结 |
|---|---|---|
| Keep It Simple, Stupid | KISS | 保持简单 |
| You Aren't Gonna Need It | YAGNI | 不需要就别写 |
| Don't Repeat Yourself | DRY | 别重复 |
| Write Everything Twice | WET | 别过早抽象 |
| Single Responsibility | SRP | 一个类只做一件事 |
| Open/Closed | OCP | 扩展开放,修改关闭 |
| Liskov Substitution | LSP | 子类能替换父类 |
| Interface Segregation | ISP | 接口要小而专 |
| Dependency Inversion | DIP | 依赖抽象不依赖具体 |
| Law of Demeter | LoD | 少管闲事 |
| Separation of Concerns | SoC | 各管各的 |
| Single Level of Abstraction | SLAP | 同一抽象层次 |
五、如何应用
- 不要教条:原则是指导,不是铁律
- 权衡取舍:有时原则之间会冲突,需要判断
- 渐进改进:先让代码工作,再逐步优化
- 团队共识:原则的应用程度需要团队达成一致
"任何傻瓜都能写出计算机能理解的代码。优秀的程序员写出人类能理解的代码。" — Martin Fowler
产品设计原则 (Product Design Principles)
这些原则的共同目标:创造用户真正需要、易用且有价值的产品
一、用户体验基础原则
尼尔森十大可用性原则 (Nielsen's 10 Usability Heuristics)
Jakob Nielsen 于 1994 年提出,至今仍是 UX 设计的黄金标准。
| # | 原则 | 说明 |
|---|---|---|
| 1 | 系统状态可见性 | 让用户知道正在发生什么(加载中、已保存、出错了) |
| 2 | 系统与现实匹配 | 使用用户熟悉的语言和概念,而非技术术��� |
| 3 | 用户控制与自由 | 提供"撤销"和"退出",让用户能纠正错误 |
| 4 | 一致性与标准 | 相同的操作产生相同的结果,遵循平台惯例 |
| 5 | 错误预防 | 比起好的错误提示,更好的是从源头防止错误 |
| 6 | 识别而非回忆 | 让选项可见,减少用户记忆负担 |
| 7 | 灵活性与效率 | 为新手和专家都提供合适的操作方式 |
| 8 | 美学与极简设计 | 只展示必要信息,避免视觉噪音 |
| 9 | 帮助用户识别和恢复错误 | 错误提示要说人话,并提供解决方案 |
| 10 | 帮助与文档 | 最好不需要文档,但必要时要易于搜索和理解 |
格式塔原则 (Gestalt Principles)
视觉感知心理学原则
| 原则 | 说明 | 应用 |
|---|---|---|
| 接近性 | 靠近的元素被视为一组 | 表单分组、导航菜单 |
| 相似性 | 相似的元素被视为一组 | 按钮样式、图标风格 |
| 连续性 | 眼睛会沿着路径移动 | 流程图、时间线 |
| 闭合性 | 大脑会补全不完整的形状 | Logo 设计、图标 |
| 图底关系 | 区分前景和背景 | 弹窗、卡片阴影 |
| 共同命运 | 一起移动的元素被视为一组 | 动画、拖拽排序 |
二、交互设计原则
费茨定律 (Fitts's Law)
点击目标的时间与距离和大小有关
时间 = a + b × log₂(距离/大小 + 1)
应用:
- 重要按钮要足够大
- 常用功能放在容易触达的位置
- 移动端按钮最小 44×44px
希克定律 (Hick's Law)
选择越多,决策时间越长
时间 = a + b × log₂(n + 1) // n = 选项数量
应用:
- 减少选项数量(推荐 5-7 个)
- 分类和分组选项
- 提供默认选项和推荐
米勒定律 (Miller's Law)
短期记忆容量约为 7±2 个单元
应用:
- 导航菜单不超过 7 项
- 电话号码分段显示:138-0000-0000
- 步骤流程控制在 5-7 步
雅各布定律 (Jakob's Law)
用户把大部分时间花在其他网站上
用户期望你的产品和他们熟悉的产品一样工作。
应用:
- 遵循平台设计规范(iOS HIG、Material Design)
- 不要重新发明轮子(购物车图标、汉堡菜单)
- 创新要渐进,不要颠覆用户认知
多尔蒂门槛 (Doherty Threshold)
响应时间 < 400ms 时,用户会保持专注
| 响应时间 | 用户感受 |
|---|---|
| < 100ms | 即时 |
| 100-300ms | 流畅 |
| 300-1000ms | 可接受 |
| > 1000ms | 需要加载提示 |
| > 10s | 用户可能离开 |
奥卡姆剃刀 (Occam's Razor)
如无必要,勿增实体
最简单的解决方案通常是最好的。
应用:
- 功能做减法
- 界面元素最小化
- 流程步骤最少化
三、产品策略原则
MVP (Minimum Viable Product)
最小可行产品
用最少的功能验证核心假设,快速获取用户反馈。
传统方式:花 6 个月做完整产品 → 发现没人要
MVP 方式:花 2 周做核心功能 → 验证 → 迭代
80/20 法则 (Pareto Principle)
80% 的结果来自 20% 的原因
应用:
- 80% 用户只用 20% 功能 → 优化核心功能
- 80% 问题来自 20% 原因 → 聚焦关键问题
- 80% 收入来自 20% 用户 → 服务好核心用户
峰终定律 (Peak-End Rule)
用户记住的是体验的峰值和结尾
应用:
- 创造"哇"时刻(峰值体验)
- 优化结束体验(感谢页、确认邮件)
- 即使过程有问题,好的结尾也能挽回印象
宜家效应 (IKEA Effect)
用户对自己参与创造的东西估值更高
应用:
- 让用户自定义(头像、主题、布局)
- 引导用户完成设置流程
- 用户生成内容(UGC)
损失厌恶 (Loss Aversion)
失去的痛苦 > 获得的快乐(约 2 倍)
应用:
- "还剩 3 件" 比 "库存充足" 更有效
- 免费试用到期提醒
- 积分/等级即将过期
四、设计系统原则
原子设计 (Atomic Design)
从小到大构建设计系统
原子 → 分子 → 组织 → 模板 → 页面
(按钮) (搜索框) (导航栏) (列表页) (首页)
设计一致性三层次
| 层次 | 说明 | 示例 |
|---|---|---|
| 视觉一致性 | 颜色、字体、间距统一 | 品牌色、字号规范 |
| 功能一致性 | 相同组件行为一致 | 所有删除都需确认 |
| 外部一致性 | 符合平台/行业惯例 | iOS 返回在左上角 |
五、无障碍设计原则 (Accessibility - A11Y)
WCAG 四大原则
| 原则 | 说明 |
|---|---|
| 可感知 | 信息能被所有感官获取(文字、语音、触觉) |
| 可操作 | 所有功能可通过键盘操作 |
| 可理解 | 内容和操作易于理解 |
| 健壮性 | 兼容各种辅助技术 |
实践要点
- 图片添加 alt 文本
- 颜色对比度 ≥ 4.5:1
- 可点击区域 ≥ 44×44px
- 支持键盘导航
- 不仅靠颜色传达信息
六、速查表
用户体验
| 原则 | 一句话 |
|---|---|
| 尼尔森十原则 | 可用性设计黄金标准 |
| 格式塔原则 | 视觉分组和感知 |
| 费茨定律 | 目标要大、距离要近 |
| 希克定律 | 选项要少 |
| 米勒定律 | 记忆容量 7±2 |
| 雅各布定律 | 遵循用户习惯 |
| 多尔蒂门槛 | 响应要快(<400ms) |
| 奥卡姆剃刀 | 保持简单 |
产品策略
| 原则 | 一句话 |
|---|---|
| MVP | 最小功能快速验证 |
| 80/20 法则 | 聚焦关键 20% |
| 峰终定律 | 做好高潮和结尾 |
| 宜家效应 | 让用户参与创造 |
| 损失厌恶 | 失去比得到更痛 |
七、推荐资源
书籍
- 《设计心理学》- Don Norman
- 《点石成金》- Steve Krug
- 《简约至上》- Giles Colborne
- 《用户体验要素》- Jesse James Garrett
网站
- Laws of UX - 交互设计定律可视化
- Nielsen Norman Group - 可用性研究
- Material Design - Google 设计系统
- Human Interface Guidelines - Apple 设计指南