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

设计模式 #10

Open
juzhiyuan opened this issue Jun 20, 2018 · 0 comments
Open

设计模式 #10

juzhiyuan opened this issue Jun 20, 2018 · 0 comments

Comments

@juzhiyuan
Copy link
Owner

juzhiyuan commented Jun 20, 2018

简单工厂模式

类似于现实生活中的工厂,可产生大量相似的商品、实现同样的效果

const CreatePerson = (name, age, sex) => {
  const obj = new Object()
  obj.name = name
  obj.age = age
  obj.sex = sex
  obj.sayName = function () {
    return this.name
  }
  return obj
}

const personA = new CreatePerson('琚致远', '21', '男')
const personB = new CreatePerson('晨阳', '18', '女')

console.log(personA.name) // 琚致远
console.log(personA.age) // 21
console.log(personA.sex) // 男
console.log(personA.sayName()) // 琚致远

console.log(personB.name) // 晨阳
console.log(personB.age) // 18
console.log(personB.sex) // 女
console.log(personB.sayName()) // 晨阳

// 类型均为 object,无法识别对象类型、无法识别它们属于哪个对象的实例
console.log(typeof personA) // object
console.log(typeof personB) // object

console.log(personA instanceof Object) // true

优点

  • 解决多个相似的问题

缺点

  • 无法确定对象类型

复杂工厂模式

将其成员对象的实例化推迟到子类中,子类可以重写父类接口方法以便创建的时候指定自己的对象类型

  • 弱化对象间耦合,防止代码重复。在一个对象中进行类的实例化,可以消除重复性代码。
  • 重复性业务放在父类中,子类继承于父类中的所有成员属性与方法、专注于实现自己的业务逻辑。

场景:开了多个自行车店,每个点有几种型号的自行车出售

// 父类
const BicycleShop = () => {}

BicycleShop.prototype = {
  constructor: BicycleShop,

  /**
  * 卖自行车
  * @param {model} 自行车型号
  */
  sellBicycle: function (model) {
    const bicycle = this.createBicycle(model)
    // 执行 A 业务逻辑
    bicycle.A()

    // 执行 B 业务逻辑
    bicycle.B()

    return bicycle
  },
  createBicycle: function (model) {
    throw new Error('父类是抽象类,不可直接调用,需要子类重写该方法')
  }
}
// 自行车构造函数
function BicycleShop (name) {
  this.name = name
  this.method = () => {
    return this.name
  }
}

BicycleShop.prototype = {
  constructor: BicycleShop,

   /**
   * 卖自行车
   * @param {model} 自行车型号
   */
  sellBicycle: function (model) {
    const bicycle = this.createBicycle(model)
    // 执行 A 业务逻辑
    bicycle.A()

    // 执行 B 业务逻辑
    bicycle.B()

    return bicycle
  },
  createBicycle: function (model) {
    throw new Error('父类是抽象类,不可直接调用,需要子类重写该方法')
  }
}


/**
* 实现原型继承
* @param { Sub } 子类
* @param { Sup } 超类
*/
const extend = (Sub, Sup) => {
  // 定义空函数
  const F = function () {}

  // 设置空函数原型为超类的原型
  F.prototype = Sup.prototype

  // 实例化空函数,并将超类原型引用传递给子类
  Sub.prototype = new F()

  // 重置子类原型的构造器为子类本身
  Sub.prototype.constructor = Sub

  // 在子类中保存超类原型,避免子类与超类耦合
  Sub.sup = Sup.prototype

  // 检查超类原型的构造器是否为原型自身
  if (Sup.prototype.constructor === Object.prototype.constructor) Sup.prototype.constructor = Sup
}

function BicycleChild (name) {
  this.name = name
  // 继承构造函数父类中的属性与方法
  BicycleShop.call(this.name)
}

// 子类继承父类原型方法
extend(BicycleChild, BicycleShop)

// BicycleChild 子类重写父类方法
BicycleChild.prototype.createBicycle = () => {
  const A = () => {
    console.log('执行 A 业务逻辑')
  }

  const B = () => {
    console.log('执行 B 业务逻辑')
  }

  return {
    A,
    B
  }
}

const childClass = new BicycleChild('飞鸽')
console.log(childClass)

结果如下
image

单例模式

产生一个“类”的唯一实例(注意:JavaScript 中并没有真正的类)

有如下场景:点击某个按钮的时候,弹出遮罩层,比如web.qq.com点击登录的时候

方案一

// 生成灰色背景遮罩层
var createMask = function() {
  return document.body.appendChild(document.createElement("div"));
}

$('button').click(function() {
  var mask = createMask();
  mask.show();
})

上方代码的问题是:这个遮罩层是全局唯一的,每次调用createMask都会创建一个新的div,虽然可以在遮罩层隐藏的时候将它移除掉,但这是不合理的。

方案二

在页面一开始就创建好这个div,然后用一个变量引用它。

var mask = document.body.appendChild(document.createElement('div'));

$('button').click(function() {
  mask.show()
})

这样确实在页面只会创建一个遮罩层div,但是问题是:我们或许永远也不会使用它,因此就会浪费一个div,对dom节点的任何操作应当是吝啬的。

如果借助一个变量,来判断是否已经创建过div呢?

var mask;
var createMask = function() {
  if (mask) return mask;
  mask = document.body.appendChild(document.createElement('div'));
  return mask;
}

上方代码完成了一个产生单例对象的函数,不过我们分析下它有什么不妥:

首先,这个函数存在一定副作用,函数体内改变了外界mask的引用,在多人协作时,createMask是一个不安全的函数;此外,mask这个全局变量并不是必须非要不可,修改如下:

方案三

var createMask = (function() {
  var mask;
  return function() {
    return mask || (mask = document.body.appendChild(document.createElement('div')));
  }
})()

闭包 将变量mask包起来,至少对函数createMask来讲,它是封闭的。上方的单例模式还是有缺点的,它只能用于创建遮罩层,加入我又写了一个函数,用来创建唯一的XHR对象,能不能找到一个通用的单例包装器?

JavaScript 中,函数是第一型,意味着函数可以当做参数来传递,以下为最终代码:

方案四

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

var createMask = wrapper(function() {
  return document.body.appendChild(document.createElement('div'));
})

用一个变量来保存第一次返回值,如果它已经被赋值过了,那么在以后的调用中优先返回该变量,而真正创建遮罩层的代码是通过回调函数的方式传入到wrapper包装器的,这种方式叫桥接模式

然而wrapper函数还是不完美,它始终需要一个变量result来寄存div的引用,遗憾的是JavaScript 的函数式特性不足以完全消除声明与语句。

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