Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

设计模式 #44

Open
xiaotiandada opened this issue Jan 19, 2021 · 0 comments
Open

设计模式 #44

xiaotiandada opened this issue Jan 19, 2021 · 0 comments

Comments

@xiaotiandada
Copy link
Owner

2020-02-26 23:57:00

第一次学习设计模式 repo 所有的代码都在这儿啦!

工厂模式

相关资料:

JavaScript设计模式总结

js设计模式-工厂模式

创建三个角色然后随机决斗⚔️

console.log('工厂模式');

// 攻击力1-100
const attackPower = () => Math.floor(Math.random() * 100 + 1)

// 战士
class Warrior {
  constructor() {
    this.occupation = '战士'
    this.skill = '单一狂砍'
    this.blood = 100
    this.hit = attackPower()
    // other
  }
}

// 法师
class Mage {
  constructor() {
    this.occupation = '法师'
    this.skill = '集体冰冻'
    this.blood = 100
    this.hit = attackPower()
  }
}

// 射手
class Archer {
  constructor() {
    this.occupation = '射手'
    this.skill = '全局轰炸'
    this.blood = 100
    this.hit = attackPower()
  }
}

// 工厂对象
// class、function、object
class RoleFactory {
  constructor() {}
  createRole(role) {
    let roles = {
      Warrior: Warrior,
      Mage: Mage,
      Archer: Archer
    }

    const Character = roles[role]
    return role ? new Character() : new Warrior()

  }
}

// 创建角色
let roleFactory = new RoleFactory
let warrior = roleFactory.createRole('Warrior')
let mage = roleFactory.createRole('Mage')
let archer = roleFactory.createRole('Archer')

console.log('warrior:', warrior);
console.log('mage:', mage);
console.log('archer:', archer);

console.log('----------')

// 随机角色
const randomRole = (data, number) => {
  if (!data || !data.length || !number) return

  let randomRole = []

  for (let i = 0; i < data.length; i++) {
    let sub = Math.floor(Math.random() * data.length )
    randomRole.push(...data.splice(sub, 1))
  }
  return randomRole
}

// 战斗
const duel = roles => {
  // 最强角色
  let maxRole = null
  // 最高攻击力
  let maxHit = -1
  roles.map(item => {
    console.log(item)
    // 如果攻击力大于最大攻击力
    if (item.hit > maxHit) {
      // 设置当前角色
      maxRole = item
      // 攻击力也替换
      maxHit = item.hit
    } else if (item.hit === maxHit) {
      // 清空
      maxRole = null
      maxHit = -1
    }

  })

  return maxRole
}

const compose = (...fn) => fn.reduce((a, b) => (...args) => a(b(...args)))

let winner = compose(duel, randomRole)([warrior, mage, archer], 2)

if (winner) {
  console.log(`胜利者是: ${winner.occupation}, 他的技能是: ${winner.skill}, 攻击力: ${winner.hit}`)
} else {
  console.log(`这是平局`)
}

输出

工厂模式
warrior: Warrior { occupation: '战士', skill: '单一狂砍', blood: 100, hit: 1 }
mage: Mage { occupation: '法师', skill: '集体冰冻', blood: 100, hit: 39 }
archer: Archer { occupation: '射手', skill: '全局轰炸', blood: 100, hit: 33 }
----------
Warrior { occupation: '战士', skill: '单一狂砍', blood: 100, hit: 1 }
Mage { occupation: '法师', skill: '集体冰冻', blood: 100, hit: 39 }
胜利者是: 法师, 他的技能是: 集体冰冻, 攻击力: 39

因为第一次学习 还不太知道具体的优点和好处 也没办法口喷 所以暂时留个坑给自己 下次更加了解之后回来填坑 🍑

单例模式

https://juejin.im/post/5c984610e51d45656702a785

https://fanerge.github.io/2017/js%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F-%E5%8D%95%E4%BE%8B%E6%A8%A1%E5%BC%8F.html

定义:单例就是保证一个类只有一个实例,实现的方法一般是先判断实例存在与否,如果存在直接返回,如果不存在就创建了再返回,这就确保了一个类只有一个实例对象。 -- 复制的

