第一章:JavaScript设计模式概述
JavaScript作为一门灵活的动态语言,广泛应用于前端与后端开发中。其基于原型和函数的一等公民特性,为实现多种设计模式提供了天然支持。设计模式是解决常见软件设计问题的经验总结,能够提升代码的可维护性、复用性和扩展性。
为何使用设计模式
- 提高代码组织结构的清晰度
- 促进团队协作中的代码一致性
- 降低模块间的耦合度,增强可测试性
常见的设计模式分类
| 类别 | 典型模式 |
|---|
| 创建型模式 | 工厂模式、单例模式、构造器模式 |
| 结构型模式 | 装饰器模式、适配器模式、代理模式 |
| 行为型模式 | 观察者模式、策略模式、迭代器模式 |
一个简单的工厂模式示例
// 工厂函数创建不同类型的用户对象
function createUser(type, name) {
// 根据类型返回对应的用户实例
if (type === 'admin') {
return {
name,
role: 'Administrator',
canEdit: true
};
} else if (type === 'guest') {
return {
name,
role: 'Guest',
canEdit: false
};
}
}
// 使用工厂函数创建对象
const admin = createUser('admin', 'Alice');
const guest = createUser('guest', 'Bob');
console.log(admin); // 输出管理员用户信息
console.log(guest); // 输出访客用户信息
graph TD
A[客户端请求对象] --> B{工厂判断类型}
B -->|admin| C[创建Admin实例]
B -->|guest| D[创建Guest实例]
C --> E[返回对象]
D --> E
第二章:创建型模式的核心原理与应用
2.1 工厂模式:解耦对象创建过程
在面向对象设计中,工厂模式通过将对象的实例化过程封装到专门的工厂类中,有效解耦了客户端代码与具体实现类之间的依赖。
核心思想
工厂模式的核心在于“延迟实例化”——客户端不直接使用 new 创建对象,而是通过调用工厂方法获取所需实例,从而提升系统的可扩展性与可维护性。
简单工厂示例
type Product interface {
GetName() string
}
type ConcreteProductA struct{}
func (p *ConcreteProductA) GetName() string {
return "Product A"
}
type ProductFactory struct{}
func (f *ProductFactory) CreateProduct(typeName string) Product {
switch typeName {
case "A":
return &ConcreteProductA{}
default:
return nil
}
}
上述代码中,
ProductFactory 根据传入参数决定返回哪种产品实例。新增产品时只需扩展判断逻辑,无需修改客户端代码,符合开闭原则。
2.2 单例模式:确保全局唯一实例
单例模式是一种创建型设计模式,确保一个类仅有一个实例,并提供全局访问点。在高并发或资源受限场景中,避免重复创建对象可显著提升性能与一致性。
实现方式
常见的懒汉式单例通过双重检查锁定保证线程安全:
public class Singleton {
private static volatile Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
上述代码中,
volatile 关键字防止指令重排序,两次判空确保效率,构造函数私有化限制外部实例化。
应用场景
2.3 建造者模式:分步构建复杂对象
建造者模式用于将复杂对象的构造过程与其表示分离,使得同样的构建逻辑可以创建不同的表现形式。该模式适用于具有多个可选属性或构造步骤繁多的对象。
核心角色与结构
- Builder:定义抽象接口,声明构建各部分的方法。
- ConcreteBuilder:实现具体构建逻辑,返回最终产品。
- Director:控制构建流程的顺序。
- Product:被构建的复杂对象。
代码示例(Java)
public class Computer {
private String cpu;
private String ram;
public void setCpu(String cpu) { this.cpu = cpu; }
public void setRam(String ram) { this.ram = ram; }
}
interface ComputerBuilder {
void buildCpu();
void buildRam();
Computer getComputer();
}
class GamingComputerBuilder implements ComputerBuilder {
private Computer computer = new Computer();
public void buildCpu() { computer.setCpu("i9"); }
public void buildRam() { computer.setRam("32GB"); }
public Computer getComputer() { return computer; }
}
上述代码中,
Computer 是目标复杂对象,通过不同
Builder 实现可定制化装配流程。Director 可调用构建步骤顺序,实现解耦。
2.4 原型模式:高效复制现有对象结构
原型模式是一种创建型设计模式,通过复制已有实例来创建新对象,避免重复初始化操作。该模式适用于对象创建成本较高或结构复杂的场景。
核心实现机制
在 Go 语言中,可通过接口定义克隆方法:
type Prototype interface {
Clone() Prototype
}
type ConcretePrototype struct {
Name string
Data map[string]interface{}
}
func (p *ConcretePrototype) Clone() Prototype {
// 深拷贝关键数据
data := make(map[string]interface{})
for k, v := range p.Data {
data[k] = v
}
return &ConcretePrototype{Name: p.Name, Data: data}
}
上述代码中,
Clone() 方法返回一个新对象,确保原始对象与副本之间无引用共享,防止数据污染。
应用场景对比
| 场景 | 是否推荐使用原型模式 | 说明 |
|---|
| 配置对象复制 | 是 | 避免重复解析配置文件 |
| 轻量级对象创建 | 否 | 直接构造更高效 |
2.5 抽象工厂模式:统一产品族的创建规范
抽象工厂模式用于创建一系列相关或相互依赖的对象,而无需指定具体类。它提供一个创建产品族的接口,确保组合的一致性。
核心结构与角色
- 抽象工厂(Abstract Factory):声明创建一组产品的方法
- 具体工厂(Concrete Factory):实现创建具体产品族的逻辑
- 抽象产品(Abstract Product):定义产品类型的接口
- 具体产品(Concrete Product):工厂创建的实际对象
代码示例
type GUIFactory interface {
CreateButton() Button
CreateCheckbox() Checkbox
}
type WinFactory struct{}
func (f *WinFactory) CreateButton() Button { return &WinButton{} }
func (f *WinFactory) CreateCheckbox() Checkbox { return &WinCheckbox{} }
上述代码定义了一个 GUI 抽象工厂,可生成按钮和复选框。Windows 工厂返回对应风格的控件实例,确保界面元素风格统一。
第三章:结构型模式的架构优化能力
3.1 装饰器模式:动态扩展对象功能
装饰器模式是一种结构型设计模式,允许在不修改对象本身的前提下,动态地添加功能。它通过将对象嵌入到装饰器类中,在运行时叠加行为。
核心思想
装饰器模式遵循开闭原则,对扩展开放、对修改封闭。每个装饰器都实现与目标对象相同的接口,并在其内部持有被装饰对象的实例。
代码示例(Go)
type Writer interface {
Write(string) error
}
type FileWriter struct{}
func (f *FileWriter) Write(data string) error {
// 模拟写入文件
fmt.Println("Writing to file:", data)
return nil
}
type CompressDecorator struct {
writer Writer
}
func (c *CompressDecorator) Write(data string) error {
compressed := "compressed(" + data + ")"
return c.writer.Write(compressed)
}
上述代码中,
CompressDecorator 包装了
Writer 接口,透明地添加压缩功能。调用
Write 时,先处理数据再委托给被包装对象。
- 优势:灵活替代继承,支持多层嵌套
- 适用场景:日志记录、权限校验、数据编码等横切关注点
3.2 适配器模式:兼容不匹配的接口
在系统集成中,不同组件常因接口不一致而无法直接协作。适配器模式通过封装一个类的接口,使其能与不兼容的客户端协同工作。
结构与角色
- 目标接口(Target):客户端期望使用的接口
- 适配器(Adapter):将被适配者的接口转换为目标接口
- 被适配者(Adaptee):现有需要被适配的类
代码示例
type Target interface {
Request()
}
type Adaptee struct{}
func (a *Adaptee) SpecificRequest() {
fmt.Println("Adaptee执行特殊请求")
}
type Adapter struct {
adaptee *Adaptee
}
func (a *Adapter) Request() {
a.adaptee.SpecificRequest() // 转换调用
}
上述代码中,
Adapter 将
Adaptee 的
SpecificRequest 方法包装为
Target 接口所需的
Request 方法,实现接口兼容。
3.3 代理模式:控制对象访问权限与流程
代理模式是一种结构型设计模式,用于为其他对象提供一种间接访问机制,在不改变原始类的前提下增强访问控制与行为扩展能力。
核心结构与角色划分
代理模式通常包含三个关键角色:
- Subject(抽象主题):定义真实对象和代理对象的共同接口;
- RealSubject(真实主题):具体业务逻辑实现者;
- Proxy(代理):持有真实对象的引用,控制其访问。
代码示例:Go语言实现
type Service interface {
Request() string
}
type RealService struct{}
func (r *RealService) Request() string {
return "处理请求"
}
type Proxy struct {
real *RealService
}
func (p *Proxy) Request() string {
if p.checkAccess() {
return p.real.Request()
}
return "拒绝访问"
}
func (p *Proxy) checkAccess() bool {
// 模拟权限校验
return true
}
上述代码中,
Proxy 在调用
RealService 前执行访问控制,实现了安全隔离。参数与方法调用被封装在统一接口下,外部调用者无法感知代理存在,符合“透明性”原则。
第四章:行为型模式的交互设计智慧
4.1 观察者模式:实现事件驱动机制
观察者模式是一种行为设计模式,允许对象在状态变化时自动通知其依赖者,广泛应用于事件驱动系统中。
核心结构与角色
该模式包含两个关键角色:**主题(Subject)** 和 **观察者(Observer)**。主题维护观察者列表,并在状态变更时触发通知。
- Subject:管理观察者注册与通知逻辑
- Observer:定义接收更新的接口
Go语言实现示例
type Observer interface {
Update(message string)
}
type Subject struct {
observers []Observer
}
func (s *Subject) Attach(o Observer) {
s.observers = append(s.observers, o)
}
func (s *Subject) Notify(message string) {
for _, obs := range s.observers {
obs.Update(message)
}
}
上述代码中,
Attach 方法用于注册观察者,
Notify 遍历所有观察者并调用其
Update 方法,实现松耦合的事件广播机制。
4.2 策略模式:封装可互换的算法族
策略模式是一种行为设计模式,用于将算法族的不同实现封装在独立的类中,使它们可以相互替换而不影响客户端使用。
核心结构与角色
- Strategy(策略接口):定义算法的公共操作方法
- ConcreteStrategy(具体策略):实现不同版本的算法逻辑
- Context(上下文):持有策略实例并调用其方法执行业务
代码示例
type PaymentStrategy interface {
Pay(amount float64) string
}
type CreditCardStrategy struct{}
func (c *CreditCardStrategy) Pay(amount float64) string {
return fmt.Sprintf("Paid %.2f via Credit Card", amount)
}
type PayPalStrategy struct{}
func (p *PayPalStrategy) Pay(amount float64) string {
return fmt.Sprintf("Paid %.2f via PayPal", amount)
}
上述代码定义了支付策略接口及其实现。通过依赖抽象,上下文可在运行时动态切换支付方式,提升系统灵活性与可扩展性。
4.3 命令模式:将请求封装为独立对象
命令模式是一种行为设计模式,它将请求封装成对象,从而使请求的发送者和接收者解耦。通过将操作抽象为命令类,系统可以支持请求的排队、撤销、重做等高级功能。
核心结构
命令模式通常包含以下角色:
- Command:声明执行操作的接口
- ConcreteCommand:实现具体操作逻辑
- Invoker:触发命令执行的对象
- Receiver:真正执行操作的对象
代码示例
public interface Command {
void execute();
}
public class LightOnCommand implements Command {
private Light light;
public LightOnCommand(Light light) {
this.light = light;
}
@Override
public void execute() {
light.turnOn(); // 调用接收者的方法
}
}
上述代码定义了一个打开灯的命令。LightOnCommand 将“开灯”这一请求封装为对象,并在 execute 方法中调用接收者(Light)的具体行为,实现了调用与实现的分离。
4.4 状态模式:让对象行为随状态改变而变化
状态模式是一种行为型设计模式,允许对象在内部状态改变时切换其行为。通过将每个状态封装为独立类,避免了复杂的条件判断。
核心结构
状态模式包含上下文(Context)和具体状态类。上下文持有当前状态对象,并将状态相关的行为委托给该对象。
- State:定义状态接口
- ConcreteState:实现特定状态下的行为
- Context:维护状态实例并委托调用
代码示例
type State interface {
Handle(context *Context)
}
type ConcreteStateA struct{}
func (s *ConcreteStateA) Handle(context *Context) {
fmt.Println("处理状态A")
context.SetState(&ConcreteStateB{})
}
上述代码定义了状态接口及其实现。当 Context 调用 Handle 时,行为随当前状态自动切换,无需 if-else 判断。
第五章:从模式到架构的跃迁之路
在系统演进过程中,设计模式的积累最终会推动架构级别的重构。以某电商平台为例,初期采用单一服务封装订单、库存和支付逻辑,随着业务复杂度上升,团队逐步引入策略模式处理多种优惠计算方式,并使用观察者模式解耦订单状态变更通知。
模块化拆分与职责分离
当多个设计模式在同一服务中交织,维护成本显著上升。此时应识别核心域边界,将策略、模板方法等模式封装为独立微服务。例如:
// 优惠策略接口
type DiscountStrategy interface {
Calculate(amount float64) float64
}
// 满减策略实现
type OverAmountDiscount struct {
Threshold float64
Off float64
}
func (s *OverAmountDiscount) Calculate(amount float64) float64 {
if amount >= s.Threshold {
return amount - s.Off
}
return amount
}
服务治理与通信机制
拆分后需建立统一的服务注册与发现机制。以下为关键组件选型对比:
| 组件 | 用途 | 典型实现 |
|---|
| 服务注册中心 | 动态服务发现 | Consul, Nacos |
| API网关 | 路由与鉴权 | Kong, Spring Cloud Gateway |
| 配置中心 | 集中化配置管理 | etcd, Apollo |
事件驱动提升系统弹性
通过引入消息队列(如Kafka),将原本同步调用的用户行为日志记录转为异步处理,显著降低主流程延迟。结合CQRS模式,查询模型可独立扩展,支撑高并发报表场景。
- 定义领域事件:OrderCreated、PaymentConfirmed
- 使用Event Bus广播至订阅服务
- 消费者按需更新物化视图或触发工作流