设计模式简介
设计模式简介
最佳的实践得来的设计经验 设计模式 开发过程中面临的一般问题的解决方案
相当长的一段时间的试验和错误总结出来
设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结
设计模式原则
- S – Single Responsibility Principle 单一职责原则 一个程序只做好一件事 如果功能过于复杂就拆分开,每个部分保持独立
- O – OpenClosed Principle 开放/封闭原则 对扩展开放,对修改封闭 增加需求时,扩展新代码,而非修改已有代码
- L – Liskov Substitution Principle 里氏替换原则 子类能覆盖父类 父类能出现的地方子类就能出现
- I – Interface Segregation Principle 接口隔离原则 保持接口的单一独立 类似单一职责原则,这里更关注接口
- D – Dependency Inversion Principle 依赖倒转原则 面向接口编程,依赖于抽象而不依赖于具体 使用方只关注接口而不关注具体类的实现
创建形
单例模式
一个类只有一个实例,并提供一个访问它的全局访问点
class Axios{
constructor(config){
this.config = config;
}
}
Axios.getInstance = function(config){
if(!this.instance){
this.instance = new Axios(config);
}
return this.instance;
}
let i1=Axios.getInstance({});
let i2=Axios.getInstance({});
console.log(i1==i2);
优点
1.命名空间,减少全局变量
2.增强模块性,集中变量/逻辑管理
3.且只会实例化一次。简化了代码的调试和维护
缺点
1.单点访问,导致模块间的强耦合 从而不利于单元测试。
场景例子
1.命名空间和实现分支型方法
2.登录框
3.vuex 和 redux中的store
class Axios{
constructor(config){
this.config = config;
}
static getInstance(config){
if(!this.instance){
this.instance = new Axios(config);
}
return this.instance;
}
}
原型模式
原型模式(prototype)是指用原型实例指向创建对象的种类,并且通过拷贝这些原型创建新的对象。
原型模式,就是创建一个共享的原型,通过拷贝这个原型来创建新的类,用于创建重复的对象,带来性能上的提升
class Person {
constructor(name) {
this.name = name;
}
}
class Student extends Person {
constructor(name, grade) {
super(name);
}
sayName(){
console.log(this.name,111111111111111111);
}
}
var s = new Student("John", 10);
s.sayName();
//继承
工厂模式
- 在不暴露创建对象的具体逻辑,而是将逻辑进行封装,那么它就可以被称为工厂。
优点
1.调用者创建对象时只要知道其名称即可 扩展性高,如果要新增一个产品,直接扩展一个工厂类即可。
2.隐藏产品的具体实现,只关心产品的接口。
缺点
1.每次增加一个产品时,都需要增加一个具体类,这无形增加了系统内存的压力和系统的复杂度,也增加了具体类的依赖
黑盒----------
JQuery的$()就是一个工厂函数
vue 的异步组件
class Factory {
create(name) {
return new Product(name);
}
}
class Product {
constructor(name) {
this.name = name;
}
printName() {
console.log(this.name);
}
}
var factory = new Factory();
var product = factory.create("test");
product.printName();
-------------------------------------------------------------------------
// 抽象工厂模式
class ProductionFlow {
constructor() {
if (new.target === ProductionFlow) {
throw new Error("抽象类不能被实例");
}
}
production() {
throw new Error("production要被重写");
}
materials() {
throw new Error("materials要被重写");
}
}
class TShirt extends ProductionFlow {
production() {
console.log(`材料:${this.materials()},生产t恤`);
}
materials() {
return "纯棉";
}
}
function getAbstractProductionFactory(clothingType) {
const clothingObj = {
t_shirt: TShirt, //add more...
};
if (clothingObj[clothingType]) {
return clothingObj[clothingType];
}
throw new Error(`工厂暂时不支持生产这个${clothingType}类型的服装`);
}
const tShirtClass = getAbstractProductionFactory("t_shirt");
const tShirt = new downTShirtClass();
tShirt.production();
建造者模式
建造者模式是一种比较复杂使用频率较低的创建型设计模式,建造者模式为客户端返回的不是一个简单的产品,而是一个由多个部件组成的复杂产品。主要用于将一个复杂对象的构建与他的表现分离,使得同样的构建过程可以创建不同的表示。
- 构建与他的表现分离
优点
1.建造者独立易扩展
2.方便控制细节风险
缺点
1.产品必须有共同点,范围有限制
2.当内部有变化复杂时,会有很多建造类
构建与他的表现分离
class Product {
constructor(name, price) {
this.name = name;
this.price = price;
}
}
class Pc extends Product {
constructor(name, price) {
super(name, price);
}
}
class Mobile extends Product {
constructor(name, price) {
super(name, price);
}
}
class ProductManage{
constructor() {
this.products = [];
}
addProduct(product) {
this.products.push(product);
}
getSimplePrice(){
return this.products.reduce((total, product) => total + product.price, 0);
}
}
class FactorManger {
createPc(){
throw new Error("Not implemented");
}
createMobile(){
throw new Error("Not implemented");
}
}
class Worker extends FactorManger {
constructor() {
super();
this.productarr = new ProductManage();
}
createPc(createNumProduct){
for(let i=0;i<createNumProduct;i++){
this.productarr.addProduct(new Pc("Pc", 1000));
}
}
createMobile(createNumProduct){
for(let i=0;i<createNumProduct;i++){
this.productarr.addProduct(new Mobile("Mobile", 1));
}
}
}
class Salesman {
constructor() {
this.Worker=null;
}
setWorker(worker){
this.Worker=worker;
}
orderTotalPrice(products){
products.forEach(item => {
if(item.name === "Pc"){
this.Worker.createPc(item.num);
}else if(item.name === "Mobile"){
this.Worker.createMobile(item.num);
}
else{
throw new Error("Not implemented");
}
});
return this.Worker.productarr.getSimplePrice();
}
}
const salesman = new Salesman()
const worker = new Worker()
salesman.setWorker(worker)
const order = [
{
name: 'Pc',
num: 10
},
{
name: 'Mobile',
num: 4
},
]
console.log(`本次订单所需金额:${salesman.orderTotalPrice(order)}`) //10004
结构型
适配器模式
`将一个类的接口转化为另外一个接口.解决使类之间接口不兼容问题
优点
1.可以让任何两个没有关联的类一起运行。
2.提高了类的复用性、透明度、以及灵活性
3.适配对象,适配库,适配数据
缺点
额外对象的创建,非直接调用,存在一定的开销
过多的使用适配器模式,会让系统变得零乱,不易整体把控。
非必要用适配器模式,就重构最好,若用的话,文档完善
场景
整合第三方SDK diy $.axjax
封装旧接口 vue.computed
适配器与代理模式相似
适配器模式: 提供一个不同的接口
代理模式: 提供一模一样的接口
class Person{
getName(){
return "John";
}
}
class Adapter{
constructor(person){
this.person = person;
}
getFullName(){
return this.person.getName();
}
}
let person = new Person();
let adapter = new Adapter(person);
console.log(adapter.getFullName());
装饰器模式
装饰者模式在不更改源代码情况下,对其进行职责添加。相比于继承装饰器的更轻巧。
通俗的讲我们给心爱的手机上贴膜,带手机壳,贴纸,这些就是对手机的装饰。
优点
装饰类和被装饰类它们之间可以相互独立发展,不会相互耦合,
装饰器模式是继承的一个替代模式,它可以动态的扩展一个实现类的功能。
缺点
多层的装饰会增加复杂度
class Phone{
create(){
console.log("Phone created");
}
}
class Decorator{
constructor(phone){
this.phone = phone;
}
create(){
this.phone.create();
this.addPhoneBox();
this.addPhonePicker();
}
addPhoneBox(){
console.log("Phone box added");
}
addPhonePicker(){
console.log("Phone picker added");
}
}
let phone = new Phone();
let decorator = new Decorator(phone);
decorator.create();
代理模式
优点
代理模式能将代理对象与被调用对象分离,降低了系统的耦合度。
之间起到一个中介作用,这样可以起到保护目标对象的作用
代理对象可以扩展目标对象的功能
通过修改代理对象就可以了,符合开闭原则;
缺点
处理请求速度可能有差别,非直接访问存在开销
// 员工类
class Staff {
constructor(task) {
this.task = task;
}
applyFor(target){
target.receiveAplyFor(this.task)
}
}
// 秘书类 中间人 代理
class Secretary {
constructor() {
this.leader=new Leader();
}
receiveAplyFor(task) {
this.leader.receiveAplyFor(task);
}
}
// 领导类
class Leader{
receiveAplyFor(task) {
console.log('批准: ' + task);
}
}
const staff = new Staff('升职加薪')
staff.applyFor(new Secretary()) // 批准:升职加薪
外观模式
外观模式本质就是封装交互,隐藏系统的复杂性,提供一个可以访问的接口。
由一个将子系统一组的接口集成在一起的高层接口,以提供一个一致的外观,减少外界与多个子系统之间的直接交互,从而更方便的使用子系统。
优点
减少系统相互依赖。
提高灵活性。
提高了安全性
缺点
不符合开闭原则,如果要改东西很麻烦,继承重写都不合适。
// 外观模式
function addEvent(el,type,fn){
if(el.addEventlistener){// 高级游览器添加事件DOM API
el.addEventlistener(type,fn,false)
}else if(el.attachEvent){// 低版本游览器的添加事件API
el.attachEvent(`on${type}`,fn)
}else {//其他
el[type] = fn
}
}
二层金字塔
1
2 2 2
桥接模式
桥接模式(Bridge)将抽象部分与它的实现部分分离,使它们都可以独立地变化。
优点
有助于独立地管理各组成部分, 把抽象化与实现化解耦
提高可扩充性
缺点
大量的类将导致开发成本的增加,同时在性能方面可能也会有所减少。
桥接模式的引入会增加系统的理解与设计难度,由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计与编程。
//桥接模式(Bridge)
class color{
constructor(name){
this.name = name;
}
}
class shape{
constructor(name,color){
this.name = name;
this.color = color;
}
show(){
console.log(this.name + this.color.name);
}
}
let red = new color('红色');
let yellow = new color('黄色');
let circle = new shape('圆形',red);
let triangle = new shape('三角形',yellow);
circle.show();
triangle.show();
组合模式
组合模式
组合模式就是由一些小的子对象构建出的更大的对象,而这些小的子对象本身可能也是由多个孙对象组合而成的。
组合模式将对象组合成树状结构,以表示“部分-整体”的层次结构。除了用来表示树状结构之外,组合模式的另一个好处就是通过对象的多态性表现,使得用户对单个对象和组合对象的使用具有一致性。
缺点
如果通过组合模式创建了太多的对象,那么这些对象可能会让系统负担不起。
优点
高层模块调用简单,节点可以自由添加
// 组合模式
class Train{
create(){
console.log('创建火车票订单')
}
}
class Hotal{
create(){
console.log('创建酒店订单')
}
}
class TotalOrder{
constructor(){
this.orderList = [];
}
add(order){
this.orderList.push(order);
return this;
}
create(){
this.orderList.forEach(order=>{
order.create();
})
return this;
}
}
let totalOrder = new TotalOrder();
// 可以在购票网站买车票同时也订房间
totalOrder.add(new Train()).add(new Hotal()).create();
享元模式
运用共享技术有效地支持大量细粒度对象的复用。
享元模式的关键是如何区分内部状态和外部状态
内部状态:可以被对象共享,通常不会改变的称为内部状态
外部状态:取决于具体的场景,根据具体的场景变化,并且不能被共享的称为外部状态
优点
大大减少对象的创建,降低系统的内存,使效率提高。
缺点
提高了系统的复杂度,需要分离出外部状态和内部状态,而且外部状态具有固有化的性质, 不应该随着内部状态的变化而变化,否则会造成系统的混乱
let id = 0
// 定义内部状态
class Upload {
constructor(uploadType) {
this.uploadType = uploadType
}
// 点击删除时 小于3000直接删除,大于3000通过confirm提示弹窗删除。
delFile(id) {
uploadManager.setExternalState(id,this)
if(this.fileSize < 3000){
return this.dom.parentNode.removeChild(this.dom)
}
if(window.confirm(`确定要删除该文件吗?${this.fileName}`)){
return this.dom.parentNode.removeChild(this.dom)
}
}
}
// 外部状态
class uploadManager {
static uploadDatabase = {}
static add(id, uploadType, fileName, fileSize) {
const filWeightObj = UploadFactory.create(uploadType)
const dom = this.createDom(fileName, fileSize, () => {
filWeightObj.delFile(id)
})
this.uploadDatabase[id] = {
fileName,
fileSize,
dom
}
}
// 创建DOM 并且为button绑定删除事件。
static createDom(fileName, fileSize, fn) {
const dom = document.createElement('div')
dom.innerHTML = `
<span>文件名称:${fileName},文件大小:${fileSize}</span>
<button class="delFile">删除</button>
`
dom.querySelector('.delFile').onclick = fn
document.body.append(dom)
return dom
}
static setExternalState(id, flyWeightObj) {
const uploadData = this.uploadDatabase[id]
for (const key in uploadData) {
if (Object.hasOwnProperty.call(uploadData, key)) {
flyWeightObj[key] = uploadData[key]
}
}
}
}
// 定义一个工厂创建upload对象,如果其内部状态实例对象存在直接返回,反之创建保存并返回。
class UploadFactory {
static createFlyWeightObjs = {}
static create(uploadType) {
if (this.createFlyWeightObjs[uploadType]) {
return this.createFlyWeightObjs[uploadType]
}
return this.createFlyWeightObjs[uploadType] = new Upload(uploadType)
}
}
// 开始加载
const startUpload = (uploadType, files)=>{
for (let i = 0, file; file = files[i++];) {
uploadManager.add(++id, uploadType, file.fileName, file.fileSize)
}
}
startUpload('plugin', [
{fileName: '1.txt',fileSize: 1000},
{fileName: '2.html',fileSize: 3000},
{fileName: '3.txt',fileSize: 5000}
]);
startUpload('flash', [
{fileName: '4.txt',fileSize: 1000},
{fileName: '5.html',fileSize: 3000},
{fileName: '6.txt',fileSize: 5000}
]);
行为型
观察者模式/发布订阅
发布订阅又称观察者模式,它定义对象之间的1对N的依赖关系,当其中一个对象发生变化时,所有依赖于它的对象都会得到通知。
优点
支持简单的广播通信,自动通知所有已经订阅过的对象
目标对象与观察者之间的抽象耦合关系能单独扩展以及重用
增加了灵活性
观察者模式所做的工作就是在解耦,让耦合的双方都依赖于抽象,而不是依赖于具体。从而使得各自的变化都不会影响到另一边的变化。
缺点
过度使用会导致对象与对象之间的联系弱化,会导致程序难以跟踪维护和理解
// 观察者模式/发布订阅
class Subject {
constructor() {
this.state = 0;
this.observers = [];
}
getState(){
return this.state;
}
setState(state) {
this.state = state;
this.notifyAllObservers();
}
notifyAllObservers() {
this.observers.forEach(observer => observer.update());
}
attach(observer) {
this.observers.push(observer);
}
}
// 观察者
class Observer {
constructor(name, subject) {
this.name = name;
this.subject = subject;
this.subject.attach(this);
}
update() {
console.log(`${this.name} update, state: ${this.subject.getState()}`);
}
}
const subject = new Subject();
const observer1 = new Observer('observer1', subject);
const observer2 = new Observer('observer2', subject);
subject.setState(99);
迭代器模式
迭代器模式是指提供一种方法顺序访问一个聚合对象中的每个元素,并且不需要暴露该对象的内部。
优点
它支持以不同的方式遍历一个聚合对象。
迭代器简化了聚合类。在同一个聚合上可以有多个遍历。
在迭代器模式中,增加新的聚合类和迭代器类都很方便,无须修改原有代码。
缺点
由于迭代器模式将存储数据和遍历数据的职责分离,增加新的聚合类需要对应增加新的迭代器类,类的个数成对增加,这在一定程度上增加了系统的复杂性。
class Iterator{
constructor(container){
this.list = container.list;
this.index = 0;
}
next(){
if(this.hasNext){
return this.list[this.index++];
}
return null
}
hasNext(){
if(this.index>=this.list.length){
return false;
}
return true;
}
}
class Container{
constructor(list){
this.list = list;
}
getIterator(){
return new Iterator(this);
}
}
let container = new Container([1, 2, 3, 4, 5])
let iterator = container.getIterator()
while(iterator.hasNext()) {
console.log(iterator.next())
}
策略模式
策略模式指的是定义一系列算法,把他们一个个封装起来,目的就是将算法的使用和算法的实现分离开来。同时它还可以用来封装一系列的规则,比如常见的表单验证规则,只要这些规则指向的目标一致,并且可以被替换使用,那么就可以用策略模式来封装它们。
优点
算法可以自由切换,避免了使用多层条件判断,增加了扩展性
缺点
策略类增多,所有策略类都需要对外暴露。
//策略模式
const rules = {
cover_img: {
must: false,
msg: '请上传封面图片',
val: ''
},
name: {
must: true,
msg: '姓名不能为空',
val: ''
},
sex: {
must: true,
msg: '请填写性别',
val: ''
},
birthday: {
must: false,
msg: '请选择生日',
val: ''
},
}
function verify(){
for(const key in rules){
if(rules[key].must&&!rules[key].val){
console.log(rules[key].msg)
}
}
}
verify()
// 姓名不能为空
// 请填写性别
模板方法模式
模板方法模式由两部分结构组成,第一部分是抽象父类,第二部分是具体的实现子类。通常在抽象父类中封装了子类的算法框架,包括实现一些公共方法和封装子类中所有方法的执行顺序。子类通过继承这个抽象类,也继承了整个算法结构,并且可以选择重写父类的方法。
优点
提取了公共代码部分,易于维护
缺点
增加了系统复杂度,主要是增加了的抽象类和类间联系
// 模板方法模式
class Beverage {
constructor({brewDrink, addCondiment}) {
this.brewDrink = brewDrink
this.addCondiment = addCondiment
}
/* 烧开水,共用方法 */
boilWater() { console.log('水已经煮沸=== 共用') }
/* 倒杯子里,共用方法 */
pourCup() { console.log('倒进杯子里===共用') }
/* 模板方法 */
init() {
this.boilWater()
this.brewDrink()
this.pourCup()
this.addCondiment()
}
}
/* 咖啡 */
const coffee = new Beverage({
/* 冲泡咖啡,覆盖抽象方法 */
brewDrink: function() { console.log('冲泡咖啡') },
/* 加调味品,覆盖抽象方法 */
addCondiment: function() { console.log('加点奶和糖') }
})
coffee.init()
职责链模式
场景例子
JS 中的事件冒泡
作用域链
原型链
优点
降低耦合度,它将请求的发送者和接收者解耦。
简化了对象,使得对象不需要知道链的结构。
增强给对象指派职责的灵活性。通过改变链内的成员或者调动它们的次序,允许动态地新增或者删除责任。
缺点
不能保证每一条请求都一定被接收。
系统性能将受到一定影响,而且在进行代码调试时不太方便,可能会造成循环调用。
可能不容易观察运行时的特征,有碍于排除问题。
// 职责链模式
// 请假审批,需要组长审批、经理审批、总监审批
class Action{
constructor(name){
this.name = name;
this.nextAction = null;
}
setNextAction(action){
this.nextAction = action;
}
handle(){
console.log(`${this.name} 审批通过`);
if(this.nextAction != null){
this.nextAction.handle();
}
}
}
let a1 = new Action("组长")
let a2 = new Action("经理")
let a3 = new Action("总监")
a1.setNextAction(a2)
a2.setNextAction(a3)
a1.handle()
命令模式
优点
对命令进行封装,使命令易于扩展和修改
命令发出者和接受者解耦,使发出者不需要知道命令的具体执行过程即可执行
缺点
使用命令模式可能会导致某些系统有过多的具体命令类
//命令模式
// 接收者类
class Receiver {
execute() {
console.log("接收者执行请求");
}
}
// 命令者
class Command {
constructor(receiver) {
this.receiver = receiver;
}
execute() {
console.log("命令");
this.receiver.execute();
}
}
// 触发者
class Invoker {
constructor(command) {
this.command = command;
}
invoke() {
console.log("开始");
this.command.execute();
}
}
// 仓库
const warehouse = new Receiver();
// 订单
const order = new Command(warehouse);
// 客户
const client = new Invoker(order);
client.invoke()
备忘录模式
备忘录模式就是在不破坏封装的前提下,捕获一个对象内部状态,并在该对象之外保存这个状态,以保证以后可以将对象恢复到原先的状态。
优点
给用户提供了一种可以恢复状态的机制,可以使用户能够比较方便地回到某个历史的状态
缺点
消耗资源。如果类的成员变量过多,势必会占用比较大的资源,而且每一次保存都会消耗一定的内存。
// 备忘录模式
// 备忘类
class Memento{
constructor(content){
this.content = content;
}
getContent(){
return this.content;
}
}
// 备忘列表
class CareTaker{
constructor(){
this.list=[];
}
add(memento){
this.list.push(memento);
}
get(index){
return this.list[index];
}
}
// 编辑器
class Editor{
constructor(){
this.content = null;
}
setContent(content){
this.content = content;
}
getContent(){
return this.content;
}
saveContentToMemento(){
return new Memento(this.content);
}
getContentFromMemento(memento){
this.content = memento.getContent();
}
}
//测试代码
let editor = new Editor()
let careTaker = new CareTaker()
editor.setContent('111')
editor.setContent('222')
careTaker.add(editor.saveContentToMemento())
editor.setContent('333')
careTaker.add(editor.saveContentToMemento())
editor.setContent('444')
console.log(editor.getContent()) //444
editor.getContentFromMemento(careTaker.get(1))
console.log(editor.getContent()) //333
editor.getContentFromMemento(careTaker.get(0))
console.log(editor.getContent()) //222
状态模式
场景
一个对象的行为取决于它的状态,并且它必须在运行时刻根据状态改变它的行为
一个操作中含有大量的分支语句,而且这些分支语句依赖于该对象的状态
优点
定义了状态与行为之间的关系,封装在一个类里,更直观清晰,增改方便
状态与状态间,行为与行为间彼此独立互不干扰
用对象代替字符串来记录当前状态,使得状态的切换更加一目了然
缺点
会在系统中定义许多状态类
逻辑分散
// 状态模式
class State{
constructor(attack){
this.attack = attack;
}
handle(context){
console.log(this.attack);
context.setState(this);
}
}
class Context{
constructor(){
this.state=null;
}
getState(){
return this.state;
}
setState(state){
this.state = state;
}
}
const q1 = new State('q1 第1击'),
q2 = new State('q2 第2击'),
q3 = new State('q3 第3击'),
context = new Context();
q1.handle(context)//q1 第1击
q2.handle(context)//q2 第2击
q3.handle(context)//q3 第3击
访问者模式
访问者模式是将数据的操作和数据的结构进行分离,对数据中各元素的操作封装独立的类,使其在不改变数据结构情况下扩展新的数据。
优点
符合单一职责原则。具有优秀的扩展性和灵活性。
缺点
违反了依赖倒置原则,依赖了具体类,没有依赖抽象。
// 访问者模式
// 访问者
class Visitor {
constructor(){}
visitConcreteElement(ConcreteElement){
ConcreteElement.operation()
}
}
// 元素类
class ConcreteElement {
constructor(){}
operation(){
console.log('ConcreteElement.operation invoked')
}
accept(visitor){
visitor.visitConcreteElement(this)
}
}
// 客户端
const visitor = new Visitor()
const element = new ConcreteElement()
element.accept(visitor)
中介者模式
中介者模式的作用就是解除对象与对象之间的紧密耦合关系。增加一个中介者对象之后,所有相关对象都通过中介者对象来通信,而不是相互引用,所以当一个对象发生改变时,只需要通过中介者对象即可。中介者使各对象之间耦合松散,而且可以独立改变他们之间的交互。中介者模式使网状的多对多关系变成了相对简单的一对多关系。
优点
降低了类的复杂度,将一对多转化成了一对一。各个类之间的解耦。
缺点
当中介者变得庞大复杂,导致难以维护。
// 中介者模式
class A {
constructor() {
this.number = 0
}
setNumber(num, m) {
this.number = num
if (m) {
m.setB()
}
}
}
class B {
constructor() {
this.number = 0
}
setNumber(num, m) {
this.number = num
if (m) {
m.setA()
}
}
}
class Mediator {
constructor(a, b) {
this.a = a
this.b = b
}
setA() {
let number = this.b.number
this.a.setNumber(number * 10)
}
setB() {
let number = this.a.number
this.b.setNumber(number / 10)
}
}
let a = new A()
let b = new B()
let m = new Mediator(a, b)
a.setNumber(10, m)
console.log(a.number, b.number)
b.setNumber(10, m)
console.log(a.number, b.number)
解释器模式
解释器模式提供了评估语言的语法或表达式的方式,它属于行为型模式。这种模式实现了一个表达式接口该接口,该接口解释一个特定的上下文。
优点
可扩展性比较好,灵活。增加了新的解释表达式的方式。
缺点
可利用场景比较少,在web开发中几乎不可见。对于复杂的环境比较难维护。
解释器模式会引起类膨胀。它还采用递归调用方法,没控制好可能会导致崩溃。
// 解释器模式
class Context{
constructor(){
this._list=[];
this._sum=0;
}
get sum(){
return this._sum;
}
set sum(newValue){
this._sum=newValue;
}
add(expression){
this._list.push(expression);
}
get list(){
return [...this._list];
}
}
class PlusExpression{
interpret(context){
if(!(context instanceof Context)){
throw new Error("TypeError");
}
context.sum=++context.sum;
}
}
class MinusExpression{
interpret(context){
if(!(context instanceof Context)){
throw new Error("TypeError");
}
context.sum=--context.sum;
}
}
/** 以下是测试代码 **/
const context = new Context();
// 依次添加: 加法 | 加法 | 减法 表达式
context.add(new PlusExpression());
context.add(new PlusExpression());
context.add(new MinusExpression());
// 依次执行: 加法 | 加法 | 减法 表达式
context.list.forEach(expression => expression.interpret(context));
console.log(context.sum);

浙公网安备 33010602011771号