相关文章
设计模式 创建型
一、适配器模式
适配器模式(Adapter Pattern)是一种结构型设计模式,用于将一个类的接口转换成客户端所期望的另一个接口,以解决不兼容接口之间的问题。适配器模式允许不同接口的类协同工作,而无需修改其原始代码。
适配器模式通常涉及三个角色:目标接口(Target Interface)、适配器(Adapter)和被适配者(Adaptee)。
- 目标接口(Target Interface) 定义了客户端期望的接口,适配器模式将被适配者的接口转换成目标接口。
- 适配器(Adapter) 实现了目标接口,并且持有一个被适配者的实例,负责将目标接口的方法委派给被适配者。
- 被适配者(Adaptee) 是需要被转换成目标接口的类,其接口与目标接口不兼容。
下面是适配器模式的一个示例,假设我们有一个旧系统中的类 LegacyPrinter,它有一个不兼容的 print 方法,而我们希望将其适配成一个符合新系统中 Printer 接口的类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| class LegacyPrinter { constructor() { this.printMessage = function(message) { console.log("Legacy Printer: " + message); }; } }
class Printer { constructor() { this.print = function(message) {}; } }
class LegacyPrinterAdapter extends Printer { constructor() { super(); this.legacyPrinter = new LegacyPrinter(); this.print = function(message) { this.legacyPrinter.printMessage(message); }; } }
const printer = new LegacyPrinterAdapter(); printer.print("Hello, World!");
|
输出
1
| Legacy Printer: Hello, World!
|
二、装饰器模式
装饰器模式(Decorator Pattern)是一种结构型设计模式,允许向对象动态地添加新功能,同时不改变其原始类的结构。该模式通过将对象封装在一个装饰器对象中,以及将装饰器对象链式地组合起来,实现功能的动态添加和组合。
装饰器模式通常涉及以下角色:
- 组件(Component): 定义一个抽象接口,可以是一个抽象类或接口,也可以是一个具体类。
- 具体组件(Concrete Component): 实现了组件接口的具体类,是被装饰的对象。
- 装饰器(Decorator): 继承了组件接口,并持有一个对组件的引用,其本身也实现了组件接口。装饰器可以在调用被装饰对象的同时,增加新的功能。
下面是一个简单的装饰器模式的示例,假设我们有一个咖啡店,可以制作各种咖啡,我们想要为咖啡添加不同的调料,比如牛奶、糖浆或者摩卡:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
| class CoffeeDecorator { constructor(coffee) { this.coffee = coffee; this.descriptionValue = ""; }
cost() { return this.coffee.cost(); }
description() { return this.descriptionValue; } }
class MilkDecorator extends CoffeeDecorator { constructor(coffee) { super(coffee); this.costAddition = 0.5; this.descriptionValue = "Milk"; }
cost() { return this.coffee.cost() + this.costAddition; } }
class SyrupDecorator extends CoffeeDecorator { constructor(coffee) { super(coffee); this.costAddition = 0.3; this.descriptionValue = "Syrup"; }
cost() { return this.coffee.cost() + this.costAddition; } }
class MochaDecorator extends CoffeeDecorator { constructor(coffee) { super(coffee); this.costAddition = 0.7; this.descriptionValue = "Mocha"; }
cost() { return this.coffee.cost() + this.costAddition; } }
let coffee = new Coffee(); console.log(coffee.description() + ": $" + coffee.cost());
coffee = new MilkDecorator(coffee); console.log(coffee.description() + ": $" + coffee.cost());
coffee = new SyrupDecorator(coffee); console.log(coffee.description() + ": $" + coffee.cost());
coffee = new MochaDecorator(coffee); console.log(coffee.description() + ": $" + coffee.cost());
|
输出
1 2 3 4
| Coffee: $1 Milk: $1.5 Syrup: $1.8 Mocha: $2.5
|
三、代理模式
代理模式(Proxy Pattern)是一种结构型设计模式,允许在访问对象时引入一定程度的间接性,以控制对对象的访问。
代理模式通常涉及以下角色:
- 主题(Subject): 定义了真实对象和代理对象的共同接口,客户端可以通过这个接口访问真实对象或代理对象。
- 真实对象(Real Subject): 实现了主题接口,是被代理的对象,代理模式的最终目的是要访问和控制这个对象。
- 代理对象(Proxy): 实现了主题接口
以下是一个简单的代理模式的示例,假设我们有一个图片接口 Image,包含加载图片和显示图片的方法,以及一个真实的图片类 RealImage 和一个代理类 ProxyImage:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| class Image { display() {} }
class RealImage extends Image { constructor(filename) { super(); this.filename = filename; this.loadFromDisk(); }
display() { console.log("Displaying " + this.filename); }
loadFromDisk() { console.log("Loading " + this.filename + " from disk"); } }
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("image.jpg");
image.display();
|
输出
1 2
| Loading image.jpg from disk Displaying image.jpg
|
四、组合模式
组合模式(Composite Pattern)是一种结构型设计模式,它允许你将对象组合成树形结构以表示“部分-整体”的层次结构。组合模式使得客户端可以统一地处理单个对象和组合对象,而不需要区分它们的类型。
在组合模式中,通常会有以下角色:
- 组件(Component): 定义了统一的接口,可以是抽象类或接口,用于表示组合中的所有对象,包括叶子节点和容器节点。
- 叶子节点(Leaf): 表示组合中的叶子对象,它没有子对象,实现了组件接口。
- 容器节点(Composite): 表示组合中的容器对象,可以包含叶子节点和其他容器节点,实现了组件接口,并维护了一个子组件列表。
组合模式的核心思想是通过统一的接口来处理组合中的所有对象,这样客户端就可以像处理单个对象一样来处理整个树形结构。
让我们通过一个简单的文件系统的例子来说明组合模式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
| class Component { constructor(name) { this.name = name; }
display() {} }
class File extends Component { display() { console.log("File: " + this.name); } }
class Folder extends Component { constructor(name) { super(name); this.children = []; }
add(component) { this.children.push(component); }
remove(component) { const index = this.children.indexOf(component); if (index !== -1) { this.children.splice(index, 1); } }
display() { console.log("Folder: " + this.name); this.children.forEach(child => child.display()); } }
const root = new Folder("Root");
const folder1 = new Folder("Folder 1"); folder1.add(new File("File 1-1")); folder1.add(new File("File 1-2"));
const folder2 = new Folder("Folder 2"); folder2.add(new File("File 2-1")); folder2.add(new File("File 2-2"));
root.add(folder1); root.add(folder2); root.add(new File("File 3"));
root.display();
|
输出
1 2 3 4 5 6 7 8
| Folder: Root Folder: Folder 1 File: File 1-1 File: File 1-2 Folder: Folder 2 File: File 2-1 File: File 2-2 File: File 3
|
五、桥接模式
桥接模式(Bridge Pattern)是一种结构型设计模式,它将抽象部分与实现部分分离,使它们可以独立地变化。桥接模式通过将抽象和实现分离,使得它们可以独立地进行变化和扩展,而不会相互影响。
在桥接模式中,通常会有两个维度的抽象:
- 抽象部分(Abstraction): 它定义了抽象的接口和行为,通常会包含一个指向实现部分的引用,这样抽象部分就可以调用实现部分的具体功能。
- 实现部分(Implementor): 它定义了实现的接口和行为,抽象部分的引用可以将抽象部分的请求委派给实现部分进行处理。
假设我们要设计一个跨平台的图形界面,支持不同的操作系统(例如 Windows 和 MacOS)和不同的图形控件(例如按钮、文本框等)。我们希望能够灵活地组合不同的操作系统和图形控件,而不需要修改已有的代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| class UIControl { constructor(platform) { this.platform = platform; }
draw() { this.platform.drawUIControl(); } }
class WindowsUIControl { drawUIControl() { console.log("Drawing Windows UI Control"); } }
class MacOSUIControl { drawUIControl() { console.log("Drawing MacOS UI Control"); } }
const windowsUIControl = new UIControl(new WindowsUIControl()); windowsUIControl.draw();
const macOSUIControl = new UIControl(new MacOSUIControl()); macOSUIControl.draw();
|
输出
1 2
| Drawing Windows UI Control Drawing MacOS UI Control
|
六、外观模式
外观模式(Facade Pattern)是一种结构型设计模式,它提供了一个统一的接口,用来访问子系统中的一群接口。外观模式定义了一个高层接口,使得客户端可以简化与复杂系统的交互,而不需要了解系统内部的具体实现细节。
假设我们正在开发一个音响系统,它包含了多个子系统,如播放器、音量控制器、功放等。每个子系统都有自己的接口和实现,而客户端需要与这些子系统进行交互。在没有外观模式的情况下,客户端需要了解每个子系统的接口和调用方式,这样会增加客户端的复杂性。而通过外观模式,我们可以提供一个简单的接口给客户端使用,使得客户端可以轻松地与整个音响系统进行交互。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
| class Player { play() { console.log("Playing..."); }
pause() { console.log("Pausing..."); }
stop() { console.log("Stopping..."); } }
class VolumeController { mute() { console.log("Muting..."); }
setVolume(volume) { console.log("Setting volume to", volume); } }
class Amplifier { powerOn() { console.log("Powering on..."); }
powerOff() { console.log("Powering off..."); } }
class AudioFacade { constructor() { this.player = new Player(); this.volumeController = new VolumeController(); this.amplifier = new Amplifier(); }
playMusic() { this.amplifier.powerOn(); this.player.play(); }
pauseMusic() { this.player.pause(); }
stopMusic() { this.player.stop(); this.amplifier.powerOff(); }
adjustVolume(volume) { this.volumeController.setVolume(volume); }
mute() { this.volumeController.mute(); } }
const audioFacade = new AudioFacade(); audioFacade.playMusic(); audioFacade.adjustVolume(50); audioFacade.mute(); audioFacade.pauseMusic(); audioFacade.stopMusic();
|
输出
1 2 3 4 5 6 7
| Powering on... Playing... Setting volume to 50 Muting... Pausing... Stopping... Powering off...
|
七、亨元模式
享元模式(Flyweight Pattern)是一种结构型设计模式,旨在通过共享对象来最大程度地减少内存使用和提高性能。在享元模式中,对象被分为内部状态(Intrinsic State)和外部状态(Extrinsic State)两部分,其中内部状态可以被多个对象共享,而外部状态则必须在对象之间单独存储。
假设我们正在开发一个游戏,游戏中有许多树木对象,每个树木对象都有自己的位置和类型。在游戏中,有许多相同类型的树木对象,它们的外部状态(位置)不同,但内部状态(类型)是相同的。在没有使用享元模式的情况下,每个树木对象都需要单独存储其类型信息,这样会占用大量的内存。而通过享元模式,我们可以共享相同类型的树木对象,从而节省内存。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
| class Tree { constructor(type) { this.type = type; }
render(x, y) { console.log(`Rendering a ${this.type} tree at (${x}, ${y})`); } }
class TreeFactory { constructor() { this.treeTypes = {}; }
getTreeType(type) { if (!this.treeTypes[type]) { this.treeTypes[type] = new Tree(type); } return this.treeTypes[type]; } }
class GameScene { constructor() { this.trees = []; this.treeFactory = new TreeFactory(); }
addTree(type, x, y) { const treeType = this.treeFactory.getTreeType(type); this.trees.push({ type: treeType, x, y }); }
renderScene() { this.trees.forEach(tree => { tree.type.render(tree.x, tree.y); }); } }
const gameScene = new GameScene(); gameScene.addTree("pine", 10, 20); gameScene.addTree("oak", 30, 40); gameScene.addTree("pine", 50, 60);
gameScene.renderScene();
|
输出
1 2 3
| Rendering a pine tree at (10, 20) Rendering a oak tree at (30, 40) Rendering a pine tree at (50, 60)
|