1. Введение
- Для каждого шаблона проектирования я использую очень простой пример, чтобы всем было понятно, введение является основным, а глубина дополняется.
- В конце концов, углубленные шаблоны проектирования также должны углублять понимание в процессе фактического написания кода и оптимизации кода (то есть вам нужно столкнуться со сценариями, которые вам нужно использовать).
- Я не буду перечислять все паттерны проектирования, такие как паттерн построителя в java, я не использовал его в реальных сценариях, поэтому у меня нет глубокого понимания, поэтому мы его пропустим.
2. Что такое шаблоны проектирования
Давайте посмотрим, что такое шаблоны проектирования в Java.
Что ж, давайте использовать эту картинку как введение, давайте начнем! ! ! иди! иди! иди!
2.1 Шесть принципов шаблонов проектирования
Здесь нас интересуют только два принципа (два часто используемых принципа в js, принцип единой ответственности и принцип замыкания переключателя. Поскольку мы начинаем, чем проще контент, тем лучше)
- S(SRP) Принцип единой ответственности
Принцип единой ответственности очень прост: один метод и один класс отвечают только за одну ответственность, изменение программы каждой ответственности не повлияет на другие программы.
// 个人认为组件化思想就是单一职责原则很好的体现
// 组件的好处在于如果我们有10个页面都需要这个组件,那么我们就可以重用这个组件,复用代码
// 而且当这个组件的逻辑需要改变的时候,一般情况下我们只需要改这个组件本身的逻辑就好了,不影响其它组件
- O(SCP) Открытый Закрытый Принцип
Например, классы, модули и функции должны быть открыты для расширения, но закрыты для модификации.
// 举个例子,我最深的体会就是固定逻辑抽象出来,然后不固定的逻辑方便拓展
// 这里我们可以看到,getUserInfo就是获取用户信息的逻辑是不变的,我们就封装到一个方法里,这就是固定逻辑
const getUserInfo = function( callback ){
$.ajax('http:// xxx.com/getUserInfo', callback)
}
// 但是用获取的信息做的事情可能不同,是变化的,这就是不固定的逻辑
getUserInfo((data)=>{
console.log(data.userName)
})
getUserInfo((data)=>{
console.log(data.userId)
})
3. Создание шаблонов проектирования
На мой взгляд, творческий шаблон проектирования — это шаблон проектирования того, как создавать объекты.Например, наши общие объекты создания выглядят следующим образом
const obj = new Object()
Но в процессе собственно написания кода сложность создания объектов гораздо выше, чем приведенный выше код. Далее мы познакомим вас с общим шаблоном проектирования для создания объектов, который называется простой заводской шаблон.
3.1 Простой заводской шаблон
Я лично думаю, что основное значение этого шаблона заключается в том, что предоставляется только один фабричный метод, и нам не нужно заботиться о том, какой тип экземпляра объекта (то есть, какой конструктор является новым) для фактической сборки. (Я видел в исходном коде версии react15, что в этом режиме используется метод createElement())
// 暴露出一个工厂类,或者你也写成构造函数也行,比如说这个工厂类叫User,构造不一样的角色,不同的角色的有不一样的属性
class User {
//构造器
constructor(opt) {
this.viewPage = opt.viewPage;
this.name = opt.name;
}
//静态方法,这是内部实现工厂方法的细节
static getInstance(role) {
switch (role) {
case 'superAdmin':
return new User({ name: '超级管理员', viewPage: ['首页', '通讯录', '发现页', '应用数据', '权限管理'] });
break;
case 'admin':
return new User({ name: '管理员', viewPage: ['首页', '通讯录', '发现页', '应用数据'] });
break;
case 'user':
return new User({ name: '普通用户', viewPage: ['首页', '通讯录', '发现页'] });
break;
default:
throw new Error('参数错误, 可选参数:superAdmin、admin、user')
}
}
}
//调用
let superAdmin = User.getInstance('superAdmin');
let admin = User.getInstance('admin');
let normalUser = User.getInstance('user');
3.2, одноэлементный режим
Гарантирует наличие только одного экземпляра класса. Мы можем понять, что это значит, просто взглянув на код, шаблон singleton понять проще.
// 这是单例类
var Singleton = function( name ){
this.name = name;
};
// 暴露出一个静态方法,来获取单例
Singleton.getInstance = (function(){
var instance = null;
return function( name ){
// 第一次执行getInstance函数,因为变量instance是null,所以执行if里面的语句
// 第二次执行getInstance函数,因为变量instance是new Singleton( name ),所以执行不执行if里的语句
if ( !instance ){
instance = new Singleton( name );
}
return instance;
}
})();
Хорошо, давайте представим эти два типа шаблонов креативного проектирования. Это проще, чем петь и танцевать рэп и баскетбол? Далее мы представим шаблоны структурного проектирования.
4. Шаблоны структурного проектирования
Структурные шаблоны фокусируются на общей конечной структуре и создают более сложные структуры посредством наследования и композиции. Возьмите первый шаблон адаптера ниже, мы быстро понимаем, что называется более сложной структурой.
4.1 Режим адаптера
Это относится к преобразованию интерфейса в другой интерфейс, который вы хотите.
// 比如获取到后端传来的数据,但这个数据不是我们想要的格式
// 这个时候就可以用适配器来转换一下
function ajaxAdapter(data){
// 处理数据并返回新数据
// 通过对象解构,获取list列表,data数据假如是 {code: 200, data: {list: [{name:1}]} }
const { data: { list } } = data
return list
}
$.ajax({
url: 'http://zxx',
success(data){
doSomething(ajaxAdapter(data))
}
})
4.2, шаблон декоратора
Это означает, что исходный объект может удовлетворить более сложные потребности пользователей, упаковывая и расширяя его (добавляя атрибуты или методы) без изменения исходного объекта.
// 我们声明了一个对象小蔡,他只会打篮球
let xiaoCai={
sing(){
console.log("大家好,我是小蔡,我会打篮球");
}
};
// 我们声明了一个对象小李,他也想学小蔡打篮球
let xiaoLi={
sing(){
console.log("大家好,我是小李,我也想学打篮球");
}
};
// 小李在B站看了视频之后,也会打篮球了
// 把小李的sing方法保存起来
const xiaoLiSing = xiaoLi.sing
// 重写小李的sing方法,把小蔡的篮球技术给小李
xiaoLi.sing = () => {
xiaoLiSing()
xiaoCai.sing()
}
4.3 Режим внешнего вида
Предоставляет унифицированный интерфейс более высокого уровня для сложного набора интерфейсов подсистемы, благодаря которому упрощается доступ к интерфейсам интерфейса подсистемы.
// js里比如根据不同浏览器做兼容处理,提供统一接口
// 有的浏览器通过DOM元素的innerText属性获取其中的值,比如<div>hello</div>中hello这个文本
// 有的浏览器通过DOM元素的textContent属性获取其中的值
function getInnerText(element){
//判断element是否支持innerText
if(typeof element.innerText === 'string'){
return element.innerText;
}else{
return element.textContent;
}
}
4.4, прокси-режим
Относится к
- 1. Прокси — это объект, который можно использовать для управления доступом к другому объекту.
- 2. Прокси-объект и объект онтологии реализуют один и тот же интерфейс и передают любые вызовы методов объекту онтологии.
- 3. Простое понимание состоит в том, что вам нужно найти знаменитость, которая рекламирует ваш продукт. Телефонные звонки, которые вы делаете, обычно являются звонками знаменитых агентов, а не прямым поиском знаменитостей. Агент на самом деле агент, а знаменитость - это тело .
// 举例场景是缓存代理
// 缓存是指每次传入的参数和结果缓存到一个对象里
// 这样下一次传入同样的参数,如果之前在缓存对象里,就直接拿缓存里的数据
// 这样就不用每次都要调用函数去计算,有缓存直接用缓存,提升了效率
var plus = function(...argArray){
var a = 0;
for(var i = 0; i < argArray.length; i++){
a = a + argArray[i]
}
return a
}
// 高阶函数(将函数作为参数或者返回值的函数)包装一下上面的plus函数
var proxyFactory = function(fn) {
var cache = {}; // 参数缓存列表
return function(...argArray){
// 将传入的参数变为字符串,作为
var argString = argArray.join();
// 如果在缓存里就输出缓存内容
var argsString = String(argArray)
if(argsString in cache){
return cache[argsString]
}
// 如果没有在缓存里就保存在缓存中
return cache[argsString] = fn(...argArray)
}
}
// 测试
const proxyPlus = proxyFactory(plus)
console.log(proxyPlus(5,6,7,8))
4.5 Режим моста
- Это означает, что, хотя система изменяется по нескольким измерениям, она не увеличивает свою сложность и достигает разделения.
- Насколько я понимаю, абстрагирование общей логики в общую логику - это режим моста.
// 这个例子就是抽象层和实现层的解耦
// 提取共同点(抽象层)给每个对象都添加公共方法,即addMethod方法
Object.prototype.addMethod = function(name,fn){
this[name] = fn;
}
// 创建类并实例化对象(实现层)
function Box(x,y,z){
this.x=x;
this.y=y;
this.z=z;
}
var box=new Box(20,10,10);
// 为对象拓展方法(桥接方法)
box.addMethod("init",function(){
console.log("盒子的长度为:"+this.x+" , 宽度为:"+this.y+" , 高度为:"+this.z);
});
// 测试代码
box.init();
4.6 Комбинированный режим
- Это означает, что в программировании шаблон композиции заключается в использовании небольших подобъектов для создания более крупных объектов, и эти маленькие подобъекты сами состоят из более мелких объектов.
- Здесь только сочетание, а не принадлежность.
// 这里的场景是加入有一堆命令,通过组合模式构建更复杂的,自定义的命令集合
// 宏命令的代码
var closeDoorCommand = {//作为叶对象
execute: function(){
console.log( '关门' );
}
};
var openPcCommand = { //作为叶对象
execute: function(){
console.log( '开电脑' );
}
};
var openQQCommand = {//作为叶对象
execute: function(){
console.log( '登录QQ' );
}
};
//组合模式的根对象
var MacroCommand = function(){
return {
commandsList: [],
add: function( command ){//叶对象作为数组的元素传递到
//数组中
this.commandsList.push( command );
},
execute: function(){ //执行组合命令
for ( var i = 0, command; command = this.commandsList[ i++ ]; ){
command.execute(); //叶对象都有execute方法
}
}
}
};
var macroCommand = MacroCommand();
macroCommand.add( closeDoorCommand );//添加到根对象数组中
macroCommand.add( openPcCommand );//同上
macroCommand.add( openQQCommand );//同上
macroCommand.execute();//执行根命令
4.7 Режим наилегчайшего веса
- Технология совместного использования эффективно поддерживает большое количество мелких объектов, позволяет избежать накладных расходов на большое количество небольших классов с одинаковым содержимым (например, потребление памяти) и позволяет всем совместно использовать класс (метакласс).
// 举例对象池子
// 对象池是另外一种性能优化方案,和享元模式有一些相似之处,但没有分离内部状态和外部状态这个过程
// 建立一个对象池工厂 objectPoolFactory
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; //防止iframe重复加载的bug
iframeFactory.recover(iframe); //iframe加载完成后往对象池填回节点(收回)
};
return iframe;
});
//调用
var iframe1 = iframeFactory.create();
iframe1.src = 'http://www.qq.com';
5. Шаблоны поведенческого дизайна
Порождающие шаблоны проектирования касаются того, как создаются объекты и что можно сделать после их создания, за которыми следуют структурные шаблоны, описывающие, как объединять классы и объекты в более крупные структуры.
Наконец, в игру вступает шаблон поведенческого проектирования, который фокусируется не только на структуре классов и объектов, но и на взаимодействиях между ними.
5.1 Шаблон шаблонного метода
Он определяет скелет алгоритма в операции, откладывая при этом некоторые шаги на подклассы. Метод шаблона позволяет подклассам переопределять некоторые конкретные шаги алгоритма без изменения структуры алгоритма.Конкретная демонстрация реализации выглядит следующим образом.
// 模板方法分为两部分:1、抽象的父类 2、具体实现的子类
// 父类,我们这里举例称作饮料类,即Beverage
class Beverage{
// 冲饮料第一步需要把水煮沸
boilWater(){
console.log('把水煮沸')
}
// 冲饮料第二部需要把材料放入沸水
brew(){
}
// 冲饮料第三步把饮料倒进杯子里
addCoundiments(){
}
init(){
this.boilWater();
this.brew();
this.addCondiments();
}
}
// 子类,我们假如冲茶
class Tea extends Beverage{
// 冲饮料第二步需要把材料放入沸水
brew(){
console.log('用沸水泡茶')
}
// 冲饮料第三步把饮料倒进杯子里
addCondiments(){
console.log('把茶放入杯子里')
}
}
const tea = new Tea()
tea.init()
5.2, командный режим
Отделить запрос от реализации и инкапсулировать его в независимый объект, чтобы разные запросы могли параметризовать реализацию клиента.
// 比如二次封装react的antd组件,我几乎都是用命令模式去封装的
// 在我的运用中,这个设计模式就是用数据去驱动UI,比如用的elementUI\antd组件,是不是放入数据,视图就自动生成了呢
// 这里简单将命令模式应用于canvas绘图
const CanvasCommand = (function(){
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
const Action = {
// 填充颜色
fillStyle(c){
ctx.fillStyle = c
}
// 填充矩形
fillRect(x, y, width, height){
ctx.fillRect(x, y, width, height)
}
}
return {
execute(msg){
if(msg.length){
msg.forEach((v)=>{
Action[v.command].apply(Action, msg.param)
} )
} else {
return
}
}
}
}
})()
// 写命令
CanvasCommand.excute([
{command: 'fillStyle', param: 'red'},
{command: 'fillRect', param: [20, 20, 100, 100]}
])
5.3 Модель «публикация-подписка»
- Если вы хотите сказать, что в веб-интерфейсе есть только один шаблон проектирования, который нужно изучить, то я думаю, что это шаблон публикации-подписки.
- Я не буду говорить о его громоздком определении. Простое понимание состоит в том, что вы использовали функцию onClick для привязки событий кликов. Когда вы пишете функцию для привязки событий кликов, это называется регистрацией или подпиской на события кликов.
- Когда вы фактически нажимаете на кнопку или элемент div, который вы привязываете, это называется публикацией или запуском события, которое является применением этого шаблона проектирования.
- Его роль, такая как redux и vuex, состоит в том, чтобы решить связь между модулями, фактически, он решает связь между основным объектом и наблюдателем.
// 手写一个基于发布订阅模式的事件绑定机制
const event = {
// 事件都注册在eventList数组里
eventList: [],
/**
* 订阅事件函数
* @key {string} 订阅事件名称
* @fn {function} 订阅事件的回调函数
**/
listen(key, fn) {
if(typeof fn !== 'function'){
console.log('请添加回调函数');
return
}
if(!this.eventList[key]) {
this.eventList[key] = []
}
this.eventList[key].push(fn)
},
trigger(key, ...arg){
const fns = this.eventList[key];
if(!fns || fns.length === 0){
return false
}
fns.forEach((fn) => {
fn.apply(this. arg)
})
}
}
// 测试
event.listen('click', function(){ console.log('订阅事件1') })
event.listen('click', function(){ console.log('订阅事件2') })
event.trigger('click')
5.4, стратегический режим
- Шаблон стратегии состоит в том, чтобы определить ряд алгоритмов, инкапсулировать их один за другим и сделать их взаимозаменяемыми.
- Например, если мы недавно провели опрос, чтобы попросить пользователей сети проголосовать за нашу самую красивую фотографию в этом месяце, каждая фотография будет иметь следующие результаты: 1. Изображение будет иметь 10 звезд, а изображение с уровнем B будет отображать 10. Звезды. На фотографиях будет 9 звезд, а на фотографиях с рейтингом C — 8 звезд. . . . По аналогии, на картинке F-уровня показано 5 звезд.Обычный метод написания следующий: первый абзац кода, режим стратегии улучшен, а второй абзац кода следующий
// 常规写法
function showResult(level) {
if(level === 'A'){
return '10颗星'
}else if(level === 'B'){
return '9颗星'
}else if(level === 'C'){
return '8颗星'
}else if(level === 'D'){
return '7颗星'
}else if(level === 'E'){
return '6颗星'
}else if(level === 'F'){
return '5颗星'
}
}
console.log(showResult('F'))
Если есть 1000 вариантов if...else, то если мы судим таким образом, нам нужно судить 1000 раз, чтобы соответствовать последнему условию if...else, эффективность очень низкая, и теперь мы используем режим стратегии улучшить
// 策略模式改进,把if的条件定义为一个个的算法
const state = {
A() {
return '10颗星'
},
B() {
return '9颗星'
},
C() {
return '8颗星'
},
D() {
return '7颗星'
},
E() {
return '6颗星'
},
F() {
return '5颗星'
},
}
// 策略模式调用
function showResult(level){
return state[level]()
}
console.log(showResult('F'))
5.5, промежуточная модель
- Режим посредника в основном решает сложность связи между несколькими объектами и классами, он в основном принимает все сообщения через класс посредника, а затем пересылает их.
- Роль шаблона посредника заключается в установлении связи между объектами и объектами (это немного похоже на шаблон публикации-подписчика...)
// 写一个JS版的泡泡堂游戏, 这是自己在书上看到的一个很好的中介者模式案例
// 目前玩家是两个人,所以当其中一个玩家死亡的时候游戏便结束,同时通知它的对手胜利
function Player( name ) {
this.name = name;
this.enemy = null;
}
Player.prototype.win = function(){
console.log(this.name + 'won')
}
Player.prototype.lose = function(){
console.log(this.name + 'lost')
}
Player.prototype.die = function(){
this.lose();
this.enemy.win();
}
// 接下来创建2个玩家对象
var player1 = new Player('沈腾');
var player2 = new Player('贾玲');
// 给玩家相互设置敌人
player1.enemy = player2;
player2.enemy = player1;
// 当玩家player1被泡泡炸死的时候,只需要调用这一句代码 便完成了一局游戏
player1.die()
// 接下来,假如玩家数量变为了8个,我们的游戏代码就变成了下面这样
// 以下创建人物的代码这里不需要看懂,接着往后面看就行了,我是怕有些人跑代码跑不通,所以现把队伍的人建立起来
// 定一个数组players来保存所有玩家
var players = []
// 红队
var player1 = playerFactory('沈腾', 'read');
var player2 = playerFactory('黄海波', 'read');
var player3 = playerFactory('张全蛋', 'read');
var player4 = playerFactory('李大嘴', 'read');
// 蓝队
var player5 = playerFactory('小沈阳', 'blue');
var player6 = playerFactory('周杰', 'blue');
var player7 = playerFactory('贾乃亮', 'blue');
var player8 = playerFactory('李小璐', 'blue');
// 定义一个工厂来创建玩家
function playerFactory (name, teamColor){
var newPlayer = new Player(name, teamColor); // 创建新玩家
for(var i = 0 ; i < players.length ; i++){ // 通知所有玩家,有新角色加入
var player = players[i]
if(player.teamColor === newPlayer.teamColor){
player.partners.push(newPlayer);
newPlayer.partners.push(player);
}else{
player.enemies.push(newPlayer); // 相互添加敌人列表
newPlayer.enemies.push(player);
}
}
players.push(newPlayer);
return newPlayer;
}
// 接着再改写构造函数Player, 使每个玩家对象都增加一些属性,分别是队友列表,敌人列表,玩家当前的状态,角色名字,以及玩家所在队伍的颜色
function Player(name, teamColor){
this.partners = []; // 队友列表
this.enemies = []; // 敌人的状态
this.state = 'live'; // 玩家状态
this.name = name; // 角色名字
this.teamColor = teamColor; // 队伍颜色
}
// 玩家胜利和失败之后的展现
Player.prototype.win = function(){
console.log('winner: ' + this.name) // 玩家团队胜利
};
Player.prototype.lose = function(){
console.log('lose: ' + this.name) // 玩家团队失败
};
// 玩家死亡之后要遍历自己的队友,如果队友全部死亡则游戏结束,这局游戏失败,同时通知敌人队伍的所有玩家取得胜利,代码如下
Player.prototype.die = function(){ // 玩家死亡
var all_dead = true;
this.state = 'dead'; // 设置玩家为死亡状态
for(var i = 0, partner; partner = this.partners[i++];){
if(partner.state !== 'dead'){ // 如果还有一个队友没有死亡, 则游戏还未失败
all_dead = false;
break;
}
}
if(all_dead === true){ // 如果队友全部死亡
this.lose(); // 通知自己游戏失败
for(var i = 0; i < this.partners.length; i++){ // 通知所有队友玩家游戏失败
var partner = this.partners[i]
partner.lose()
}
for(var i = 0; i < this.enemies.length; i++ ){ // 通知所有敌人游戏胜利
var enemy = this.enemies[i]
enemy.win()
}
}
}
Код теста выглядит следующим образом:
// 让红队玩家全部死亡
player1.die();
player2.die();
player3.die();
player4.die();
Мы видим, что каждый игрок сохраняет список товарищей по команде и противников.Если в сети 10 000 человек, то этот список очень большой.Всем слишком дорого проходить этот список, поэтому нам нужен проблема, и в это время сияет модель посредника.
// 以下的playerDirector就是中介者,我们最后来实现它,先不管
// 重新定义Player类
function Player(name, teamColor) {
this.name = name; // 角色名字
this.teamColor = teamColor; // 队伍颜色
this.state = 'alive'; // 玩家生存状态
};
Player.prototype.win = function () {
console.log(this.name + ' won ');
};
Player.prototype.lose = function () {
console.log(this.name + ' lost');
};
// 玩家死亡
Player.prototype.die = function () {
this.state = 'dead';
playerDirector.reciveMessage('playerDead', this); // 给中介者发送消息,玩家死亡
};
// 移除玩家
Player.prototype.remove = function () {
playerDirector.reciveMessage('removePlayer', this); // 给中介者发送消息,移除一个玩家
};
// 玩家换队
Player.prototype.changeTeam = function (color) {
playerDirector.reciveMessage('changeTeam', this, color); // 给中介者发送消息,玩家换队
};
// 创建玩家的工厂函数改为
var playerFactory = function (name, teamColor) {
var newPlayer = new Player(name, teamColor); // 创造一个新的玩家对象
playerDirector.reciveMessage('addPlayer', newPlayer); // 给中介者发送消息,新增玩家
return newPlayer;
};
// 中介者用订阅发布这模式实现
var playerDirector = (function () {
var players = {}, // 保存所有玩家
operations = {}; // 中介者可以执行的操作
// 新增一个玩家
operations.addPlayer = function (player) {
var teamColor = player.teamColor; // 玩家的队伍颜色
players[teamColor] = players[teamColor] || []; // 如果该颜色的玩家还没有成立队伍,则
新成立一个队伍
players[teamColor].push(player); // 添加玩家进队伍
};
// 移除一个玩家
operations.removePlayer = function (player) {
var teamColor = player.teamColor, // 玩家的队伍颜色
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,
teamPlayers = players[teamColor]; // 玩家所在队伍
var all_dead = true;
for (var i = 0, player; player = teamPlayers[i++];) {
if (player.state !== 'dead') {
all_dead = false;
break;
}
}
if (all_dead === true) { // 全部死亡
for (var i = 0, player; player = teamPlayers[i++];) {
player.lose(); // 本队所有玩家 lose
}
for (var color in players) {
if (color !== teamColor) {
var teamPlayers = players[color]; // 其他队伍的玩家
for (var i = 0, player; player = teamPlayers[i++];) {
player.win(); // 其他队伍所有玩家 win
}
}
}
}
};
var reciveMessage = function () {
var message = Array.prototype.shift.call(arguments); // arguments 的第一个参数为消息名称
operations[message].apply(this, arguments);
};
return {
reciveMessage: reciveMessage
}
})();
тестовый код
我们来看下测试结果:
// 红队:
var player1 = playerFactory( '皮蛋', 'red' ),
player2 = playerFactory( '小乖', 'red' ),
player3 = playerFactory( '宝宝', 'red' ),
player4 = playerFactory( '小强', 'red' );
// 蓝队:
var player5 = playerFactory( '黑妞', 'blue' ),
player6 = playerFactory( '葱头', 'blue' ),
player7 = playerFactory( '胖墩', 'blue' ),
player8 = playerFactory( '海盗', 'blue' );
player1.die();
player2.die();
player3.die();
player4.die();
5.6 Режим итератора
- Этот шаблон используется для последовательного доступа к элементам объекта коллекции без необходимости знать базовое представление объекта коллекции.
- Итератор es6 в js — это шаблон итератора, который вполне может отражать этот шаблон проектирования.
// 数组生成迭代器需要调用它的Symbol.iterator属性,就可以以此遍历这个数组的每一项了
let arr = ['a', 'b', 'c'];
let iter = arr[Symbol.iterator]();
iter.next() // { value: 'a', done: false }
iter.next() // { value: 'b', done: false }
iter.next() // { value: 'c', done: false }
iter.next() // { value: undefined, done: true }
5.7 Модель цепочки ответственности
- Чтобы избежать связи отправителя запроса с несколькими обработчиками запросов, все обработчики запросов соединяются в цепочку через предыдущий объект, запоминая ссылку на его следующий объект; когда происходит запрос, запрос может быть передан Эта цепочка передается до тех пор, пока не будет объект для обработки.
Наш пример сценария, вероятно, таков: у компании есть определенные льготы для пользователей, которые заплатили депозит. После официальной покупки пользователи, внесшие депозит в размере 500 юаней, получат купон на 100 юаней, пользователи с депозитом в размере 200 юаней могут получить купон на 50 юаней, а пользователи, ранее не вносившие депозит, могут только войти в обычный режим покупки, то есть купонов торговых центров нет, и их наличие может быть не гарантировано, когда запасы ограничены.
Вот несколько полей, которые нам нужны
- orderType: Указывает тип заказа (различает пользователей депозита и обычных покупателей).Когда значение кода равно 1, это пользователь депозита 500 юаней, когда он равен 2, это пользователь депозита 200 юаней, а когда он равен 3, это обычный пользователь покупки.
- pay: Указывает, оплатил ли пользователь депозит.Значение является истинным или ложным.Хотя пользователь разместил заказ с депозитом в 500 юаней, если он не оплатил депозит, он может перейти только к обычному режиму покупки.
- stock: Указывает количество мобильных телефонов, обычно приобретаемых текущим пользователем.Пользователи, внесшие депозит в размере 500 или 200 юаней, не подпадают под это ограничение.
// 实现代码如下
var order = function(orderType, pay, stock){
if(orderType === 1){ // 500元定金购买模式
if(pay === true ){ // 已支付定金
console.log('500元定金预购,得到100元优惠券')
}else{
if(stock > 0){ // 用于普通购买模式
console.log('普通购买,无优惠券');
}else{
console.log('手机库存不足')
}
}
}
else if (orderType === 1){ // 200元定金购买模式
if(pay === true ){
console.log('200元定金预购,得到100元优惠券')
}else{
if(stock > 0){
console.log('普通购买,无优惠券');
}else{
console.log('手机库存不足')
}
}
}
else if (orderType === 3){ // 普通购买模式
if(stock > 0){
console.log('普通购买,无优惠券');
}else{
console.log('手机库存不足')
}
}
}
order(1, true, 500) // 输出: 500元定金预购,得到100元优惠券
Этот код очень громоздкий и трудный для чтения.В общем, я бы подумал об использовании режима стратегии для изменения условий if в алгоритмы один за другим, чтобы уменьшить большое количество суждений if...else.
// 500元订单
var order500 = function(orderType, pay, stock){
if(orderType === 1 && pay === true){
console.log('500元定金预购,得到100元优惠券')
}else{
order200(orderType, pay, stock); // 将请求传给200元的订单
}
}
// 200元订单
var order200 = function(orderType, pay, stock){
if(orderType === 2 && pay === true){
console.log('200元定金预购,得到50元优惠券')
}else{
order200(orderType, pay, stock); // 将请求传给普通订单
}
}
// 普通订单
var orderNormal = function(orderType, pay, stock){
if(stock > 0){
console.log('普通购买,无优惠券')
}else{
console.log('手机库存不足')
}
}
// 测试结果
order500(1, true, 500); // 输出: 500元定金预购,得到100优惠券
Далее, давайте сделаем еще один шаг, в конце концов, используя модель цепочки ответственности, чтобы преобразовать
// 500元订单
var order500 = function(orderType, pay, stock){
if(orderType === 1 && pay === true){
console.log('500元定金预购,得到100元优惠券')
}else{
return 'nextSuccessor'
}
}
// 200元订单
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('手机库存不足')
}
}
Затем мы пишем класс цепочки, чтобы сформировать нашу цепочку
var Chain = function( fn ){
this.fn = fn;
this.successor = null
}
Chain.prototype.setNextSuccessor = function(successor){ // 这个方法是把请求给链条的下一个
return this.successor = successor
}
Chain.prototype.passRequest = function(...args){ // 传递请求给某个节点
var ret = this.fn(...args);
if(ret === 'nextSuccessor'){
return this.successor && this.successor.passRequest(...args)
}
return ret
}
Теперь мы обернем три функции заказа как узлы цепочки ответственности:
var chainOrder500 = new Chain(order500);
var chainOrder200 = new Chain(order200);
var chainOrderNormal = new Chain(orderNormal);
Затем укажите порядок узлов в цепочке ответственности
chainOrder500.setNextSuccessor(chainOrder200);
chainOrder200.setNextSuccessor(chainOrderNormal);
Наконец передайте запрос первому узлу
chainOrder500.passRequest(1, true, 500); // 输出:500元定金预购,得到100元优惠券
chainOrder500.passRequest(2, true, 500); // 输出:200元定金预购,得到50元优惠券
chainOrder500.passRequest(3, true, 500); // 输出:普通购买,无优惠券
chainOrder500.passRequest(1, false, 0); // 输出:手机库存不足
Расширение: шаблон асинхронной цепочки ответственности
Chain.prototype.next = function(...args){
return this.successor && this.successor.passRequest(...args)
}
Асинхронная функция выглядит следующим образом
var fn1 = new Chain(function(){
console.log(1);
return 'nextSuccessor'
});
var fn2 = new Chain(function(){
console.log(2);
var self = this;
setTimeout(function(){
self.next()
})
});
var fn3 = new Chain(function(){
console.log(3);
});
// 测试
fn1.setNextSuccessor(fn2).setNextSuccessor(fn3);
fn1.passRequest()
Эпилог
- Некоторые случаи в этой статье взяты из «Шаблоны проектирования JavaScript и практика разработки» + «Шаблоны проектирования JavaScript».
- Мое собственное исследование шаблонов проектирования подошло к концу, и более глубокое понимание шаблонов проектирования требует непрерывного обучения и размышлений в реальном бою.
- Следующая цель в этом году — продвинутое углубленное изучение Node + базовые алгоритмы, поэтому я надеюсь написать статью о базовых алгоритмах внешнего интерфейса, а также node (яйцо) + mysql (в настоящее время изучается фреймворк orm) + react (dva) +webpack4 фоновая система управления проектом боя
- Дорога далека, а так много нужно узнать. . . . . До 2019 года осталось всего 5 месяцев, все! ! ! !