javascript 设计模式

1 单例模式

  1. 定义

保证一个类仅有一个实例,并提供一个访问它的全局访问点, 比如 webpack打包的模块, 在多个文件多次引用某个文件的情况下只打包一次该文件.

  1. 核心

确保只有一个实例,并提供全局访问

  1. 实现

假设要设置一个管理员,多次调用也仅设置一次,我们可以使用闭包缓存一个内部变量来实现这个单例

// 构造函数
function SetManager(name) {
    this.manager = name;
}

SetManager.prototype.getName = function() {
    console.log(this.manager);
};

//函数内部使用了单例模式,只生成一个manager对象
var SingletonSetManager = (function() {
    var manager = null;

    return function(name) {
        if (!manager) {
            manager = new SetManager(name);
        }

        return manager;
    } 
})();

SingletonSetManager('a').getName(); // a
SingletonSetManager('b').getName(); // a
SingletonSetManager('c').getName(); // a

2 策略模式

1. 定义

定义一系列的算法,把它们一个个封装起来,并且使它们可以相互替换。

2. 核心

将算法的使用和算法的实现分离开来。

一个基于策略模式的程序至少由两部分组成:

第一个部分是一组策略类,策略类封装了具体的算法,并负责具体的计算过程。

第二个部分是环境类Context,Context接受客户的请求,随后把请求委托给某一个策略类。要做到这点,说明Context 中要维持对某个策略对象的引用

3. 实现

策略模式可以用于组合一系列算法,也可用于组合一系列业务规则

假设需要通过成绩等级来计算学生的最终得分,每个成绩等级有对应的加权值。我们可以利用对象字面量的形式直接定义这个组策略

// 加权映射关系
var levelMap = {
    S: 10,
    A: 8,
    B: 6,
    C: 4
};

// 组策略
var scoreLevel = {
    basicScore: 80,

    S: function() {
        return this.basicScore + levelMap['S']; 
    },

    A: function() {
        return this.basicScore + levelMap['A']; 
    },

    B: function() {
        return this.basicScore + levelMap['B']; 
    },

    C: function() {
        return this.basicScore + levelMap['C']; 
    }
}

// 调用
function getScore(level) {
    return scoreLevel[level] ? scoreLevel[level]() : 0;
}

console.log(
    getScore('S'),
    getScore('A'),
    getScore('B'),
    getScore('C'),
    getScore('D')
); // 90 88 86 84 0

3 代理模式

1. 定义

为一个对象提供一个代用品或占位符,以便控制对它的访问

2. 核心

当客户不方便直接访问一个 对象或者不满足需要的时候,提供一个替身对象 来控制对这个对象的访问,客户实际上访问的是 替身对象。

替身对象对请求做出一些处理之后, 再把请求转交给本体对象

代理和本体的接口具有一致性,本体定义了关键功能,而代理是提供或拒绝对它的访问,或者在访问本体之前做一 些额外的事情

3. 实现

保护代理主要实现了访问主体的限制行为,以过滤字符作为简单的例子

// 主体,发送消息
function sendMsg(msg) {
    console.log(msg);
}

// 代理,对消息进行过滤
function proxySendMsg(msg) {
    // 无消息则直接返回
    if (typeof msg === 'undefined') {
        console.log('deny');
        return;
    }
    
    // 有消息则进行过滤
    msg = ('' + msg).replace(/泥\s*煤/g, '');

    sendMsg(msg);
}


sendMsg('泥煤呀泥 煤呀'); // 泥煤呀泥 煤呀
proxySendMsg('泥煤呀泥 煤'); // 呀
proxySendMsg(); // deny

4 发布-订阅模式

1. 定义

也称作观察者模式,定义了对象间的一种一对多的依赖关系,当一个对象的状态发 生改变时,所有依赖于它的对象都将得到通知

2. 核心

取代对象之间硬编码的通知机制,一个对象不用再显式地调用另外一个对象的某个接口。

与传统的发布-订阅模式实现方式(将订阅者自身当成引用传入发布者)不同,在JS中通常使用注册回调函数的形式来订阅

3. 实现

JS中的事件就是经典的发布-订阅模式的实现

// 订阅
document.body.addEventListener('click', function() {
    console.log('click1');
}, false);

document.body.addEventListener('click', function() {
    console.log('click2');
}, false);

