
主要关注对象的创建过程,旨在提供一种灵活、可复用的对象像创建机制,从而解耦对象的创建和使用。这类设计模式通过某种方式控制对象的创建,以适应不同的应用场景
主要关注类和对象的组合方式,以获得更大的结构。这类模式通过某种方式组合类或对象,从而形成更加灵活、强大的结构
主要关注对象之间的通信和交互方式,以实现更加灵活、可扩展的行为。这类模式通过某种方式定义对象之间的交互,从而实现特定的行为
一个类应该只有一个引起它变化的原因,也就是说:一个类应该只负责一项职责
软件实体(类、模块、函数等)应该对扩展开放,对修改关闭。也就是说应该通过扩展的方式来实现对原有类功能的扩展,而不要修改原有的代码
高层模块不应该依赖于低层模块,两者都应该依赖于抽象。抽象不应该依赖于细节,细节应该依赖于抽象。也就是说:应该通过抽象来解耦模块之间的依赖关系
客户端不应该依赖于它不需要的接口,也就是说:应该将大的接口拆分成小的、特定的接口,以避免客户端依赖不需要的方法
最少知道原则,一个对象应该对其他对象保持最少的了解。也就是说:一个对象应该只与其直接相关的对象进行交互,而不应该与间接相关的对象进行交互
子类应该可以替换其基类,而不影响程序的正确性。也就是说:子类应该遵循基类的契约,保证可以在不修改程序的情况下替换基类
确保一个类只有一个实例,并提供一个全局访问点来访问这个实例
Singleton(单例):定义一个Instance操作,允许客户端访问它的唯一实例
允许你分步骤构建复杂对象,允许你使用相同的构建代码生成不同类型和形式的对象
包含角色
工作流程
优点
使用场景
建造者模式与工厂模式的区别在于:建造者模式更加关注零件装配的顺序,而工厂模式更注重零件的创建
允许向一个现有的对象添加新的功能,同时又不改变其结构
核心思想是动态地给一个对象添加一些额外地职责,就增加功能来说:装饰模式相比生成子类更加灵活。装饰模式以对客户端透明地方式动态地给一个对象附加更多责任。换言之:客户端并不会觉得对象在装饰前和装饰后有什么不同。装饰模式可以在不需要创造更多子类的情况下,将对象地功能加以扩展
包含角色
工作流程
// 举例:向咖啡中加配料更新价格
// 抽象组件:饮料
class Beverage {
constructor() {
if (new.target === Beverage) {
throw new Error("Beverage is an abstract class.");
}
}
getDescription() {
throw new Error("Method 'getDescription()' must be implemented.");
}
cost() {
throw new Error("Method 'cost()' must be implemented.");
}
}
// 具体组件:浓缩咖啡
class Espresso extends Beverage {
getDescription() {
return "Espresso";
}
cost() {
return 1.99;
}
}
// 抽象装饰类:调料
class CondimentDecorator extends Beverage {
constructor(beverage) {
super();
if (new.target === CondimentDecorator) {
throw new Error("CondimentDecorator is an abstract class.");
}
this.beverage = beverage;
}
getDescription() {
return this.beverage.getDescription();
}
cost() {
return this.beverage.cost();
}
}
// 具体装饰类:摩卡
class Mocha extends CondimentDecorator {
getDescription() {
return `${this.beverage.getDescription()}, Mocha`;
}
cost() {
return this.beverage.cost() + 0.2;
}
}
class Milk extends CondimentDecorator {
getDescription() {
return `${this.beverage.getDescription()}, Milk`;
}
cost() {
return this.beverage.cost() + 0.1;
}
}
// 客户端代码
let beverage = new Espresso();
console.log(`${beverage.getDescription()} $${beverage.cost()}`);
beverage = new Mocha(beverage);
console.log(`${beverage.getDescription()} $${beverage.cost()}`);
beverage = new Milk(beverage);
console.log(`${beverage.getDescription()} $${beverage.cost()}`);优点
缺点
使用场景
实际例子
// 抽象组件:角色
class Character {
constructor(name) {
this.name = name;
}
getAttack() {
return 10;
}
getDefense() {
return 10;
}
getDescription() {
return `${this.name}`;
}
}
// 抽象装饰类: 装备
class EquipmentDecorator extends Character {
constructor(character) {
super();
this.character = character;
}
getAttack() {
return this.character.getAttack();
}
getDefense() {
return this.character.getDefense();
}
getDescription() {
return this.character.getDescription();
}
}
// 具体装饰类:武器
class WeaponDecorator extends EquipmentDecorator {
constructor(character, weaponName, attactBonus) {
super(character);
this.weaponName = weaponName;
this.attactBonus = attactBonus;
}
getAttack() {
return super.getAttack() + this.attactBonus;
}
getDescription() {
return `${super.getDescription()}, Weapon: ${this.weaponName}`
}
}
// 具体装饰类:盔甲
class ArmorDecorator extends EquipmentDecorator {
constructor(character, armorName, defenseBonus) {
super(character);
this.armorName = armorName;
this.defenseBonus = defenseBonus;
}
getDefense() {
return super.getDefense() + this.defenseBonus;
}
getDescription() {
return `${super.getDescription()}, Armor: ${this.armorName}`;
}
}
// 客户端代码
let character = new Character("Warrior");
console.log(character.getDescription());
console.log(`Attack: ${character.getAttack()}, Defense: ${character.getDefense()}`);
character = new WeaponDecorator(character, "Sword", 5);
console.log(character.getDescription());
console.log(`Attack: ${character.getAttack()}, Defense: ${character.getDefense()}`);
character = new ArmorDecorator(character, "Shield", 3);
console.log(character.getDescription());
console.log(`Attack: ${character.getAttack()}, Defense: ${character.getDefense()}`);适配器模式允许一个类接口转换成客户端所期望地另一种接口,从而使原本由于接口不兼容而无法一起工作的类能一起工作
核心思想是将一个类的接口包装在一个新的适配器类中,从而使其与其他类兼容。适配器模式分为类适配器和对象适配器;类适配器通过继承实现,对象适配器通过组合实现
包含角色
工作流程
// 目标接口:新的温度计接口
class NewThermometer {
constructor() {
console.log('target', new.target)
if (new.target === NewThermometer) {
throw new Error("NewThermometer cannot be instantiated directly");
}
}
getTemperature () {
throw new Error("Method 'getTemperature()' must be implemented.");
}
}
// 适配者类 旧的温度计
class OldThermometer {
constructor() {
this.themperature = 0;
}
setTemperture(temperature) {
this.temperature = temperature;
}
getTemperatureInFahrenheit() {
return this.temperature;
}
}
// 适配器:将旧温度计适配到新温度计接口
class ThermometerAdapter extends NewThermometer {
constructor(oldThermometer) {
super()
this.oldThermometer = oldThermometer;
}
getTemperature() {
const temperatureInFahrenheit = this.oldThermometer.getTemperatureInFahrenheit();
const temperatureInCelsius = (temperatureInFahrenheit - 32) * 5 / 9;
return temperatureInCelsius;
}
}
// 客户端代码
function clientCode (thermometer) {
console.log(`Temperature: ${thermometer.getTemperature()}°C`);
}
const oldThermometer = new OldThermometer();
oldThermometer.setTemperture(98.6);
const newThermometer = new ThermometerAdapter(oldThermometer);
clientCode(newThermometer);
// 在这个例子中,NewThermometer是目标接口,定义了新的温度计应该具有的接口。OldThermometer是适配者类,表示旧的温度计,它使用华氏度表示温度。ThermometerAdapter是适配器,它包装了一个OldThermometer对象,并将其接口转换为NewThermometer接口,将华氏度转换为摄氏度优点
缺点
使用场景
组合模式是一种结构型设计模式,它允许你将对象组合成树形结构来表现 “部分-整体” 的层次关系。组合模式使得客户端对单个对象和组合对象的使用具有一致性
组合模式的核心思想是定义一个抽象的组件接口,让单个对象和组合对象都实现这个接口。这样,客户端就可以一致的对待单个对象和组合对象,而不必关心他们的具体类型
包含角色
实现方式
工作流程
// 下面是一个使用JavaScript实现的组合模式的例子,演示了如何使用组合模式来表示文件系统中的目录和文件
class FileSystemItem {
constructor(name) {
this.name = name;
}
display() {
throw new Error("Method 'display()' must be implemented.");
}
}
// 叶子:文件
class File extends FileSystemItem {
contructor(name) {
super(name);
}
display() {
console.log(`Displaying file ${this.name}`);
}
}
// 组合:目录
class Directory extends FileSystemItem {
constructor(name) {
super(name);
this.items = [];
}
add(item) {
this.items.push(item);
}
remove(item) {
const index = this.items.indexOf(item);
if (index !== -1) {
this.items.splice(index, 1);
}
}
display() {
console.log(`Directory: ${this.name}`);
for (const item of this.items) {
item.display();
}
}
}
// 客户端代码
function clientCode () {
const root = new Directory("root");
const dir1 = new Directory("dir1");
const dir2 = new Directory("dir2");
const file1 = new File("file1.txt");
const file2 = new File("file2.txt");
const file3 = new File("file3.txt");
root.add(dir1);
root.add(dir2);
root.add(file1);
dir1.add(file2);
dir2.add(file3);
root.display();
}
clientCode();优点
缺点
使用场景
桥接模式是一个结构性设计模式,它将抽象部分和实现部分分离,使他们都可以独立地变化。这种分离可以在程序运行时刻实现,也可以在编译时实现。桥接模式通过提供抽象化和现实化之间地桥接结构,来实现二者地解耦
包含角色
工作流程
// 下面是一个简单的例子,演示了一个绘图应用,它可以使用不同的渲染器(如SVG、Canvas)来绘制不同的形状(如圆形、矩形)。
// 实现化角色
class Renderer {
constructor() {
if (new.target === Renderer) {
throw new Error("Cannot instantiate abstract class.");
}
}
renderCircle(radius) {
}
renderRect(width, height) {
}
}
// 具体化实现角色:SVG渲染器
class SVGRenderer extends Renderer {
renderCircle(radius) {
console.log(`Rendering a circle with radius ${radius} using SVG.`);
}
renderRect(width, height) {
console.log(`Rendering a rectangle with width ${width} and height ${height} using SVG.`);
}
}
// 具体实现化角色:Canvas渲染器
class CanvasRenderer extends Renderer {
renderCircle(radius) {
console.log(`Rendering a circle with radius ${radius} using Canvas.`);
}
renderRect(width, height) {
console.log(`Rendering a rectangle with width ${width} and height ${height} using Canvas.`);
}
}
// 抽象化角色:形状
class Shape {
constructor(renderer) {
this.renderer = renderer
}
}
// 修正抽象化角色:圆形
class Circle extends Shape {
constructor(renderer, radius) {
super(renderer);
this.radius = radius;
}
draw() {
this.renderer.renderCircle(this.radius);
}
}
// 修正抽象化角色:矩形
class Rect extends Shape {
constructor(renderer, width, height) {
super(renderer);
this.width = width;
this.height = height;
}
draw() {
this.renderer.renderRect(this.width, this.height);
}
}
// 客户端代码
function clientCode () {
const svgRenderer = new SVGRenderer();
const canvasRenderer = new CanvasRenderer();
const svgCircle = new Circle(svgRenderer, 100);
const svgRect = new Rect(svgRenderer, 100, 200);
const canvasCircle = new Circle(canvasRenderer, 100);
const canvasRect = new Rect(canvasRenderer, 100, 200);
svgCircle.draw();
svgRect.draw();
canvasCircle.draw();
canvasRect.draw();
}
clientCode();
// Renderer是实现化角色,定义了渲染器的接口。
// SVGRenderer和CanvasRenderer是具体实现化角色,实现了具体的渲染方法。
// Shape是抽象化角色,持有一个渲染器的引用。
// Circle和Rect是修正抽象化角色,继承自Shape,并定义了具体的绘制方法。
// Rendering a circle with radius 5 using SVG.
// Rendering a rectangle with width 10 and height 20 using SVG.
// Rendering a circleRendering a circle with radius 8 using Canvas.
// Rendering a rectangle with width 15 and height 30 using Canvas.
// 如果我们想要添加一个新的渲染器(如WebGL渲染器),只需要创建一个新的WebGLRenderer类,实现Renderer接口,而不需要修改任何形状的代码
class WebGLRenderer extends Renderer {
renderCircle(radius) {
console.log(`Rendering a circle with radius ${radius} using WebGL.`);
}
renderRect(width, height) {
console.log(`Rendering a rectangle with width ${width} and height ${height} using WebGL.`);
}
}
// 客户端代码
const webglRenderer = new WebGLRenderer();
const webglCircle = new Circle(webglRenderer, 6);
const webglRect = new Rect(webglRenderer, 12, 24);
webglCircle.draw();
webglRect.draw();
// 同样地,如果我们想要添加一个新的形状(如三角形),只需要创建一个新的Triangle类,继承自Shape,并实现draw()方法,而不需要修改任何渲染器的代码
class Triangle extends Shape {
constructor(renderer, base, height) {
super(renderer);
this.base = base;
this.height = height;
}
draw() {
console.log(`Rendering a triangle with base ${this.base} and height ${this.height}.`);
// 使用渲染器绘制三角形...
}
}优点
缺点
享元模式主要目标是使用共享技术有效地支持大量细粒度地对象,这种模式可以帮助节省内存,特别是在处理大量相似对象的情况下
主要思想是将一个对象的状态分为内部状态和外部状态,内部状态是对象共享出来的信息,存储在享元对象内部并且不会随着环境改变而改变;外部状态是对象得以依赖的一个标记,是随环境改变而改变的、不可以共享的状态
示例
它为复杂的子系统提供一个简化的接口,使得客户端可以更方便的使用这些子系统
外观模式提供了一种简化复杂子系统访问的方法,它在客户端和子系统之间引入了一个外观对象,将客户端的请求委托给适当的子系统对象。这样可以简化客户端的代码,并将客户端与子系统解耦
包含角色
工作流程
// 子系统类
// DVD播放
class DVDPlayer {
on () {
console.log('DVD Player is on');
}
play (movie) {
console.log(`Playing movie: ${movie}`);
}
stop () {
console.log('DVD player stopped');
}
off () {
console.log('DVD player is off');
}
}
// 投影
class Projector {
on () {
console.log('Projector is on');
}
wideScreenMode () {
console.log('Projector in widescreen mode');
}
off () {
console.log('Projector is off');
}
}
// 环绕立体声
class SurroundSoundSystem {
on() {
console.log('Surround sound system is on');
}
setVolume(volume) {
console.log(`Setting volume to ${volume}`);
}
off() {
console.log('Surround sound system is off');
}
}
// 外观类
class HomeTheaterFacade {
constructor(devplayer, projector, soundSystem) {
this.dvdPlayer = devplayer;
this.projector = projector;
this.soundSystem = soundSystem;
}
watchMovie(movie) {
console.log('Get ready to watch a movie...');
this.dvdPlayer.on();
this.projector.on();
this.projector.wideScreenMode();
this.soundSystem.on();
this.soundSystem.setVolume(5);
this.dvdPlayer.play(movie);
}
endMove () {
console.log('Shutting movie theater down...');
this.dvdPlayer.stop();
this.dvdPlayer.off();
this.projector.off();
this.soundSystem.off();
}
}
// 客户端代码
const devPlayer = new DVDPlayer();
const projector = new Projector();
const soundSystem = new SurroundSoundSystem();
const homeTheater = new HomeTheaterFacade(devPlayer, projector, soundSystem);
homeTheater.watchMovie('Titanic');
homeTheater.endMove();优点
缺点和限制
使用场景
为另一个对象提供一个替身或占位符以控制对这个对象的访问。代理对象在客户端和目标对象之间起到中介作用,它可以在目标对象被访问之前或之后增加一些额外的处理
包含角色
工作流程
// 抽象主题
class Image {
display () {}
}
// 真实主题
class RealImage extends Image {
constructor(fileName) {
super()
this.fileName = fileName
this.loadFromDisk(fileName)
}
display () {
console.log(`Displaying ${this.fileName}`);
}
loadFromDisk () {
console.log(`Loading ${this.fileName}`);
}
}
// 代理类
class ProxyImage extends Image {
constructor(fileName) {
super()
this.fileName = fileName
this.realImage = null
}
display () {
if (!this.realImage) {
this.realImage = new RealImage(this.fileName)
}
this.realImage.display()
}
}
// 客户端
const image = new ProxyImage('test_10mb.jpg')
// 第一次调用 display 方法,将触发真实图像的加载
image.display();
// 第二次调用 display 方法,不会触发真实图像的加载,因为它已经被加载过了
image.display();使用场景
缺点
定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象,这个主题对象在状态发生变化时,会通知所有观察者对象,使他们能够主动更新自己
核心思想:当一个对象的状态发生变化时,所有依赖它的对象都得到通知并被自动更新。这种交互也称为 发布-订阅模式。观察者模式可以实现表示层和数据逻辑层的分离,并在观察目标和观察者之间建立一个抽象的耦合,观察者支持广播通信,观察目标会向所有登记过的观察者发出通知
包含角色
工作流程
// 天气数据和显示板的交互
// 主题:天气数据
class WeatherData {
constructor() {
this.observers = [];
this.temperature = null;
this.humidity = null;
this.pressure = null;
}
registerObserver(observer) {
this.observer.push(observer);
}
removeObserver(observer) {
const index = this.observers.indexOf(observer);
if (index !== -1) {
this.observers.splice(index, 1);
}
}
notifyObservers() {
this.observers.forEach(observer => observer.update())
}
measurementsChanged() {
this.notifyObservers();
}
setMeasurements(temperature, humidity, pressure) {
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
this.measuredChanged();
}
getTemperature() {
return this.temperature;
}
getHumidity() {
return this.humidity;
}
getPressure() {
return this.pressure;
}
}
// 观察者:显示面板
class DisplayElement {
constructor(weatherData) {
this.weatherData = weatherData;
this.temperature = null;
this.humidity = null;
this.pressure = null;
this.weatherData.registerObserver(this);
}
update() {
this.temperature = this.weatherData.getTemperature();
this.humidity = this.weatherData.getHumidity();
this.pressure = this.weatherData.getPressure();
this.display();
}
display() {
console.log(`Temperature: ${this.temperature}°C, Humidity: ${this.humidity}%, Pressure: ${this.pressure}hPa`);
}
}
// 客户端// 客户端代码
const weatherData = new WeatherData();
const displayElement1 = new DisplayElement(weatherData);
const displayElement2 = new DisplayElement(weatherData);
weatherData.setMeasurements(25, 65, 1013);
weatherData.setMeasurements(26, 70, 1015);
weatherData.removeObserver(displayElement2);
weatherData.setMeasurements(27, 75, 1020);
// Temperature: 25°C, Humidity: 65%, Pressure: 1013hPa
// Temperature: 25°C, Humidity: 65%, Pressure: 1013hPa
// Temperature: 26°C, Humidity: 70%, Pressure: 1015hPa
// Temperature: 26°C, Humidity: 70%, Pressure: 1015hPa
// Temperature: 27°C, Humidity: 75%, Pressure: 1020hPa优点
缺点
使用场景
实例
// 使用观察者模式实现简单的事件管理的例子
// 主题:事件管理器
class EventManager {
constructor() {
this.listeners = new Map();
}
subscribe(eventType, listener) {
if (!this.listeners.has(eventType)) {
this.listeners.set(eventType, []);
}
this.listeners.get(eventType).push(listener);
}
unsubscribe(eventType, listener) {
if (this.listeners.has(eventType)) {
const index = this.listeners.get(eventType).indexOf(listener);
if (index !== -1) {
this.listeners.get(eventType).splice(index, 1);
}
}
}
notify (eventType, data) {
if (this.listeners.has(eventType)) {
this.listeners.get(eventType).forEach(listener => listener(data));
}
}
}
// 观察者:事件监听器
class EventListener {
constructor(name) {
this.name = name;
}
handleEvent(data) {
console.log(`${this.name} received: ${data}`);
}
}
// 客户端代码
const eventManager = new EventManager();
const listener1 = new EventListener("Listener 1");
const listener2 = new EventListener("Listener 2");
eventManager.subscribe("eventA", listener1.handleEvent.bind(listener1));
eventManager.subscribe("eventA", listener2.handleEvent.bind(listener2));
eventManager.subscribe("eventB", listener1.handleEvent.bind(listener1));
eventManager.notify("eventA", "Event A occurred!");
eventManager.notify("eventB", "Event B occurred!");
eventManager.unsubscribe("eventA", listener2.handleEvent);
eventManager.notify("eventA", "Event A occurred again!");
// Listener 1 received: Event A occurred!Listener 2 received: Event A occurred!
// Listener 1 received: Event B occurred!
// Listener 1 received: Event A occurred again!它允许在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便在需要时可以将对象恢复到先前的状态
包含角色
工作流程
优点
使用场景
适用于需要保存和恢复数据的相关状态场景,比如撤销操作、事务管理、游戏存档等。但是如果类的成员变量过多,会占用比较大的资源,而且每一次保存都会消耗一定的内存,这时需要注意程序的性能问题
它将请求封装成对象,以便使用不同的请求、队列或者日志来参数化其他对象。命令模式也支持可撤销的操作
包含角色
工作流程
// 接收者
class Receiver {
execute() {
console.log('Executing a request.');
}
}
// 抽象命令类
class Command {
constructor(receiver) {
this.receiver = receiver;
}
execute() {}
}
// 具体命令类
class ConcreteCommand extends Command {
execute() {
console.log('ConcreteCommand: Calling receiver.');
this.receiver.execute();
}
}
// 调用者
class Invoker {
setCommand(command) {
this.command = command;
}
executeCommand() {
console.log('Invoker: Calling command.');
this.command.execute();
}
}
// 客户端
const receiver = new Receiver();
const command = new ConcreteCommand(receiver);
const invoker = new Invoker();
invoker.setCommand(command);
invoker.executeCommand();优点
使用场景
命令模式将调用操作的对象与知道如何实现该操作的对象解耦,使得调用者和接收者之间没有直接引用,调用者与接收者之间通过命令对象进行交互
允许一个对象在其内部状态改变时改变时改变它的行为,看起来似乎修改了它的类。在状态模式中,行为是通过状态来定义的,也就是说,每一个状态定义了一组相关的行为
包含角色
工作流程
优点
使用场景
通过引入一个中介者对象来简化对象之间的通信或交互,这种模式用于系统内部的对象之间的通信,使得这些对象不需要显式的相互引用,从而降低它们之间的耦合度
包含角色
工作流程
优点
使用场景
它在一个方法中定义了一个算法的骨架,将一些步骤推迟到子类中。模板方法允许子类在不改变算法结构的情况下重写算法的特定步骤
包含角色
工作流程
class AbstractClass {
templateMethod() {
this.baseOperation1();
this.requiredOperations1();
this.baseOperation2();
this.hook1();
this.requiredOperation2();
this.baseOperation3();
this.hook2();
}
baseOperation1() {
console.log('AbstractClass says: I am doing the bulk of the work');
}
baseOperation2() {
console.log(
'AbstractClass says: But I let subclasses override some operations'
);
}
baseOperation3() {
console.log(
'AbstractClass says: But I am doing the bulk of the work anyway'
);
}
requiredOperations1() {}
requiredOperation2() {}
hook1() {}
hook2() {}
}
class ConcreteClass1 extends AbstractClass {
requiredOperations1() {
console.log('ConcreteClass1 says: Implemented Operation1');
}
requiredOperation2() {
console.log('ConcreteClass1 says: Implemented Operation2');
}
}
class ConcreteClass2 extends AbstractClass {
requiredOperations1() {
console.log('ConcreteClass2 says: Implemented Operation1');
}
requiredOperation2() {
console.log('ConcreteClass2 says: Implemented Operation2');
}
hook1() {
console.log('ConcreteClass2 says: Overridden Hook1');
}
}
console.log('Same client code can work with different subclasses:');
clientCode(new ConcreteClass1());
console.log('');
console.log('Same client code can work with different subclasses:');
clientCode(new ConcreteClass2());
function clientCode(abstractClass) {
abstractClass.templateMethod();
}
// 在这个例子中,AbstractClass 是抽象父类,它定义了一个名为 templateMethod 的模板方法。这个模板方法由一系列的方法组成,其中 requiredOperations1 和 requiredOperation2 是抽象的,需要子类 ConcreteClass1 和 ConcreteClass2 来提供具体的实现
// hook1 和 hook2 是钩子方法,子类可以选择是否覆盖它们优点
使用场景
定义了一系列算法,将每个算法封装起来,并且使它们可以相互替换。策略模式让算法可以独立于使用它的客户端而变化
策略模式提供了一种更清晰、更灵活的方式来组织代码。它将算法的实现和使用分离开来,使得代码更容易理解和维护。同时,它也遵循了开闭原则,你可以引入新的策略而无需修改现有的代码
包含角色
工作流程
// 抽象策略类
class CalculationStrategy {
calculate(price) {}
}
// 具体策略类
class NormalStrategy extends CalculationStrategy {
calculate(price) {
return price;
}
}
// 具体策略类
class DiscountStrategy extends CalculationStrategy {
calculate(price) {
return price * 0.8;
}
}
// 上下文
class PriceContext {
constructor(strategy) {
this.strategy = strategy;
}
setStrategy(strategy) {
this.strategy = strategy;
}
calculate(price) {
return this.strategy.calculate(price);
}
}
// 客户端
const priceContext = new PriceContext(new NormalStrategy());
console.log(priceContext.calculate(100)); // 100
priceContext.setStrategy(new DiscountStrategy());
console.log(priceContext.calculate(100)); // 80
// 在这个例子中,CalculationStrategy是抽象策略类,它定义了计算价格的接口。NormalStrategy和DiscountStrategy是具体策略类,分别实现了普通计算和打折计算
// PriceContext是上下文,它维护对策略对象的引用,并提供了一个setStrategy方法来动态地改变策略
// 在客户端代码中,我们首先创建了一个使用NormalStrategy的PriceContext,并计算价格,输出为100。然后,我们将策略改为DiscountStrategy,再次计算价格,这次输出为80
// 这个例子展示了策略模式如何让算法可以独立于使用它的客户端而变化。客户端可以在运行时根据需要切换算法,而不需要修改使用算法的代码优点
使用场景
包含角色
工作流程
优点
使用场景