设计模式系列三

2019-11-30  本文已影响0人  fanstastic
  1. 灵活可拆分的职责链节点
    我们约定,如果某个节点不能处理请求,则返回一个特定的字符串 'nextSuccessor'来表示该请求需要继续传递:
let order500 = function(orderType, pay, stock) {
  if (orderType == 1 && pay == true) {
    
  } else return 'nextSuccessor'
}
let order200 = function(orderType, pay, stock) {
  if (orderType == 2 && pay == true) {
    
  } else return 'nextSuccessor'
}
let orderNormal = function(orderType, pay, stock) {
  if (stock > 0) {
    
  } else {}
}

接下来需要把函数包装进职责链节点,我们定义一个构造函数Chain,在new Chain的时候传递的参数即为需要被包装的函数,同时它还拥有一个实例属性this.successor,表示在链中的下一个节点。

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)
  
  return ret
}
function Player(name, teamColor) {
  this.name = name
  this.teamColor = teamColor
  this.status = 'alive'
}
Player.prototype.win = function() {}
Player.prototype.loose = function() {}
Player.prototype.die = function() {
  this.state = 'die'
  playerDirector.receiveMessage('playerDead', this)
}
Player.prototype.loose = function() {
    playerDirector.receiveMessage('removePLayer', this)
}
Player.prototype.changeTeam = function() {
    playerDirector.receiveMessage('changeTeam', this, color)
}
let playerFac = function(name, teamColor) {
  let newPlayer = new Player(name, teamColor)
  playerDirector.receiveMessage('addPlayer', newPlayer)
  return newPlayer
}

最后,我们需要实现这个中介者对象,一般有两种方式

  1. 利用发布订阅模式。将playerDirector实现为订阅者,各player作为发布者,一旦player的状态改变,便推送消息给playerDirector
  2. 在playerDirector中开放一些接收消息的接口,各player可以直接调用这些接口来给
    playerDirector发送消息,player只需传递一个参数给playerDirector
let playerDirector =  (function() {
  let player = {}, // 保存所有玩家
  operations = {} // 中介者可以进行的操作 
  operations.addPlayer = fucntion(player) {
    let teamColor = player.teamColor // 玩家队伍颜色
    players[teamColor] = players[teamColor] || [] 不存在则创建一个团队
    players[teamColor].push(player)
  }
  operations.removePlayer = fucntion(player) {
    let teamColor = player.teamColor, // 玩家队伍颜色
    teamPlayers = players[teamColor] || [] 不存在则创建一个团队
    for (i=teamPlayers.length-1;i>=0;i--) {
        if (teamPlayers[i] == player) teamPlayers.splice(i, 1)
    }
  }
  let receiveMessage = function(...args) {
    let message = args[0]
    operations[message].apply(this, args)
  }  
  return {
    receiveMessage: receiveMessage
  }
})()
let Plane = function(){}
Plane.prototype.fire = () => {发射普通子弹}
MissileDecorator = function(plane) {
  this.plane = plane
}
MissileDecorator.prototype.fire = function() {
  this.plane.fire()
  console.log('发射导弹')
}

导弹类和原子弹类的构造函数都接受参数plane对象,并且保存好这个参数,在它们的fire方法中,除了执行自身的操作,还调用plane对象的fire方法。
这种给对象动态增加职责的方式,并没有真正地改动对象自身,而是将对象放入另一个对象之中,这些对象以一条链的方式进行引用,形成一个聚合对象。这些对象拥有相同的方法。

Function.prototype.before = function(beforefn) {
  let self = this
  return function(...args) {
    beforefn.apply(this, args)
    return self.apply(this, args)
  }
}
// 使用不污染原型的方式
let Light = function() {
  this.state = 'off'
  this.button = null
}

通常我们谈到封装,一般都会优先封装对象的行为,而不是状态。状态模式是把事物的状态封装成单独的类,和此种状态有关的行为都被封装在类的内部.