我的理解为一个类仅有一个实例, 并且全局访问都是相同的实例

建议先看完上面文章的单例模块, 我觉得他们总结得比我要好

// 登录框
/**
* 我这里单击按钮🔘显示登录框, 并且自身设置关闭事件 只是隐藏
* 在下一次显示登录框的时候不会创建 而是直接展示 因为单例模式
* 登录框已经创建了 所以直接返回了对象 然后展示
*/
const loginBox = (function(){
  let div
  return function() {
    if (!div) {
      div = document.createElement('div')
      div.onclick = function() {
        div.style.display = 'none'
      }
      div.innerHTML = '登录 hhhh (单击我隐藏)'
      document.body.appendChild(div)
    }
    return div
  }
})()

document.querySelector('#btn').onclick = function() {
  let loginbox = loginBox()
  loginbox.style.display = 'block'
}

这只是一个简单的示范... 🍑

装饰器模式

https://juejin.im/post/5c984610e51d45656702a785

在不改变对象自身的基础上,动态的给某个对象添加新的功能,同时又不改变其接口

https://fanerge.github.io/2017/js%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F-%E8%A3%85%E9%A5%B0%E8%80%85%E6%A8%A1%E5%BC%8F.html

定义:装饰者(decorator)模式能够在不改变对象自身的基础上,在程序运行期间给对象动态的添加职责。

这样给对象动态的增加职责的方式就没有改变对象自身,一个对象放入另一个对象就形成了一条装饰链(一个聚合对象), 而上面的shot和track也就是装饰者、装饰函数 ,当函数执行时,会把请求转给链中的下一个对象。

{
  class Plane {
    fire() {
      console.log('发射普通子弹')
    }
  }

  class Missile {
    plane: any
    constructor(plane) {
      this.plane = plane
    }

    fire () {
      this.plane.fire()
      console.log('发射导弹')
    }
  }

  let plane = new Plane()
  plane = new Missile(plane)
  console.log(plane.fire())
}
// 利用AOP给函数动态添加功能,即Function的after或者before
{
  let func = function() {
    console.log('2')
  }

  Function.prototype.before = function(fn) {
    const _this = this
    return function() {
      fn.apply(this, arguments)
      return _this.apply(this, arguments)
    }
  }
  Function.prototype.after = function(fn) {
    const _this = this
    return function() {
      const ret = _this.apply(this, arguments)
      fn.apply(this, arguments)
      return ret
    }
  }
  
  func = func.before(function() {
    console.log(1)
  }).after(() => {
    console.log(3)
  });
  
  func()
}
{
  // 利用装饰器
  function func1() {
    console.log(1)
  }
  function func2() {
    console.log(2)
  }
  class Func {
    @func2
    @func1
    init() {
      console.log(3)
    }
  }

  let func = new Func()
  func.init()

}
{
  // 封装成函数
  const before = function (fn, before) {
    return function() {
      before.apply(this, arguments)
      return fn.apply(this, arguments)
    }
  }

  const after = function(fn, after) {
    return function() {
      const ret = fn.apply(this, arguments)
      after.apply(this, arguments)
      return ret
    }
  }

  function func(x) {
    console.log(x)
  }

  function func1(2) {
    console.log(1)
  }


  function func2() {
    console.log(2)
  }


  before(func1, func2)()
  after(func1, func2)()
}

核心就在after before

{
 // ...
 function handwashing() {
   console.log('洗手')
 }

 function drink() {
   console.log('喝水')
 }

 function eat() {
   console.log('吃饭')
 }


 before(eat, handwashing)()
 after(eat, drink)()
 
 // 吃饭前洗手
 // 吃饭后喝水
}

比较基础的演示了 :)

代理模式

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

https://fanerge.github.io/2017/js%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F-%E4%BB%A3%E7%90%86%E6%A8%A1%E5%BC%8F.html

// 缓存代理
const mult = function() {
  let a = 1

  for (let i = 0; i < arguments.length; i++) {
    a *= arguments[i]
  }

  return a
}


const plus = function() {
  let a = 0

  for (let i = 0; i < arguments.length; i++) {
    a += arguments[i]
  }

  return a
}