// 发布
document.body.click(); // click1  click2
// 观察者
var observer = {
    // 订阅集合
    subscribes: {},

    // 订阅
    subscribe: function(type, fn) {
        if (!this.subscribes[type]) {
            this.subscribes[type] = [];
        }
        
        // 收集订阅者的处理
        typeof fn === 'function' && this.subscribes[type].push(fn);
    },

    // 发布  可能会携带一些信息发布出去
    publish: function() {
        var type = [].shift.call(arguments),
            fns = this.subscribes[type];
        
        // 不存在的订阅类型,以及订阅时未传入处理回调的
        if (!fns || !fns.length) {
            return;
        }
        
        // 挨个处理调用
        for (var i = 0; i < fns.length; ++i) {
            fns[i].apply(this, arguments);
        }
    },
    
    // 删除订阅
    remove: function(type, fn) {
        // 删除全部
        if (typeof type === 'undefined') {
            this.subscribes = [];
            return;
        }

        var fns = this.subscribes[type];

        // 不存在的订阅类型,以及订阅时未传入处理回调的
        if (!fns || !fns.length) {
            return;
        }

        if (typeof fn === 'undefined') {
            fns.length = 0;
            return;
        }

        // 挨个处理删除
        for (var i = 0; i < fns.length; ++i) {
            if (fns[i] === fn) {
                fns.splice(i, 1);
            }
        }
    }
};

// 订阅岗位列表
function jobListForA(jobs) {
    console.log('A', jobs);
}

function jobListForB(jobs) {
    console.log('B', jobs);
}

// A订阅了笔试成绩
observer.subscribe('job', jobListForA);
// B订阅了笔试成绩
observer.subscribe('job', jobListForB);


// A订阅了笔试成绩
observer.subscribe('examinationA', function(score) {
    console.log(score);
});

// B订阅了笔试成绩
observer.subscribe('examinationB', function(score) {
    console.log(score);
});

// A订阅了面试结果
observer.subscribe('interviewA', function(result) {
    console.log(result);
});

observer.publish('examinationA', 100); // 100
observer.publish('examinationB', 80); // 80
observer.publish('interviewA', '备用'); // 备用

observer.publish('job', ['前端', '后端', '测试']); // 输出A和B的岗位


// B取消订阅了笔试成绩
observer.remove('examinationB');
// A都取消订阅了岗位
observer.remove('job', jobListForA);

observer.publish('examinationB', 80); // 没有可匹配的订阅,无输出
observer.publish('job', ['前端', '后端', '测试']); // 输出B的岗位

5 命令模式

1. 定义

用一种松耦合的方式来设计程序,使得请求发送者和请求接收者能够消除彼此之间的耦合关系

命令(command)指的是一个执行某些特定事情的指令


// 自增
function IncrementCommand() {
    // 当前值this.val = 0;
    // 命令栈this.stack = [];
    // 栈指针位置this.stackPosition = -1;
};

// 定义一个命令
IncrementCommand.prototype = {
    constructor: IncrementCommand,

    // 执行
    execute: function() {
        this._clearRedo();
        
        // 定义执行的处理var command = function() {
            this.val += 2;
        }.bind(this);
        
        // 执行并缓存起来        command();
        
        this.stack.push(command);

        this.stackPosition++;

        this.getValue();
    },
    
    canUndo: function() {
        return this.stackPosition >= 0;
    },
    
    canRedo: function() {
        return this.stackPosition < this.stack.length - 1;
    },

    // 撤销
    undo: function() {
        if (!this.canUndo()) {
            return;
        }
        
        this.stackPosition--;

        // 命令的撤销,与执行的处理相反var command = function() {
            this.val -= 2;
        }.bind(this);
        
        // 撤销后不需要缓存        command();

        this.getValue();
    },
    
    // 重做
    redo: function() {
        if (!this.canRedo()) {
            return;
        }
        
        // 执行栈顶的命令this.stack[++this.stackPosition]();

        this.getValue();
    },
    
    // 在执行时,已经撤销的部分不能再重做
    _clearRedo: function() {
        this.stack = this.stack.slice(0, this.stackPosition + 1);
    },
    
    // 获取当前值
    getValue: function() {
        console.log(this.val);
    }
};
var incrementCommand = new IncrementCommand();

// 模拟事件触发,执行命令var eventTrigger = {
    // 某个事件的处理中,直接调用命令的处理方法
    increment: function() {
        incrementCommand.execute();
    },

    incrementUndo: function() {
        incrementCommand.undo();
    },

    incrementRedo: function() {
        incrementCommand.redo();
    }
};


