设计模式应用举例

javascript/jquery

浏览数:172

2018-12-6

纸上得来终觉浅,学习设计模式,看了很多书,但是始终还是觉得不如直接看例子来的更加客观具体,下面主要记录了js中的几个常见的设计模式举例,供自己以后复习的时候可以直接通过例子更快更好的理解设计模式。

单例模式

保证一个类仅有一个实例,并提供一个全局访问入口

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

var createLoginLayer = function(){
    var div;
    
    return function(){
        if(!div){
            div = document.createElement('div');
            div.innerText = '这是弹窗';
            div.style.display = 'none';
            document.body.appendChild(div);
        }
        
        return div;
    }
});

var singletonCreateLoginLayer = getSingleton(createLoginLayer);

document.getElementById('loginBtn').onclick = function(){
    var layer = singletonCreateLoginLayer();
    layer.style.display = 'block';
}

策略模式

定义一系列算法,并使之可以相互替换。目的就是使算法的使用和定义分离出来。

var Strategy = {
    S: function(salary){
        return salary * 4;
    },
    A: function(salary){
        return salary * 3;
    },
    B: function(salary){
        return salary * 2;
    }
};

var getBouns = function(strategy, salary){
    return Strategy[strategy](salary);
}

getBouns('B', 1000); // 2000

表单校验

var strategies = {
    isNonEmpty: function(value, errorMsg){
        ....
    },
    minLength: function(value, length, errorMsg){
        ....
    },
    isMobile: function(value, errorMsg){
        ...
    }
};

var Validator = function(){
    this.cache = [];
};

Validator.prototype.add = function(dom, rule, errorMsg){
    var ary = [rule];
    
    this.cache.push(function(){
        var strategy = ary.shift();
        ary.unshift(dom.value);
        ary.push(errorMsg);
        return strategies[strategy].apply(dom, ary);
    });
}

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

var validatorFunc = function(){
    var validator = new Validator(); // 创建一个对象
    
    validator.add(...);
    
    var errorMsg = validator.start(); // 获取校验结果
}

var registerForm = document.getElementById('form');
registerForm.onsubmit = function(){
    var errorMsg = validatorFunc();
    
    if(errorMsg){
        return false;
    }
}

代理模式

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

单一职责原则指的是,就一个类(通常也包括对象和函数等)而言,应该仅有一个引起它变 化的原因。如果一个对象 了多 职责,就意味着这个对象将变得 大,引起它变化的原因可 能会有多个。面向对象设计 将行为分 到细 度的对象之中,如果一个对象 的职责过多, 等于把这些职责耦合到了一起,这种耦合会 致 和低内聚的设计。当变化发生时,设计可能 会 到意外的 。

虚拟代理

图片懒加载

const myImg = (
  const node = documnet.createElement('img')
  document.body.appendChild(node)
  return {
    setSrc(src) {
       node.src= src
    }
  }
)()

const proxy = (
  const img = new Image()
  img.onload = () => {
    myImg.setSrc(this.src)
  }
  return {
    setImg(src) {
      img.src = src
      myImg.setSrc('loading.gif')
    }
  }
)()

观察者模式

发布订阅模式又叫观察者模式,它定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知。在JavaScript开发中,我们一般用事件模型来替代传统的发布订阅模式。

const Event = (
  function() {
    var eventList = {}
    var addEventListen
    var trigger
    var remove 
    addEventListen = function(eventName, fn) {
      eventList[eventName] = eventList[eventName] || []
      eventList[eventName].push(fn)
    }
    trigger = function() {
      var key = Array.prototype.shift.call(arguments)
      var fns = eventList[key]
      if (!fns || !fns.length) {
         return
      }
      fns.forEach((fn, index) => {
        fn.apply(this, arguments)
      })
    }
    remove = function(eventName, fn) {
      var fns = eventList[eventName]
      if (!fns || !fns.length) {
       return false
      }
      if (!fn) {
        fns.length = 0
      } else {
        fns.forEach((_fn, index) => {
          if(fn === _fn) {
            fns.splice(index, 1)
          } 
        })
      }
    }
    return {
      addEventListen,
      trigger,
      remove
    }
  }
)()

var testFn = () => {
    console.log('you have click a cancel btn')
}

Event.addEventListen('click', () => {
  console.log('you have click a button')
})

Event.addEventListen('click', () => {
  console.log('you have click button2')
})

Event.addEventListen('click', () => {
  console.log('you have click button3')
})

Event.addEventListen('click', testFn)

Event.remove('click', testFn)
Event.trigger('click')

享元模式

享元模式是为性能优化而生的,在一个存在大量相似对象的系统中,享元模式可以很好地解决大量对象带来的性能问题

文件上传

