什么是桥接模式? JavaScript 实现一个桥接模式的示例
·
什么是桥接模式?
桥接模式定义
将抽象部分与它的实现部分分离,使它们都可以独立地变化。核心是通过 “组合” 替代 “继承”,避免抽象与实现的紧耦合,让两者能各自扩展。
核心意图
- 解决多维度变化的问题:当一个类存在两个或多个独立变化的维度(如 “抽象类的变化” 和 “具体实现的变化”),通过桥接将维度分离,各自独立扩展。
- 例如:“形状”(抽象维度:圆形、方形)和 “颜色”(实现维度:红色、蓝色),桥接后可灵活组合(红圆、蓝方等),且新增形状或颜色无需修改原有代码。
结构
- 抽象化角色(Abstraction):定义抽象接口,包含一个对实现化角色的引用。
- 扩展抽象化角色(RefinedAbstraction):抽象化角色的子类,扩展抽象接口。
- 实现化角色(Implementor):定义实现接口,供抽象化角色调用。
- 具体实现化角色(ConcreteImplementor):实现实现化接口的具体类。
关键特点
- 强调 **“分离抽象与实现”,让两者独立变化,属于结构型模式 ** 中 “提前设计以应对未来变化” 的模式。
- 组合关系替代继承,减少类爆炸(若用继承,多维度组合会导致类数量呈指数增长)。
JavaScript代码实现
场景说明
- 抽象维度:形状(圆形、方形),负责定义形状的基本属性(如大小)和绘制逻辑的入口。
- 实现维度:渲染方式(Canvas 渲染、SVG 渲染),负责具体的绘制细节(如何用 Canvas 或 SVG 画图形)。
- 桥接关系:形状通过持有 “渲染方式” 的引用,将具体绘制操作委托给渲染实现,实现抽象与实现的分离。
// -------------- 实现化角色(Implementor):定义渲染方式的接口 --------------
class Renderer {
// 绘制圆形的抽象方法(参数:圆心x、y,半径r)
drawCircle(x, y, r) {
throw new Error("子类必须实现 drawCircle 方法");
}
// 绘制方形的抽象方法(参数:左上角x、y,边长size)
drawSquare(x, y, size) {
throw new Error("子类必须实现 drawSquare 方法");
}
}
// -------------- 具体实现化角色:Canvas 渲染 --------------
class CanvasRenderer extends Renderer {
constructor(canvasId) {
super();
this.canvas = document.getElementById(canvasId);
this.ctx = this.canvas.getContext("2d");
}
drawCircle(x, y, r) {
this.ctx.beginPath();
this.ctx.arc(x, y, r, 0, 2 * Math.PI);
this.ctx.strokeStyle = "red";
this.ctx.stroke();
console.log(`Canvas 绘制圆形:(${x},${y}),半径${r}`);
}
drawSquare(x, y, size) {
this.ctx.beginPath();
this.ctx.rect(x, y, size, size);
this.ctx.strokeStyle = "blue";
this.ctx.stroke();
console.log(`Canvas 绘制方形:(${x},${y}),边长${size}`);
}
}
// -------------- 具体实现化角色:SVG 渲染 --------------
class SVGRenderer extends Renderer {
constructor(svgId) {
super();
this.svg = document.getElementById(svgId);
}
drawCircle(x, y, r) {
const circle = document.createElementNS("http://www.w3.org/2000/svg", "circle");
circle.setAttribute("cx", x);
circle.setAttribute("cy", y);
circle.setAttribute("r", r);
circle.setAttribute("stroke", "green");
circle.setAttribute("fill", "transparent");
this.svg.appendChild(circle);
console.log(`SVG 绘制圆形:(${x},${y}),半径${r}`);
}
drawSquare(x, y, size) {
const rect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
rect.setAttribute("x", x);
rect.setAttribute("y", y);
rect.setAttribute("width", size);
rect.setAttribute("height", size);
rect.setAttribute("stroke", "purple");
rect.setAttribute("fill", "transparent");
this.svg.appendChild(rect);
console.log(`SVG 绘制方形:(${x},${y}),边长${size}`);
}
}
// -------------- 抽象化角色(Abstraction):形状 --------------
class Shape {
// 构造函数接收渲染器(桥接的关键:持有实现层的引用)
constructor(renderer) {
this.renderer = renderer; // 桥接点:形状依赖渲染器
}
// 绘制方法(由子类实现,最终委托给渲染器)
draw() {
throw new Error("子类必须实现 draw 方法");
}
}
// -------------- 扩展抽象化角色:圆形 --------------
class Circle extends Shape {
constructor(renderer, x, y, radius) {
super(renderer);
this.x = x;
this.y = y;
this.radius = radius;
}
// 调用渲染器的具体实现
draw() {
this.renderer.drawCircle(this.x, this.y, this.radius);
}
}
// -------------- 扩展抽象化角色:方形 --------------
class Square extends Shape {
constructor(renderer, x, y, size) {
super(renderer);
this.x = x;
this.y = y;
this.size = size;
}
// 调用渲染器的具体实现
draw() {
this.renderer.drawSquare(this.x, this.y, this.size);
}
}
// -------------- 测试代码 --------------
// 1. 创建两种渲染器(实现维度)
const canvasRenderer = new CanvasRenderer("myCanvas");
const svgRenderer = new SVGRenderer("mySvg");
// 2. 创建形状,并绑定不同的渲染器(桥接)
const circle1 = new Circle(canvasRenderer, 50, 50, 30); // 用 Canvas 画圆
const square1 = new Square(canvasRenderer, 100, 100, 40); // 用 Canvas 画方
const circle2 = new Circle(svgRenderer, 80, 80, 20); // 用 SVG 画圆
const square2 = new Square(svgRenderer, 150, 150, 30); // 用 SVG 画方
// 3. 绘制
circle1.draw();
square1.draw();
circle2.draw();
square2.draw();
代码说明
- 实现化角色(Renderer):定义了渲染圆形和方形的抽象方法,是所有渲染方式的接口。
- 具体实现(CanvasRenderer、SVGRenderer):分别实现了用 Canvas 和 SVG 绘制图形的细节,属于 “实现维度”,可独立扩展(如新增 WebGL 渲染器)。
- 抽象化角色(Shape):作为所有形状的基类,持有一个
renderer引用(桥接的核心),将绘制操作委托给渲染器。 - 扩展抽象(Circle、Square):继承自
Shape,定义具体形状的属性(如半径、边长),并通过draw方法调用渲染器的实现,属于 “抽象维度”,可独立扩展(如新增三角形)。
更多推荐

所有评论(0)