const createProxyFactory = function(fn) {
  let cache = {} // 保存计算结果
  return function() {
    let args = Array.from(arguments).join(',')
    if (args in cache) {
      return cache[args]
    } else {
      return cache[args] = fn.apply(this, arguments)
    }
  }
}


let proxyMult = createProxyFactory(mult)
let proxyPlus = createProxyFactory(plus)

console.log(proxyMult(1,2,3,4))
console.log(proxyPlus(1,2,3,4))

这里每次进行同类的计算时(乘法和加法两类),先判断缓存对象cache中是否存在该参数连接成的字符串作为key的属性。
如果有,则直接从cache中读取,否则就进行计算并保存其结果。

// 虚拟代理

const sleep = time => new Promise(resolve => setTimeout(resolve, time))

const imgFunc = (function() {
  let imgNode = document.createElement('img')
  document.body.appendChild(imgNode)

  return {
    setSrc(src) {
      imgNode.src = src
    }
  }
})()


const proxyImage = (function() {
  const img = new Image()
  img.onload = function() {
    imgFunc.setSrc(this.src)
  }

  return {
    setSrc(src) {
      imgFunc.setSrc('https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=766379944,3048822499&fm=26&gp=0.jpg')
      // 模拟加载时间
      setTimeout(() => {
        img.src = src
      }, 2000)
    }
  }
})()

proxyImage.setSrc('http://t8.baidu.com/it/u=1484500186,1503043093&fm=79&app=86&f=JPEG?w=1280&h=853')

图片懒加载的方式:先通过一张loading图占位,然后通过异步的方式加载图片,等图片加载好了再把完成的图片加载到img标签里面。

策略模式

https://fanerge.github.io/2017/js%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F-%E7%AD%96%E7%95%A5%E6%A8%A1%E5%BC%8F.html

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

const strategy = {
  a(salary) {
     return salary * 4
  },
  b(salary) {
    return salary * 3
  },
  c(salary) {
    return salary * 2
  }
}


Object.freeze(strategy)


const returnMoney = (type, salary) => {
  return strategy[type](salary)
}

console.log(returnMoney('a', 1000))
console.log(returnMoney('b', 3000))
console.log(returnMoney('c', 4000))

简单的使用

ps: 策略模式指的是定义一系列的算法,把它们一个个封装起来,将不变的部分和变化的部分隔开,
实际就是将算法的使用和实现分离出来;

表单验证

我在原有的code上加了一个方法(错误展示, 比较基础所以很多地方没有考虑到)

<iframe height="265" style="width: 100%;" scrolling="no" title="策略模式" src="https://codepen.io/xiaotiandada/embed/xxZOBvG?height=265&theme-id=dark&default-tab=js,result" frameborder="no" allowtransparency="true" allowfullscreen="true"> See the Pen 策略模式 by xiaotiandada (@xiaotiandada) on CodePen. </iframe>
<!-- ... -->
<style>
  .error-msg {
    color: red;
    font-size: 14px;
  }
</style>

<form id="registerForm">
  <div>
    <label for="">用户名</label>
    <input type="text" name="username">
  </div>
  <div>
    <label for="">密码</label>
    <input type="password" name="password">
  </div>
  <div>
    <label for="">手机号码</label>
    <input type="text" name="phoneNumber">
  </div>
  <button>submit</button>
</form>
<!-- ... -->
// 这里我们实现一组策略类封装具体的验证规则
const strategyForm = {
	// 是否为空
	isNotEmpty (value, errorMsg){
		if (value === '') {
			return errorMsg;
		}
	},
	// 最小长度
	minLength (value, length, errorMsg){
		if (value.length < length) {
			return errorMsg;
		}
	},
	// 手机号码格式
	mobileFormat (value,errorMsg){
		if(!/(^1[3|5|8][0-9]{9}$)/.test(value)) {
			return errorMsg;
		}
	}
};
Object.freeze(strategyForm);


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