// uploadType作为内部状态,再抽离外部状态
var Upload = function(uploadType){
    this.uploadType = uploadType;
};

// 定义删除文件的方法
Upload.prototype.delFile = function(id){
    uploadManager.setExternalState(id, this); // 设置外部状态
    
    if(this.fileSize < 3000){
        return this.dom.parentNode.removeChild(this.dom);
    }
    
    if(window.confirm('确定要删除文件吗?'+ file.fileName)){
        return this.dom.parentNode.removeChild(this.dom);
    }
};

// 工厂进行对象实例化
var UploadFactory = (function(){
    var createFlyWeightObjs = {};
    return {
        create: function(uploadType){
            if(createFlyWeightObjs[uploadType]){
                return createFlyWeightObjs[uploadType];
            }
            
            return createFlyWeightObjs[uploadType] = new Upload(uploadType);
        }
    }
})();

// 管理器封装外部状态
var uploadManager = (function(){
    var uploadDataBase = {}; // 存储外部状态
    
    return {
        add: function(id, uploadType, fileName, fileSize){
            var flyWeightObj = UploadFactory.create(uploadType);
            
            var dom = document.createElement('div');
            dom.innerHTML = '...';
            document.body.appendChild(dom);
            
            uploadDataBase[id] = { // 添加外部状态
                fileName: fileName,
                fileSize: fileSize,
                dom: dom
            };
            
            return flyWeightObj;
        },
        setExternalState: function(id, flyWeightObj){ // 设置外部状态
            var uploadData = uploadDataBase[id];
            for(var i in uploadData){
                flyWeightObj[i] = uploadData[i];
            }
        }
    }
})();

var id = 0;

window.startUpload = function(uploadType, files){
    for(var i = 0, file; file = files[i++];){
        var uploadObj = uploadManager.add(++id, uploadType, file.fileName, file.fileSize);
    }
};

startUpload('plugin', [
    {
        fileName: '1.txt',
        fileSize: 1000
    },
    ...
]);

对象池

对象池维护一个装载空闲对象的池子,如果需要对象的时候,不是直接new,而是转从对象池里获取。如果对象池没有空闲对象,则创建一个新的对象,当获取出的对象完成它的职责之后,再进入池子等待被下次获取。

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

// 现在利用`ObjectPoolFactory`来创建一个装载一些`iframe`的对象池
var iframeFactory = objectPoolFactory(function(){
    var iframe = document.createElement('iframe');
    document.body.appendChild(iframe);
    
    iframe.onload = function(){
        iframe.onload = null;
        iframeFactory.recover(iframe);
    }
    
    return iframe;
});

var iframe1 = iframeFactory.create();
iframe1.src = 'http://baidu.com';

var iframe2 = iframeFactory.create();
iframe2.src = 'http://qq.com';

setTimeout(function(){
    var iframe3 = iframeFactory.create();
    iframe3.src = 'http://163.com';
}, 3000);

中介者模式

用一个中介者对象来封装一系列的对象交互。中介者使各个对象之间不会相互引用。从而使其达到松散耦合的目的。
与观察者模式对比来看,中介者模式是观察者模式中的共享被观察者对象。在这个系统中的对象之间直接的发布/订阅关系被牺牲掉了,取而代之的是维护一个通信的中心节点。

写程序是为了快速完成项目交付生产,而不是堆砌模式和过渡设计。关键就在于如何去衡量对象之间的耦合程度。如果对象之间的复杂耦合确实导致调用和维护出现了困难,而且这些耦合度随项目的变化呈指数增长曲线,那就可以考虑用中介者模式来重构代码。

function Player(name, teamColor){
    this.name = name; // 角色名字
    this.teamColor = teamColor; // 队伍颜色
    this.state = 'alive'; // 玩家生存状态
}

Player.prototype.win = function(){ 
    console.log('winner:' + this.name);
};

Player.prototype.lose = function(){ 
    console.log('loser:' + this.name);
};

Player.prototype.die = function(){
    this.state = 'dead';
    playerDirector.ReceiveMessage('playerDead', this); // 给中介者发送消息,玩家死亡
};

Player.prototype.remove = function(){
    playerDirector.ReceiveMessage('removePlayer', this);  // 给中介者发送消息,移除一个玩家
};

Player.prototype.changeTeam = function(){
    playerDirector.ReceiveMessage('changeTeam', this); // 给中介者发送消息,玩家换队
};

var playerFactory = function(name, teamColor){
    var newPlayer = new Player(name, teamColor);
    playerDirector.ReceiveMessage('addPlayer', newPlayer); // 给中介者发送消息,新增玩家
    
    return newPlayer;
};