eventTrigger['increment'](); // 2
eventTrigger['increment'](); // 4
eventTrigger['incrementUndo'](); // 2
eventTrigger['increment'](); // 4
eventTrigger['incrementUndo'](); // 2
eventTrigger['incrementUndo'](); // 0
eventTrigger['incrementUndo'](); // 无输出
eventTrigger['incrementRedo'](); // 2
eventTrigger['incrementRedo'](); // 4
eventTrigger['incrementRedo'](); // 无输出
eventTrigger['increment'](); // 6

6 模板方法模式

1. 定义

模板方法模式由两部分结构组成,第一部分是抽象父类,第二部分是具体的实现子类。

2. 核心

在抽象父类中封装子类的算法框架,它的 init方法可作为一个算法的模板,指导子类以何种顺序去执行哪些方法。

由父类分离出公共部分,要求子类重写某些父类的(易变化的)抽象方法

3. 实现

模板方法模式一般的实现方式为继承

以运动作为例子,运动有比较通用的一些处理,这部分可以抽离开来,在父类中实现。具体某项运动的特殊性则有自类来重写实现。

最终子类直接调用父类的模板函数来执行

// 体育运动
function Sport() {

}

Sport.prototype = {
    constructor: Sport,
    
    // 模板,按顺序执行
    init: function() {
        this.stretch();
        this.jog();
        this.deepBreath();
        this.start();

        var free = this.end();
        
        // 运动后还有空的话,就拉伸一下
        if (free !== false) {
            this.stretch();
        }
        
    },
    
    // 拉伸
    stretch: function() {
        console.log('拉伸');
    },
    
    // 慢跑
    jog: function() {
        console.log('慢跑');
    },
    
    // 深呼吸
    deepBreath: function() {
        console.log('深呼吸');
    },

    // 开始运动
    start: function() {
        throw new Error('子类必须重写此方法');
    },

    // 结束运动
    end: function() {
        console.log('运动结束');
    }
};

// 篮球
function Basketball() {

}

Basketball.prototype = new Sport();

// 重写相关的方法
Basketball.prototype.start = function() {
    console.log('先投上几个三分');
};

Basketball.prototype.end = function() {
    console.log('运动结束了,有事先走一步');
    return false;
};


// 马拉松
function Marathon() {

}

Marathon.prototype = new Sport();

var basketball = new Basketball();
var marathon = new Marathon();

// 子类调用,最终会按照父类定义的顺序执行
basketball.init();
marathon.init();

7 职责链模式

1. 定义

使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系,将这些对象连成一条链,并沿着这条链 传递该请求,直到有一个对象处理它为止

2. 核心

请求发送者只需要知道链中的第一个节点,弱化发送者和一组接收者之间的强联系,可以便捷地在职责链中增加或删除一个节点,同样地,指定谁是第一个节点也很便捷

3. 实现

以展示不同类型的变量为例,设置一条职责链,可以免去多重if条件分支

// 定义链的某一项
function ChainItem(fn) {
    this.fn = fn;
    this.next = null;
}

ChainItem.prototype = {
    constructor: ChainItem,
    
    // 设置下一项
    setNext: function(next) {
        this.next = next;
        return next;
    },
    
    // 开始执行
    start: function() {
        this.fn.apply(this, arguments);
    },
    
    // 转到链的下一项执行
    toNext: function() {
        if (this.next) {
            this.start.apply(this.next, arguments);
        } else {
            console.log('无匹配的执行项目');
        }
    }
};

// 展示数字
function showNumber(num) {
    if (typeof num === 'number') {
        console.log('number', num);
    } else {
        // 转移到下一项
        this.toNext(num);
    }
}

// 展示字符串
function showString(str) {
    if (typeof str === 'string') {
        console.log('string', str);
    } else {
        this.toNext(str);
    }
}

// 展示对象
function showObject(obj) {
    if (typeof obj === 'object') {
        console.log('object', obj);
    } else {
        this.toNext(obj);
    }
}

var chainNumber = new ChainItem(showNumber);
var chainString = new ChainItem(showString);
var chainObject = new ChainItem(showObject);

// 设置链条
chainObject.setNext(chainNumber).setNext(chainString);

chainString.start('12'); // string 12
chainNumber.start({}); // 无匹配的执行项目
chainObject.start({}); // object {}
chainObject.start(123); // number 123

参考资料:

https://www.cnblogs.com/imwtr/p/9451129.html#o6

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值