1

在做canvas、webGL游戏时,很深切的感觉到,游戏编程玩的都是设计模式。架构没预先设计好,强耦合性代码在后期维护简直灾难。

大型单页应用里,复杂度上升到一定程度时,没有适当的设计模式进行降耦,后续的开发也难以下手。

而设计模式正是为了降耦而存在。

参考《javascript设计模式》——曾探

函数节流

var throttle = function(fn, interval){
    var _self = fn,
        timer,
        firstTime = true;

    return function(){
        var args = arguments.
            _me = this;
        
        if( firstTime ){
                _self.apply(_me, args);
            return first
            }
            
            if(timer){
            return false;
        }
    
        timer = setTimeout(function(){
            clearTimeout(timer);
            timer = null;
            _self.apply(_me, args);
        }, interval || 500);
    }    
}

window.onresize = throttle(function(){
    console.log(1);
}, 500);

分式函数

var timeChunk = function(arr, fn, count){
    var obj, t;
    var len = ary.length;

    var start = function(){
        for(var i = 0; i < Math.min(coutn||1, arr.length); i++){
            var obj = ary.shift();
            fn(obj);
        }
    }

    return function(){
        t = setInterval(function(){
            if(ary.length === 0){
                return clearInterval(t);
            }
            start();
      
        }, 200);
    };
};

惰性函数(重写自重写)

var addEvent = function(elem, type, handler){
if(window.addEventListener){
    addEvent = function(elem, type, handler){
        elem.addEventListener(type, handler, false);
    }
} else if(window.addachEvent){
    addEvent = function(elem, type, handler){
        elem.attachEvent('on' + type, handler);   
    }
}

}

单例模式

定义:保证类有且仅有一个实力,并提供全局访问接口。

私有变量加下划线 var _a = 1;

通用惰性单例模式

把创建单例和管理单例的职责分开来:


var getSingle = function(fn){
    var result;
    return function(){
        return result || (result = fn.apply(this, arguments));
    }
}

var createLayer = function(){
    var div = document.createElement('div');
    document.body.appendChild(div);
}

var createSingleCreateLayer = getSingle(createLayer);

document.getElementById('#test').onclick = () => {
    createSingleCreateLayer();
}


策略模式

定义:封装一系列算法,并使其可以相互替换

计算工资Demo:

var strategies = {
    'S': function(salary){
        return salary * 4;
    },
    'A': function(salary){
        return salary * 3;
    }
}

var calculateBonus = (level, salary) => {
    return strategies[level](salary);
}

表单添加多种校验规则:


/*--------------- Strategies --------------*/
var strategies = {
    isNotEmpth: (value, errorMsg) => {
        if(value == ''){
            return errorMsg;
        }
    },
    minLength: (value, length,errorMsg) => {
        if(value.length < length){
            return errorMsg;
        }
    }
}

/*---------------- Validator ---------------*/
var Validator = () => {
    this.cache = [];
}

Validator.prototype.add = (dom, rules) => {
    var self = this;
    
    for(var i = 0; rule; rule = rules[i++]){
        ((rule) => {
            var strategyAry = strategy.split(':');
            var errorMsg = rule.errorMsg;
            
            self.cache.push(function(){
                var strategy = strategyAry.shift();
                strategyAry.unshift(dom.value);
                strategyAry.push(errorMsg);
                
                return strategies[strategy].apply(dom, strategyAry);
            });
        })(rule);
    }
};

Validator.prototype.start = () => {
    for(var i = 0; validatorFunc; validatorFunc = this.cache[i++]){
        var errorMsg = validatorFunc();
        
        if( errorMsg ){
            return errorMsg;
        }
    }
}

/*-------------- 客户端调用 ------------*/
var registerForm = document.getElementById('registerForm');

var validataFunc = () => {
    var validator = new Validator();
    
    validator.add(registorForm.username, [{
        strategy: 'isNotEmpty',
        errorMsg: '用户名不能为空'
    },{
        strategy: 'minLength',
        errorMsg: ‘最下长度为6’
    }]);
    
    validator.add(registorForm.password, [{
        strategy: 'minLength',
        errorMsg: '最小长度'
    }])
    
    var errorMsg = validator.start();
    return errorMsg;
}