let OffLightstate = function(light) {
  this.light = light
}
OffLightstate.prototype.buttonWasPressed = function() {
  this.light.setState(this.light.WeakLightState)
}
let WeakLightstate = function(light) {
  this.light = light
}
WeakLightstate.prototype.buttonWasPressed = function() {
  this.light.setState(this.light.StrongLightState)
}
let StrongLightState = function(light) {
  this.light = light
}
StrongLightState.prototype.buttonWasPressed = function() {
  this.light.setState(this.light.OffLightState)
}
let Light = function() {
  this.offLightstate = new OffLightstate(this)
this.WeakLightstate = new WeakLightstate(this)
this.StrongLightState = new StrongLightState(this)
this.button=null
}
Light.prototype.init = function() {
  this.currentState = this.offLightstate
this.button.onClick = function() {
  self.currentState.buttonWasPressed()
}
}
let State = function() {}
State.prototype.buttonWasPressed = () => { throw new Error('error') }
let plugin = (() => {
  let plugin = document.createElement('embed')
  plugin.style.display = 'none'
  plugin.sign = () => { 扫描文件 } 
  plugin.plause = () => {}
  plugin.uoploading = () => {}
  plugin.del = () => {}
  plugin.done = () => {}
  document.body.appendChild(plugin)
  return plugin
})()

let Upload = function(fileName) {
  this.plugin=plugin
  this.fileName = fileName
  this.button1 = null
  this.button2 = null
  this.state = 'sign'
}
Upload.prototype.init = function() {
  let that = this
  this.dom = document.createElement('div')
  this.dom.innerHTML = '<button data-action="button1">扫描中</button>'
  document.body.appendChild(this.dom)
  this.button1 = this.dom.querySelector('[data-action="button1"]')
  this.bindEvent()
}
Upload.prototype.bindEvent = function() {
  let self = this
  this.button1.onClick = function() {
    if (self.state === 'sign') {} else if (){
        self.changeStatus('pause')
    }
}
}
let Upload = function(fileName) {
  this.plugin = plugin
  this.fileName = fileName
  this.button1 = null
  this.button2 = null
  this.signState = new SignState(this)
  this.uploadingState = new UploadingaState(this)
  this.pauseState = new PauseState(this)
  this.doneState = new DoneState(this)
  this.errorState = new ErrorState(this)
  this.currState = new this.signState
}
Upload.prototype.bindEvent = function() {
  let self = this
  this.button1.onClick = function() {
    self.curState.clickHander1()
  }
  this.button2.onClick = function() {
    self.curState.clickHander2()
  }
}

Upload.prototype.sign = function(){
  this.plugin.sign()
  this.currState = this.signState
}
Upload.prototype.uploading = function(){
  this.plugin.uploading()
  this.currState = this.uploadingState
}
let StateFactory = (() => {
  let State = function() {}
 State.prototype.clickHandler1 = function() {
  throw new Error('子类必须重写父类的clickHandler1方法')  
}
  return function(param) {
      let F = function(uploadObj)  {
          this.uploadObj = uploadObj;
      } 
      F.prototype = new State()
      for (var i in param) {
         F.prototype[i] = param[i] // 将传入对象的方法拷贝到原型上
      }
    return F
  }
})()
let SignState = StateFactory({
  clickHandler1: function() { 扫描中,点击无效 },
  clickHandler2: function() { 文件正在扫描不能删除 }
})

  1. 状态模式定义了状态与行为的关系,并将它们封装在一个类里。通过增加新的状态类,很容易增加新的状态和转换。
  2. 避免Context的无限膨胀,状态切换的逻辑被分布在状态类中,也去掉了context中过多的条件分支
  3. 用对象代替字符串记录当前状态,更加一目了然
  1. 有两种选择来管理state对象的创建和销毁。第一种是当state对象被需要时裁创建并随后销毁,另一种是一开始就创建好所有状态不销毁。
  2. 本章,我们为每个context对象都创建了一组state对象,实际上这些state对象是可以共享的,各context对象可以共享一个state对象。
let Light = function() {
 this.curState = FSM.off
 this.button = null
}

let deletgate = function(client, delegation) {
  return {
   buttonWasPressed: function(...args) {
       return delegation.buttonWasPressed.apply(client, args)
   }
 }
}