// 实现playerDirector对象
var playDirector = (function(){
    var players = {}; // 保存所有玩家
    var operations = {}; // 中介者可以执行的操作
    
    // 新增一个玩家
    operations.add = function(player){
        var teamColor = player.teamColor;
        players[teamColor] = players[teamColor] || [];
        players[teamColor].push(player);
    };
    
    // 移除一个玩家
    operations.removePlayer = function(player){
        var teamColor = player.teamColor;
        var teamPlayers = players[teamColor] || [];
        
        for(var i=teamPlayers.length - 1; i >= 0 ;i --){
            if(teamPlayers[i] === player){
                teamPlayers.splice(i, 1);
            }
        }
    };
    
    // 玩家换队
    operations.changeTeam = function(player, newTeamColor){
        operations.removePlayer(player); // 从原队伍中删除
        player.teamColor = newTeamColor; // 换颜色
        operations.addPlayer(player); // 新增玩家到新的队伍
    }
    
    operations.playerDead = function(player){
        var teamColor = player.teamColor;
        var teamPlayer = players[teamColor];
        
        var all_dead = true;
        
        // 遍历队友列表
        for(var i=0, player; player = teamPlayer[i++];){
            if(player.state !== 'dead'){
                all_dead = false;
                break;
            }
        }
        
        // 如果队友全部死亡
        if(all_dead === true){
            this.lose();
            
            // 通知所有队友玩家游戏失败
            for(var i=0, player; player = teamPlayer[i++];){
                player.lose();
            }
            
            // 通知所有敌人游戏胜利
            for(var color in players){
                if(color !== teamColor){
                    var teamPlayers = players[color];
                    for(var i=0, player; player = teamPlayers[i++];){
                        player.win();
                    }
                }
            }
        }
    }
    
    var ReceiveMessage = function(){
        var message = Array.prototype.shift.call(arguments);
        operations[message].apply(this, arguments);
    };
    
    return {
        ReciveMessage: ReceiveMessage
    }
})();

// 创建8个玩家对象
var player1 = playerFactory('a', 'red');
var player2 = playerFactory('b', 'red');
var player3 = playerFactory('c', 'red');
var player4 = playerFactory('d', 'red');

var player5 = playerFactory('e', 'blue');
var player6 = playerFactory('f', 'blue');
var player7 = playerFactory('g', 'blue');
var player8 = playerFactory('h', 'blue');

装饰者模式

给对象动态地增加职责的方式称为装饰者模式。
装饰者模式能够在不改变对象自身的基础上,在程序运行期间给对象动态地添加职责。跟继承相比,装饰者是一种更轻便灵活的做法,这是一种“即用即付”的方式。
函数通过Function.prototype.before或者Function.prototype.after被装饰之后,返回的实际上是一个新的函数,如果在原函数上保存了一些属性,那么这些属性会丢失。
这种装饰方式也叠加了函数的作用域,如果装饰的链条过长,性能上也会受到一些影响。

Function.prototype.before = function(beforeFn){
    var _self = this; 
    return function(){ 
        if(beforefn.apply(this, arguments) === false){
            return;
        }; 
        return _self.apply(this, arguments); 
    }
};

var validata = function(){
    if(username.value === ''){
        alert('不能为空');
        return false;
    }
    
    if(password.value === ''){
        alert('不能为空');
        return false;
    }
};


var formSubmit = function(){
    var param = {
        username: username.value,
        password: password.value
    };
    
    ajax('....');
};

formSubmit = formSubimt.before(validata);

submitBtn.onclick = function(){
    formSubmit();
};

状态模式

状态模式的关键是把事物的每种状态都封装成单独的类,跟此种状态有关的行为都被封装在这个类的内部,只需要在上下文中,把某个请求委托给当前的状态对象即可,该状态对象会福州渲染它自身的行为。

// Light 类
var Light = function(){
    this.offLightState = new OffLightState(this);
    this.weekLightState = new WeekLightState(this);
    this.strongLightState = new StrongLightState(this);
    this.button = null;
};

Light.prototype.init = function(){
    var button = document.createElement('button');
    var self = this;
    
    this.button = document.body.appendChild(button);
    this.button.innerHTML = '开关';
    
    this.currState = this.offLightState;
    
    this.button.onclick = function(){
        self.currState.buttonWasPressed();
    };
};
// offLightState
var OffLightState = function(light){
    this.light = light;
};

OffLightState.prototype.buttonWasPressed = function(){
    console.log('弱光') // offLightState 对应的行为
    this.light.setState(this.light.weekLightState); // 切换状态到 weekLightState
};

文中代码主要来自曾探老师的《JavaScript设计模式与开发实践》,书中的内容关于设计模式写的更加详实细致,如果想学习设计模式推荐这本书入门哈!

原文地址:https://segmentfault.com/a/1190000017278544