registerForm.onsubmit = () => {
    var errorMsg = validataFunc();
    
    if(errorMsg){
        alert(errorMsg);
        return false;
    }
}

代理模式

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

面向对象设计原则——单一职责原则。面向对象设计鼓励将行为分布到细粒度的对象之中。

大量计算时加入代理作为缓存,异步请求时同理可用缓存:

var mult = () => {
    var a = 1;
    for(var i = 0, l = arguments.length; i < l; i++){
        a = a * arguments[i];
    }
    return a;
}

var proxyMult = (() => {
    var cache = {};
    return () => {
        var args = Array.prototype.join.call(arguments, ',');
        if( args in cache ){
            return cache[args];
        }
        
        return cache[args] = mult.apply(this, arguments);
    }
})();
高阶函数动态创建代理
var mult = () => {
    var a = 1;
    for(var i = 0, l = arguments.length; i < l; i++){
        a = a * arguments[i];
    }
    return a;
}

var plus = () => {
    var a = 0;
    for(var i = 0, l = arguments.length; i < l; i++){
        a = a + arguments[i];
    }
    return a;
}

var createProxyFactory = (fn) => {
    var cache = {};
    return () => {
        var args = Array.prototype.join.call(arguments, ',');
        if(args in cache){
            return cache[args];
        }
        
        return cache[args] = fn.apply(this, arguments);
    }
}

迭代器模式

迭代器模式指提供一种方法顺序访问一个聚合对象的各个元素,又不暴露该对象的内部表示。

通用外部迭代器:


var Iterator = (obj) => {
    var current = 0;
    
    var next = () => {
        ++current;
    }
    
    var isDone = () => {
        return current >= obj.length;
    }
    
    var getCurrItem = () => {
        return obj[current];
    }
    
    return {
        next: next,
        isDone: isDone,
        getCurrItem: getCurrItem
    }
}

发布-订阅模式

var event = {
    clientListh: [],
    listen: function(key, fn){
        if(!this.clientListh[key]){
            this.clientList[key] = [];
        }
        this.clientList[key].push(fn);
    },
    trigger: function(){
        var key = Array.prototype.shift.call(arguments),
            fns = this.clientList[key];
            
        if(!fns || fns.length === 0){
            return false;
        }
        
        for(var i = 0; fn; fn = fns[i++]){
            fn.apply(this.arguments);
        }
    }
}

var installEvent = function(obj){
    for(var i in event){
        obj[i] = event[i];
    }
};

var salesOffices = {};
installEvnet(salesOffices);

salesOffices.listen('squareMeter88', function(price){
    console.log('price': + price);
})

salesOfffices.trigger('squareMeter88', 20000);

发布——订阅模式可以很大程度降低耦合性,但滥用也会造成背后逻辑混乱,且浪费内存。

命令模式

主要是回调函数的的面向对象的替代品

没看懂命令模式有什么意义

撤销和重做
var commandStack = [];
document.onkeypress = (ev)=>{
    var keyCode = ev.keyCode,
        command = makeCommand(Ryu, commands[keyCode]);
        
    if(command){
        command();
        commandStack.push(command);
    }
}

$('#replay').onclick = ()=>{
    var command;
    while(command = commandStack.shift()){
        command();
    }
}
宏命令
var closeDoorCommand = {
    excute: ()=> {
        console.lg('close door');
    }
}

var openPcCommand = {
    excute: ()=> {
        console.log('open pc');
    }
}

var MacroCommand = ()=> {
    return {
        conmandList: [],
        add: (command)=>{
            this.commandList.push(command);
        },
        execute: ()=>{
            for(var i = 0, command; command = this.commandsList[i++]; ){
                command.execute();
            }
        }
    }
}

组合模式

类似命令模式的加强版,也是看不懂深层意义

遍历文件夹


/************ Folder ****************/
var Folder = (name)=>{
    this.name = name;
    this.parent = null;
    this.files = [];
};

Folder.prototype.add = (file)=> {
    file.parent = this;
    this.files.push(file);
}

Folder.prototype.scan = ()=>{
    console.log('Begin scan: ' + this.name);
    for(var i = 0, file = this.files; file = files[i++]; ){
        file.scan();
    }
}

