上篇我们介绍了 设计模式之创建型模式篇,接下来介绍设计模式之结构型模式篇
适配器模式
适配器模式旨在解决接口不兼容的问题,它通过创建一个适配器类,将源对象的接口转换成目标接口,从而使得不兼容的接口能够协同工作。简单来说,适配器模式的目标就是将一个接口转化成客户端所期望的接口,通过这种方式,现有的类(或者第三方类)可以在不修改的情况下被重用
它的使用场景主要有以下几个:
1)需要使用现有类,但其接口不符合系统需求
2)希望创建一个可复用的类,与多个不相关的类一起工作,这些类可能没有统一的接口
3)通过接口转换,将一个类集成到另一个类系中
适配器模式包含以下几个主要角色:
1)目标接口
定义客户需要的接口
2)适配者类
定义一个已经存在的接口,这个接口需要适配
3)适配器类
实现目标接口,并通过组合或继承的方式调用适配者类中的方法,从而实现目标接口
通过以下这个例子来理解适配器模式,假设有一个支付系统,系统中既有旧的支付接口OldPaymentSystem
,也有新的支付接口NewPaymentSystem
,我们希望使用一个统一的接口来调用这两个不同的支付系统
1)定义目标接口
目标接口是客户端期望使用的接口,所有支付系统都应当提供一个pay()
方法
// 目标接口(Target)
class PaymentGateway {
pay(amount) {
throw new Error("该方法需要被重写")
}
}
2)定义源接口
源接口是现有的不兼容接口,假设现有的支付系统使用的是OldPaymentSystem
,它提供了一个makePayment()
方法,而不是pay()
// 源接口:旧支付系统(Adaptee)
class OldPaymentSystem {
makePayment(amount) {
console.log(`使用旧支付系统支付:${amount} 元`)
}
}
3)创建适配器
适配器的作用是将OldPaymentSystem
的接口适配到PaymentGateway
接口,使得它们能够通过相同的接口来使用
// 适配器:将旧支付系统适配为目标接口
class OldPaymentAdapter extends PaymentGateway {
constructor(oldPaymentSystem) {
super()
this.oldPaymentSystem = oldPaymentSystem
}
// 实现目标接口的 pay 方法
pay(amount) {
this.oldPaymentSystem.makePayment(amount) // 调用旧支付系统的方法
}
}
4)创建新支付系统
新支付系统与目标接口兼容,直接实现pay()
方法
// 新支付系统(NewPaymentSystem)
class NewPaymentSystem extends PaymentGateway {
pay(amount) {
console.log(`使用新支付系统支付:${amount} 元`)
}
}
5)使用适配器模式
客户端只依赖于PaymentGateway
接口来进行支付,而不关心支付系统的实现,无论使用的是旧的支付系统还是新的支付系统,客户端代码都会通过统一的接口pay()
来进行支付
// 客户端代码:使用支付网关接口
function processPayment(paymentSystem, amount) {
paymentSystem.pay(amount) // 使用支付系统进行支付
}
// 客户端代码使用旧支付系统
const oldPaymentSystem = new OldPaymentSystem()
const adaptedOldPaymentSystem = new OldPaymentAdapter(oldPaymentSystem) // 使用适配器
processPayment(adaptedOldPaymentSystem, 100)
// 客户端代码使用新支付系统
const newPaymentSystem = new NewPaymentSystem()
processPayment(newPaymentSystem, 200)
执行代码,运行结果如下:
桥接模式
桥接模式通过将一个对象的抽象部分与它的实现部分分离,使它们可以独立变化,通过组合的方式,而不是继承的方式,将抽象和实现的部分连接起来
它的使用场景主要有以下:
1)当需要抽象和实现分离,且两者独立变化时
2)当系统不希望固定抽象类和实现类的绑定时
3)当多个类的不同实现有类似的功能时
桥接模式包含以下几个主要角色:
1)抽象类
定义抽象接口,通常包含对实现接口的引用
2)扩展抽象类
对抽象的扩展,可以是抽象的子类或具体实现类
3)实现类
定义具体实现类必须遵循的接口,通常包含一些基本操作
4)具体实现
实现实现接口的具体类
通过以下绘制图形的例子来展示桥接模式,图形有不同的形状(如圆形和矩形),而每个图形可以有不同的绘制方式
1)定义实现接口
定义一个接口DrawingAPI
,它包含绘制图形的基本方法
// 实现类接口:绘制API
class DrawingAPI {
drawCircle(radius, x, y) {
throw new Error("该方法需要被重写")
}
drawRectangle(width, height, x, y) {
throw new Error("该方法需要被重写")
}
}
2)创建具体实现类
为DrawingAPI
接口创建几个具体实现类,例如,一个实现类使用Canvas
来绘制图形,另一个实现类使用SVG
来绘制
// 具体实现类:使用 Canvas 绘制图形
class CanvasDrawingAPI extends DrawingAPI {
drawCircle(radius, x, y) {
console.log(`在 Canvas 上绘制圆形,半径:${radius}, 位置:(${x}, ${y})`)
}
drawRectangle(width, height, x, y) {
console.log(`在 Canvas 上绘制矩形,宽度:${width}, 高度:${height}, 位置:(${x}, ${y})`)
}
}
// 具体实现类:使用 SVG 绘制图形
class SVGDrawingAPI extends DrawingAPI {
drawCircle(radius, x, y) {
console.log(`在 SVG 上绘制圆形,半径:${radius}, 位置:(${x}, ${y})`)
}
drawRectangle(width, height, x, y) {
console.log(`在 SVG 上绘制矩形,宽度:${width}, 高度:${height}, 位置:(${x}, ${y})`)
}
}
3)定义抽象类