Validator.prototype.add = function(dom, rules) {
	let slef = this
	// 添加错误信息提示
	const addErrorMsg = (msg, dom, errorMsg) => {
		// 如果有错误信息
		if (msg) {
			// 如果已经有了错误提示
			let errorMsgDom = dom.parentNode.querySelector('.error-msg')
			if (errorMsgDom) {
				errorMsgDom.innerHTML = errorMsg
			} else {
				// 没有提示创建
				let errorHtml = document.createElement('span')
				errorHtml.className = 'error-msg'
				errorHtml.innerHTML = errorMsg
				dom.parentNode.appendChild(errorHtml)
			}
		} else {
			// 没有错误提示
			// 如果有错误提示但是tag还存在则清除
			let errorMsgDom = dom.parentNode.querySelector('.error-msg')
			if (errorMsgDom) {
				errorMsgDom.remove()
			}
		}
	}
	for(let i = 0, rule; rule = rules[i++]; ) {
		(function(rule) {
			let strategyArr = rule.strategy.split(':')
			let errorMsg = rule.errorMsg

			slef.cache.push(function() {
				let strategy = strategyArr.shift() // 取第一个 策略名
				strategyArr.unshift(dom.value)
				strategyArr.push(errorMsg)
				let msg = strategyForm[strategy].apply(dom, strategyArr)
				addErrorMsg(msg, dom, errorMsg)
				return msg
			})

		})(rule)
	}
}

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

let registerForm = document.getElementById('registerForm')

let validateFunc = function() {
	let validator = new Validator()
	validator.add(registerForm.username, [
		{ strategy: 'isNotEmpty', errorMsg: '用户名不能为空' },
		{ strategy: 'minLength:6', errorMsg: '用户名长度不能小于6位' }
	])
	validator.add(registerForm.password,[
		{strategy: 'minLength:6',errorMsg:'密码长度不能小于6位'},
	]);
	validator.add(registerForm.phoneNumber,[
		{strategy: 'mobileFormat',errorMsg:'手机号格式不正确'},
	]);
	let errorMsg = validator.start()
	return errorMsg
}

registerForm.onsubmit = function() {
	let errorMsg = validateFunc()
	if (errorMsg) {
		console.log(errorMsg)
	} else {
		console.log('done')
	}
	return false
}

中介者模式

https://fanerge.github.io/2017/js%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F-%E4%B8%AD%E4%BB%8B%E8%80%85%E6%A8%A1%E5%BC%8F.html

定义:中介者模式的作用就是解除对象与对象之间的紧耦合关系

// MVC 模式
let M = {}
let V = {}
let C = {}

M.data = 'Hello World'
V.render = (M) => {
    // alert(M.data)
    document.body.append(document.createElement('p').innerHTML = M.data)
}
C.handleOnload = () => {
    V.render(M)
}


window.onload = C.handleOnload
<iframe height="265" style="width: 100%;" scrolling="no" title="中介者模式" src="https://codepen.io/xiaotiandada/embed/abdBOvp?height=265&theme-id=dark&default-tab=js,result" frameborder="no" allowtransparency="true" allowfullscreen="true"> See the Pen 中介者模式 by xiaotiandada (@xiaotiandada) on CodePen. </iframe>
<form action="#" id="selectPhone">
  <div>
    <label for="">选择手机颜色</label>
    <select name="" id="colorSelect">
      <option value="">请选择</option>
      <option value="red">红色</option>
      <option value="blue">蓝色</option>
    </select>
  </div>
  <div>
    <label for="">选择内存</label>
    <select name="" id="memorySelect">
      <option value="">请选择</option>
      <option value="16G">16G</option>
      <option value="32G">32G</option>
    </select>
  </div>
  <div>
    <label for="">输入购买数量</label>
    <input type="number" min="0" id="numberInput">
  </div>
  <div>
    您选择了颜色: <div id="colorInfo"></div><br/>
    您选择了内存: <div id="memoryInfo"></div><br/>
    您输入了数量: <div id="numberInfo"></div><br/>
    <button id="nextBtn" disabled="true">请选择手机颜色和购买数量</button>
  </div>
</form>
let goods = {
   "red|32G": 3,
	"red|16G": 0,
	"blue|32G": 1,
	"blue|16G": 6
}

// 获得所有节点的引用,以便对其进行操作(中介者必许获得对其他对象的引用)
let colorSelect = document.getElementById( 'colorSelect' )
let memorySelect = document.getElementById( 'memorySelect' )
let numberInput = document.getElementById( 'numberInput' )