Folder.prototype.remove = ()=>{
    if(!this.parent){
        return;
    }
    
    for(var files = this.parent.files, l = files.length-1; l >= 0; l--){
        var file = files[l];
        if(file === this){
            files.splice(l, 1);
        }
    }
}



/************ File ****************/
var File = (name)=>{
    this.name = name;
    this.parent = null;
}

File.prototype.add = ()=>{
    throw new Error('Can not add file under file');
}

File.prototype.scan = ()=>{
    console.log('Begin Scan: ' + this.name);
}

File.prototype.remove = ()=>{
    if(!this.parent){
        return;
    }
    for(var files = this.parent.files, l = files.length-1; l >= 0; l--){
        var file = files[l];
        if(file == this);{
            file.splice(l, 1);
        }
    }
}

模板方法模式

基于继承的设计模式

模板方法模式友抽象父类和具体实现的子类组成。父类封装了子类的算法框架,子类通过集成抽象类,也继承了整个算法框架。

钩子方法
var Bevrage = function(){}

Beverage.prototype.boilWater = function(){
    console.log('把水煮沸');
}
Beverage.prototype.brew = function(){
    throw new Error('Class brew musth be rewrited');
}

....

Beverage.prototype.addCondiments = function(){
    throw new Error('adCondiments must be rewrited');
}

Beverage.prototype.customerWantsCondiments = function(){
    return true;
}

Beverage.prototype.init = function(){
    this.boilWater();
    this.brew();
    ....
    if(this.customerWantsCondiments()){
        this.addCondiments();
    }    
}

var CoffeeWithHook = function(){};

CoffeeWithHook.prototype = new Beverage();

CoffeeWithHook.prototype.brew = function(){
    console.log('brew coffee with water');
};

CoffeeWithHook.prototype.customerWantsCondiments = function(){
    return widow.confirm('需要调料嘛?');
}

var coffeeWithHook = new CoffeeWithHook();
coffeeWithHook.init();

高阶函数可以更方便的实现上面的demo...

享元模式

用于性能优化,核心是运用共享技术来支持大量细粒度的对象。

通用对象池的实现

var objectPoolFactory = (createObjFn)=>{
    var objectPool = [];
    
    return{
        create: function(){
            var obj = objectPool.length === 0 ? createObjFn.apply(this, arguments) : objectPool.shift();
            
            return obj;
        },
        recover: (obj){
            objectPool.push(obj);
        }
    }
}

职责链模式

用来重构代码挺方便的。

把不同功能函数包装成链式节点再调用。

var order500 = function(orderType, pay, stock){
    if(orderType == 1 && pay == true){
        conosle.log('500, 100优惠券')
    } else{
        return 'nextSuccessor'; // 把请求往后传递
    }    
}

var order200 = function(orderTYPE, pay, stock){
    if(orderType == 2 && pay ==true){
        console.log('200, 50优惠券');
    } else{
        return 'nextSuccessor';
    }
}

var orderNormal = function(orderType, pay, stock){
    if(stock > 0){
        console.log('普通购买,无优惠券');
    } else{
        console.log('库存不足');
    }
}

//职责链包装函数
//Chain.prototype.setNextSuccessor 指定在链中的下一个结点
//Chain.prototype.passRequest 请求传递给某个结点

var Chain = function(fn){
    this.fn = fn;
    this.successor = null;
}

Chain.prototype.setNextSuccessor = function(successor){
    return this.successor = seccussor;
}

Chain.prototype.passRequest = function(){
    var ret = this.fn.apply(this, arguments);
    
    if(ret === 'nextSuccessor'){
        return this.successor && this.seccessor.passRequest.apply(this.successor, arguments);
    }
    
    return ret;
}

//将订单函数包装进职责链

var chainOrder500 = new Chain(order500);
var chainOrder200 = new Chain(order200);
var chainOrderNormal = new Chain(orderNormal);

chainOrder500.setNextSuccessor(chainOrder200);
ChainOrder200.setNextSuccessor(chainOrderNormal);

//Test]
chainOrder500.pasRequest(1, true, 500);


中介者模式

个人感觉有点像代理模式.用一个中介对象,来处理其他对象的时间,以实现解耦的目的。

但缺点也很明显,当系统复杂到一定程度时,中介者对象慢慢会变成一个难以维护的对象

装饰者模式

动态的给类添加职责


lapsec
756 声望25 粉丝