let FSM = {
 off: {
   buttonWasPressed: function() {
       
   }
 }
}

适配器模式
适配器模式是解决两个软件实体间接口不兼容问题。

  1. 代理模式
    把img标签添加到页面的功能和预加载图片的功能分开放到两个对象中去
let myImage = (function() {
  let imgNode= document.createElement('img')
  document.body.appendChild(imgNode)
  return {
    setSrc: function(src) { imgNode.src = src }
  }
})()

// 此时已经创建好img标签放入页面中,proxyImg则添加预加载的功能并且放入一张//默认图片

let proxyImage = (() => {
  // 制造一个假节点预加载图片
  let img = new Image()
  img.onload = () => {
    myImage.setSrc(this.src)
  }
  return {
    setSrc: function() {
      myImage.setSrc('default gif') //设置一张本地已经加载好的图片
      myImage.src=  src
    }
  }
})()
let getSingle = function(fn) {
  let result
  return function(...args) {
    return result || (result = fn.apply(this, args))
  }
}
// 函数执行的时候将弹框保存在单例函数的变量里
let createLoginLayer = () => {
  let div = document.createElement('div')
  div.innerHTML = 'login'
  document.body.appendChild(div)
  return div
}

let createSingleLoginLayer = getSingle(createLoginLayer)
let loginLayer1 = createSingleLoginLayer()
let loginLayer2 = createSingleLoginLayer()
Function.prototype.after = function(fn) {
   let self = this
  return function(...args) {
    let ret = self.apply(this, args)
    afterfn.apply(this, args)
    return ret
  }
}
  1. 中介者模式
    当一个对象发生改变,只需要和中介者通信即可,对象和对象间不通信。
  2. 外观模式
    外观模式主要为子系统的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得子系统更加使用。
    外观模式对客户提供一个简单易用的高层接口,高层接口会把客户请求转发给子系统实现具体功能。请求外观接口不是强制的,客户可以选择越过外观接口直接访问子系统。
  1. 找出变化
  2. 放置挂钩,我们在程序可能发生变化的地方放置挂钩,挂钩的返回结果决定下一步的走向。
  1. 发布订阅模式
    发布订阅用来降低多个对象之间的依赖关系,它可以取代对象之间硬编码的通知机制,一个对象不用再显示的调用另一个对象的某个接口。当有新的订阅者出现时,发布者代码不需要任何修改。同样发布者需要修改时,也不会影响之前的订阅者。

  2. 模板方法模式
    在一个运用模板方法的程序中,子类的方法种类和执行顺序都是不变的,所以我们把这部分逻辑抽离出来放到父类的模板方法里面;

  3. 策略模式
    策略模式将各种算法封装成单独的策略类,这些策略类可以被交换使用。

  4. 代理模式

  5. 职责链模式

public class Duck {
  public void makeSound(){}
}
public class AnimalSound {
  public void makeSound(Duck duck) {duck.makeSound()}
}

在享受静态语言类型检查带来的安全性的同时,我们也失去了一些编写代码的自由
所以我们需要通过抽象类实现向上转型

public abstract class Animal {
  abstract void makeSound()
}

抽象类的两个作用

  1. 契约作用,继承抽象类的具体类都会继承抽象类中的具体方法,并要求覆盖
  2. 实现向上转型
public interface Animal {
  abstract void makeSound();
}
public class Duck implements Animal {
  public void makeSound() {}
}

抽象类和interface的主要作用:

  1. 通过向上转型来隐藏对象的真正类型,以表现对象的多态性。
  2. 约定类与类之间的一些契约关系。
interface Command {
  execute: Function
}
class RefreshCommand implements Command {
  constructor() {}
  execute() {}
}
  1. 提炼函数
  2. 合并重复条件片段
  3. 把条件分支语句提炼成函数
  4. 提前让函数退出条件分支
  5. 传递对象参数代替过长的参数列表
  6. 尽量减少参数数量
  7. 少用三目运算符
  8. 合理使用链式调用
  9. 使用return退出多重循环
上一篇 下一篇

猜你喜欢

热点阅读