设计模式应用举例

简介: 纸上得来终觉浅,学习设计模式,看了很多书,但是始终还是觉得不如直接看例子来的更加客观具体,下面主要记录了js中的几个常见的设计模式举例,供自己以后复习的时候可以直接通过例子更快更好的理解设计模式。单例模式保证一个类仅有一个实例,并提供一个全局访问入口var getSingleton = fun...

纸上得来终觉浅,学习设计模式,看了很多书,但是始终还是觉得不如直接看例子来的更加客观具体,下面主要记录了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

目录
相关文章
|
1月前
|
设计模式 PHP
PHP中的设计模式:单一职责原则在软件开发中的应用
【10月更文挑战第8天】 在软件开发中,设计模式是解决常见问题的经验总结,而单一职责原则作为面向对象设计的基本原则之一,强调一个类应该只有一个引起变化的原因。本文将探讨单一职责原则在PHP中的应用,通过实际代码示例展示如何运用该原则来提高代码的可维护性和可扩展性。
33 1
|
1月前
|
设计模式 算法 搜索推荐
后端开发中的设计模式应用与实践
在软件开发的广袤天地中,后端技术如同构筑高楼大厦的钢筋水泥,支撑起整个应用程序的骨架。本文旨在通过深入浅出的方式,探讨后端开发领域内不可或缺的设计模式,这些模式犹如精雕细琢的工具箱,能够助力开发者打造出既健壮又灵活的系统架构。从单例模式到工厂模式,从观察者模式到策略模式,每一种设计模式都蕴含着深刻的哲理与实践价值,它们不仅仅是代码的组织方式,更是解决复杂问题的智慧结晶。
|
2月前
|
设计模式 数据库连接 PHP
PHP中的设计模式:提升代码的可维护性与扩展性在软件开发过程中,设计模式是开发者们经常用到的工具之一。它们提供了经过验证的解决方案,可以帮助我们解决常见的软件设计问题。本文将介绍PHP中常用的设计模式,以及如何利用这些模式来提高代码的可维护性和扩展性。我们将从基础的设计模式入手,逐步深入到更复杂的应用场景。通过实际案例分析,读者可以更好地理解如何在PHP开发中应用这些设计模式,从而写出更加高效、灵活和易于维护的代码。
本文探讨了PHP中常用的设计模式及其在实际项目中的应用。内容涵盖设计模式的基本概念、分类和具体使用场景,重点介绍了单例模式、工厂模式和观察者模式等常见模式。通过具体的代码示例,展示了如何在PHP项目中有效利用设计模式来提升代码的可维护性和扩展性。文章还讨论了设计模式的选择原则和注意事项,帮助开发者在不同情境下做出最佳决策。
|
2月前
|
设计模式 算法 测试技术
PHP中的设计模式:策略模式的应用与实践
在软件开发的浩瀚海洋中,设计模式如同灯塔,指引着开发者们避开重复造轮子的暗礁,驶向高效、可维护的代码彼岸。今天,我们将聚焦于PHP领域中的一种重要设计模式——策略模式,探讨其原理、应用及最佳实践,揭示如何通过策略模式赋予PHP应用灵活多变的业务逻辑处理能力,让代码之美在策略的变换中熠熠生辉。
|
2月前
|
设计模式 算法 数据库连接
PHP中的设计模式:提高代码的可维护性与扩展性本文旨在探讨PHP中常见的设计模式及其应用,帮助开发者编写出更加灵活、可维护和易于扩展的代码。通过深入浅出的解释和实例演示,我们将了解如何使用设计模式解决实际开发中的问题,并提升代码质量。
在软件开发过程中,设计模式是一套经过验证的解决方案模板,用于处理常见的软件设计问题。PHP作为流行的服务器端脚本语言,也有其特定的设计模式应用。本文将重点介绍几种PHP中常用的设计模式,包括单例模式、工厂模式和策略模式,并通过实际代码示例展示它们的具体用法。同时,我们还将讨论如何在实际项目中合理选择和应用这些设计模式,以提升代码的可维护性和扩展性。
61 4
|
18天前
|
设计模式 存储 数据库连接
PHP中的设计模式:单例模式的深入理解与应用
【10月更文挑战第22天】 在软件开发中,设计模式是解决特定问题的通用解决方案。本文将通过通俗易懂的语言和实例,深入探讨PHP中单例模式的概念、实现方法及其在实际开发中的应用,帮助读者更好地理解和运用这一重要的设计模式。
14 1
|
1月前
|
设计模式 PHP 开发者
PHP中的设计模式:桥接模式的解析与应用
在软件开发的浩瀚海洋中,设计模式如同灯塔一般,为开发者们指引方向。本文将深入探讨PHP中的一种重要设计模式——桥接模式。桥接模式巧妙地将抽象与实现分离,通过封装一个抽象的接口,使得实现和抽象可以独立变化。本文将阐述桥接模式的定义、结构、优缺点及其应用场景,并通过具体的PHP示例代码展示如何在实际项目中灵活运用这一设计模式。让我们一起走进桥接模式的世界,感受它的魅力所在。
|
29天前
|
设计模式 测试技术 持续交付
架构视角下的NHibernate:设计模式与企业级应用考量
【10月更文挑战第13天】随着软件开发向更复杂、更大规模的应用转变,数据访问层的设计变得尤为重要。NHibernate作为一个成熟的对象关系映射(ORM)框架,为企业级.NET应用程序提供了强大的支持。本文旨在为有一定经验的开发者提供一个全面的指南,介绍如何在架构层面有效地使用NHibernate,并结合领域驱动设计(DDD)原则来构建既强大又易于维护的数据层。
37 2
|
1月前
|
设计模式 算法 PHP
PHP中的设计模式:策略模式的深入解析与应用
【10月更文挑战第8天】 在软件开发的浩瀚宇宙中,设计模式如同星辰指引,照亮了代码设计与架构的航道。本文旨在深入探索PHP语境下策略模式(Strategy Pattern)的精髓,不仅剖析其内核原理,还将其融入实战演练,让理论在实践中生根发芽。策略模式,作为解决“如何优雅地封装算法族”的答案,以其独特的灵活性与扩展性,赋予PHP应用以动态变换行为的能力,而无需牵动既有的类结构。
22 2
|
1月前
|
设计模式 缓存 数据库连接
探索PHP中的设计模式:单例模式的实现与应用
在PHP开发中,设计模式是提高代码可复用性、可维护性和扩展性的重要工具。本文将深入探讨单例模式(Singleton Pattern)的基本概念、在PHP中的实现方式以及实际应用场景。单例模式确保一个类仅有一个实例,并提供全局访问点。通过具体代码示例和详细解释,我们将展示如何在PHP项目中有效利用单例模式来解决实际问题,提升开发效率和应用性能。