let colorInfo = document.getElementById( 'colorInfo' )
let memoryInfo = document.getElementById( 'memoryInfo' )
let numberInfo = document.getElementById( 'numberInfo' )
let nextBtn = document.getElementById( 'nextBtn' )

let mediator = (function() {
    return {
        changed(obj) {
            var color = colorSelect.value // 颜色
            let memory = memorySelect.value// 内存
            let number = numberInput.value // 数量

            if (obj === colorSelect) {
                colorInfo.innerHTML = color
            } else if (obj === memorySelect) {
                memoryInfo.innerHTML = memory
            } else if (obj === numberInput) {
                numberInfo.innerHTML = number
            } else {
                console.log(obj)
            }

            if (!color) {
                nextBtn.disabled = true
                nextBtn.innerHTML = '请选择手机颜色'
                return
            }

            if (!memory) {
                nextBtn.disabled = true
                nextBtn.innerHTML = '请选择内存大小'
                return
            }

            if (((number - 0) | 0 !== number - 0)) {
                nextBtn.disabled = true
                nextBtn.innerHTML = '请输入正确的购买数量'
                return
            }

            nextBtn.disabled = false;
			nextBtn.innerHTML = '放入购物车';

        }
    }
})()


// 与中介者联系起来,事件函数
colorSelect.onchange = function(){
	mediator.changed( this );
};
memorySelect.onchange = function(){
	mediator.changed( this );
};
numberInput.onchange = function(){
	mediator.changed( this );
};

发布订阅模式

// https://fanerge.github.io/2017/js%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F-%E5%8F%91%E5%B8%83%E8%AE%A2%E9%98%85%E6%A8%A1%E5%BC%8F.html

// 对象间的一种一对多的关系,让多个观察者对象同时监听某一个主题对象,当一个对象发生改变时,所有依赖于它的对象都将得到通知。

const Event = (function() {
  // 缓存列表
  let list = {}

  // 监听函数
  const listen = function(key, fn) {
    if (!list[key]) {
      list[key] = []
    }
    list[key].push(fn)
  }

  // 触发监听
  const trigger = function() {
    let key = Array.prototype.shift.call(arguments)
    let fns = list[key]

    if (!fns || fns.length === 0) {
      return false
    }

    for (let i = 0, fn; fn = fns[i++];) {
      fn.apply(this, arguments)
    }
  }
  // 移除监听函数
  const remove = function(key, fn) {
    let fns = list[key]

    if (!fns) {
      return false
    }

    if (!fn) {
      fns && (fns.length = 0)
    } else {
      for (let i = fns.length - 1; i >= 0; i--) {
        let _fn = fns[i]
        if (_fn === fn) {
          fns.splice(i, 1)
        }
      }
    }
  }

  return {
    listen,
    trigger,
    remove
  }
})()


function d1() {
  console.log('d11111')
}

function d2() {
  console.log('d22222')
}

function d3() {
  console.log('d33333')
}

Event.listen('color', d1)
Event.listen('color', d2)
Event.listen('color', d3)


Event.listen('size', d1)
Event.listen('size', d2)
Event.remove('size', d1)
Event.listen('size', d3)

Event.trigger('color')
console.log('----')
Event.trigger('size')
console.log('----')
Event.trigger('color')


// d11111
// d22222
// d33333
// ----
// d22222
// d33333
// ----
// d11111
// d22222
// d33333

迭代器模式

// https://fanerge.github.io/2017/js%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F-%E8%BF%AD%E4%BB%A3%E5%99%A8%E6%A8%A1%E5%BC%8F.html
// 定义:迭代器模式是指提供一种方法顺序访问一个聚合对象中的各个元素,而又不需要暴露该对象的内部表示。

// 内部迭代器

const each = (array, callback) => {
  for (let i = 0, len = array.length; i < len; i++) {
    if (callback.call(array[i], array[i], i) === false) {
      break
    }
  }
}

each([1,2,3,4,5], (val, i) => {
  console.log(val + ' - ' + i)
})
console.log('-----')
each([1,2,3,4,5], (val, i) => {
  if (i === 3) {
    return false
  }
  console.log(val + ' - ' + i)
})

// 外部迭代器

const Iterator = obj => {
  let current = 0;

  const next = () => {
    if (current > obj.length) {
      return false
    }
    current += 1;
  }

  const isDone = () => {
    return current >= obj.length
  }

  const getCurrentItem = () => {
    return obj[current]
  }

  return {
    next,
    isDone,
    getCurrentItem
  }
}

console.log('-----')

let iterator = Iterator([1,2,3,4,5])
console.log(iterator.getCurrentItem())
console.log(iterator.getCurrentItem())

iterator.next()

console.log(iterator.getCurrentItem())
console.log(iterator.isDone())

iterator.next()
console.log(iterator.getCurrentItem())
iterator.next()
console.log(iterator.getCurrentItem())
iterator.next()
console.log(iterator.getCurrentItem())
iterator.next()
console.log(iterator.getCurrentItem())
iterator.next()

console.log(iterator.isDone())



// 1 - 0
// 2 - 1
// 3 - 2
// 4 - 3
// 5 - 4
// -----
// 1 - 0
// 2 - 1
// 3 - 2
// -----
// 1
// 1
// 2
// false
// 3
// 4
// 5
// undefined
// true

桥接模式

https://fanerge.github.io/2017/js%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F-%E6%A1%A5%E6%8E%A5%E6%A8%A1%E5%BC%8F.html

定义:桥接模式(Bridge)将抽象部分与它的实现部分分离,使它们都可以独立地变化。

外观模式

<button id="button">外观模式</button>
<div id="foo"></div>
<div id="foo1"></div>
<div id="foo2"></div>
// https://fanerge.github.io/2017/js%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F-%E5%A4%96%E8%A7%82%E6%A8%A1%E5%BC%8F.html
// https://natee.gitbooks.io/javascript-design-patterns/facade-pattern.html
// 定义:外观模式(Facade)为子系统中的一组接口提供了一个一致的界面,此模块定义了一个高层接口,这个接口使得这一子系统更加容易使用。


const addEvent = function(el, ev, fn) {
  if (el.addEventListener) {
    el.addEventListener(ev, fn, false)
  } else if (el.attachEvent) {
    el.attachEvent(`on${ev}`, fn)
  } else {
    el[`on${ev}`] = fn
  }
}

// 没测试
let N = window.N || {}
N.tools = {
  stopPropagation(e) {
    if (e.stopPropagation) {
      e.stopPropagation()
    } else {
      e.cancelBubble = true
    }
  },
  preventDefault(e) {
    if (e.preventDefault) {
      e.preventDefault()
    } else {
      e.returnValue = false
    }
  },
  stopEvent(e) {
    this.stopPropagation(e)
    this.preventDefault(e)
  }
}


addEvent(document.getElementById('button'), 'click', function() {
  console.log('button')
})



function setStyles(elements, styles) {
  for (let i = 0, len = elements.length; i < len; i++) {
    let element = document.getElementById(elements[i])
    if (element) {
      for (let property in styles) {
        element.style[property] = styles[property]
      }
    }
  }
}

setStyles(['foo', 'foo1', 'foo2'], {
  backgroundColor: 'red',
  width: '150px',
  height: '200px'
});

访问者模式

// https://fanerge.github.io/2017/js%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F-%E8%AE%BF%E9%97%AE%E8%80%85%E6%A8%A1%E5%BC%8F.html
// 没太理解...

function Visitor () {
  this.visit  = function(ConceteElement) {
    ConceteElement.doSomething()
  }
}

function ConceteElement() {
  this.doSomething = function() {
    console.log('this is a element')
  }

  this.accept = function(visitor) {
    visitor.visit(this)
  }
}


let visitor = new Visitor()
let conceteElement = new ConceteElement()

conceteElement.accept(visitor)

模版方法模式

定义:模板方法模式由二部分组成,第一部分是抽象父类,第二部分是具体实现的子类,一般的情况下是抽象父类封装了子类的算法框架,包括实现一些公共方法及封装子类中所有方法的执行顺序,子类可以继承这个父类,并且可以在子类中重写父类的方法,从而实现自己的业务逻辑。

// https://fanerge.github.io/2017/js%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F-%E6%A8%A1%E7%89%88%E6%96%B9%E6%B3%95%E6%A8%A1%E5%BC%8F.html


const ITInterview = function() {}

ITInterview.prototype.writeTest = function() {
  console.log('this is a write test')
}

ITInterview.prototype.technicalInterView = function() {
  console.log('this is a technical interview')
}

ITInterview.prototype.leader = function() {
  console.log('this is a leader interview')
}

ITInterview.prototype.waitNotice = function() {
  console.log('wait notice')
}

ITInterview.prototype.init = function() {
  this.writeTest()
  this.technicalInterView()
  this.leader()
  this.waitNotice()
}

const itInterview = new ITInterview()
itInterview.init()


// baidu
const BaiDuITInterview = function() {}
BaiDuITInterview.prototype = new ITInterview()

BaiDuITInterview.prototype.writeTest = function() {
  console.log('this is a baidu write test')
}

BaiDuITInterview.prototype.technicalInterView = function() {
  console.log('this is a baidu technical interview')
}


const baiduItInterview = new BaiDuITInterview()
baiduItInterview.init()

组合模式

定义:组合模式(Composite)将对象组合成树形结构以表示“部分-整体”的层次结构,组合模式使得用户对单个对象和组合对象的使用具有一致性。

// https://fanerge.github.io/2017/js%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F-%E7%BB%84%E5%90%88%E6%A8%A1%E5%BC%8F.html
// 定义:组合模式(Composite)将对象组合成树形结构以表示“部分-整体”的层次结构,组合模式使得用户对单个对象和组合对象的使用具有一致性。


// 定义组合对象(文件夹)
let Folder = function(name) {
  this.name = name
  this.files = []
}

Folder.prototype.add = function(file) {
  this.files.push(file)
}

Folder.prototype.scan = function() {
  console.log('开始文件扫描:' + this.name)
	for( let i = 0, file, files = this.files; file = files[i++]; ){
		file.scan();
	}
} 

//定义叶子对象(文件)
let File = function(name) {
  this.name = name
}

File.prototype.add = function() {
  throw new Error('文件下面不能再添加文件')
}
File.prototype.scan = function() {
  console.log('开始扫瞄:' + this.name)
}


let folder = new Folder('前端学习');
let folder1 = new Folder('JS学习');
let folder2 = new Folder('JQ学习');

let file1 = new File('JS设计模式');
let file2 = new File('JQ实战');
let file3 = new File('前端性能');

folder1.add(file1);
folder2.add(file2);

folder.add(folder1);
folder.add(folder2);
folder.add(file3);
folder.scan();

// 开始文件扫描:前端学习
// 开始文件扫描:JS学习
// 开始扫瞄:JS设计模式
// 开始文件扫描:JQ学习
// 开始扫瞄:JQ实战
// 开始扫瞄:前端性能

备忘录模式

定义:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样就可以将该对象恢复到原先保存的状态

// https://fanerge.github.io/2017/js%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F-%E5%A4%87%E5%BF%98%E5%BD%95%E6%A8%A1%E5%BC%8F.html
// 定义:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样就可以将该对象恢复到原先保存的状态

const render = data => {
  console.log(data)
}

const page = function () {
  let cache = {}

  return (page) => {
    console.log('page', page)
    console.log('cache', JSON.stringify(cache))

    if (cache[page]) {
      render(cache[page])
    } else {
      let data = [
        {
          title: "hi"
        }
      ]

      cache[page] = data
      render(data)
    }
  }
}()



page(1)
page(1)
page(1)
page(1)

page(1)
page(2)
page(3)
page(4)

page(2)

职责链模式

// https://fanerge.github.io/2017/js%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F-%E8%81%8C%E8%B4%A3%E9%93%BE%E6%A8%A1%E5%BC%8F.html

// 定义:职责链模式(Chain of responsibility)是使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系。将这个对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理他为止。职责链模式的名字非常形象,一系列可能会处理请求的对象被该连接成一条链,请求在这些对象之间依次传递,直到遇到一个可以处理它的对象,我们把这些对象成为链中的节点。

// 500 元客户订单
var order500 = function (orderType, pay, stock) {
  if (orderType === 1 && pay) {
    console.log('500 rmb deposit, get 100 coupon ')
  } else {
    return 'nextSuccessor' // unknow the next node but always pass to next.
  }
};
// 200 元客户订单
var order200 = function (orderType, pay, stock) {
  if (orderType === 2 && pay) {
    console.log('200 rmb deposit , get 50 coupon')
  } else {
    return 'nextSuccessor';
  }
};
// 无预约客户订单
var orderNormal = function (orderType, pay, stock) {
  if (stock > 0) {
    console.log('normal buy no coupon')
  } else {
    console.log('the stock lack')
  }
};

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

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

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



// 现在我们把3个订单函数分别包装成职责链的节点
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,6); // 500 rmb deposit, get 100 coupon
chainOrder500.passRequest(2,true,4); // 200 rmb deposit , get 50 coupon

chainOrderNormal.passRequest(2,true,0); // 200 rmb deposit , get 50 coupon

状态模式

// https://fanerge.github.io/2017/js%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F-%E7%8A%B6%E6%80%81%E6%A8%A1%E5%BC%8F.html
// 定义:状态模式(State)定义一个对象,这个对象可以通过管理其状态从而使得应用程序作出相应的变化。

var trafficLight = (function () {
	var currentLight = null;
	return {
		change: function (light) {
			currentLight = light;
			currentLight.go();
		}
	}
})();


// 红灯
function RedLight() { }
RedLight.prototype.go = function () {
	console.log("this is red light");
}
// 绿灯
function GreenLight() { }
GreenLight.prototype.go = function () {
	console.log("this is green light");
}
// 黄灯
function YellowLight() { }
YellowLight.prototype.go = function () {
	console.log("this is yellow light");
}

trafficLight.change(new RedLight()); // this is red light
trafficLight.change(new YellowLight()); // this is yellow light


function Menu() { }
Menu.prototype.toggle = function (state) {
	state();
}

var menuStates = {
	"show": function () {
		console.log("the menu is showing");
	},
	"hide": function () {
		console.log("the menu is hiding");
	}
};

var menu = new Menu();
menu.toggle(menuStates.show);
menu.toggle(menuStates.hide);

享元模式

// https://fanerge.github.io/2017/js%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F-%E4%BA%AB%E5%85%83%E6%A8%A1%E5%BC%8F.html

// 定义:享元模式是一种用于性能优化的模式,如果系统中因为创建了大量类似的对象而导致内存不足或占用过高这种模式就非常有用了。

// 定义塑料模特的构造函数
var Model = function (sex) {
	this.sex = sex;
}
// 为模特拍照
Model.prototype.takePhoto = function () {
	console.log('sex=' + this.sex + 'underwear=' + this.underwear )
}
// 实例化一个男模特 和 一个女模特
var maleModel = new Model('male');
let female    = new Model('female');    
for (var i = 1; i <=50; i++){
	// 分别为模特换上 50 件内衣 以及 照相
	maleModel.underwear = 'underwear' + i;
	maleModel.takePhoto();
}
for (var i = 1; i <=50; i++){
	// 分别为模特换上 50 件内衣 以及 照相
	female.underwear = 'underwear' + i;
	female.takePhoto();
}




let toolTipFactory = (function() {
  let tooplTipPool = []
  return {
    create: function() {
      if (tooplTipPool.length === 0) {
        console.log(1)
        let div = document.createElement('div')
        document.body.appendChild(div)
        return div
      } else {
        console.log(2)
        return tooplTipPool.shift()
      }
    },
    recover: function(tooltipDOm) {
      tooplTipPool.push(tooltipDOm)
    }
  }
})()

// 2
let arr = []
for (let i = 0, str; str = ['a', 'b'][i++];) {
  let toolTip = toolTipFactory.create()
  toolTip.innerHTML = str
  arr.push(toolTip)
}


for (let i = 0, toolTip; toolTip = arr[i++];) {
  toolTipFactory.recover(toolTip)
}

// 11
// 22
// 1111
for (let i = 0, str; str = ['a', 'b', 'c', 'd', 'e', 'f'][i++];) {
  let toolTip = toolTipFactory.create()
  toolTip.innerHTML = str
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant