一、创建型设计模式

221 阅读5分钟

js

创建型设计模式

创建型设计模式是一类处理对象创建的设计模式,通过某种方式控制对象的创建来避免基本对象创建时可能导致设计上的问题或增加设计上的复杂度。

  1. 简单工厂模式

由一个工厂对象决定创建某一种产品对象类的实例。主要用来创建同一类对象。

类的形式

var Man = function (name) {
  this.name = name;
};
Man.prototype.say = function () {
  return `I am a ${this.name}`;
};

var Woman = function (name) {
  this.name = name;
};
Woman.prototype.say = function () {
  return `I am a ${this.name}`;
};

var Girl = function (name) {
  this.name = name;
};
Girl.prototype.say = function () {
  return `I am a ${this.name}`;
};

var Boy = function (name) {
  this.name = name;
};
Boy.prototype.say = function () {
  return `I am a ${this.name}`;
};

var Person = function (name) {
  switch (name) {
    case 'man':
      return new Man(name);
    case 'woman':
      return new Woman(name);
    case 'girl':
      return new Girl(name);
    case 'boy':
      return new Boy(name);
    default:
      return null;
  }
};

var man = new Person('man')
man.say() // 'I am a man'

对象的形式

function createPerson(name) {
  var o = new Object()
  
  // 创建一个对象,并对对象拓展属性和方法
  o.name = name
  o.say = function() {
    return `I am a ${this.name}`;
  }

  // 差异部分
  if (name === 'man') {
    o.character = function() {
      return 'I am very strong'
    }
  }
  if (name === 'woman') {
    o.character = function() {
      return 'I am very beautiful'
    }
  }
  if (name === 'girl') {
    o.character = function() {
      return 'I am very lovely'
    }
  }
  if (name === 'boy') {
    o.character = function() {
      return 'I am very clever'
    }
  }

  return o
}

var man = createPerson('man')
man.name // 'man'
man.say() // I am a man
man.character() // 'I am very strong'
  1. 工厂方法模式

通过对产品类的抽象使其创建业务主要负责用于创建多类产品的实例。

// 安全模式类
var Factory = function(type, content) {
    if (this instanceof Factory) {
        return this[type](conetnt)
    } else {
        return new Factory(type, content)
    }
}

// 栗子:投放不同的广告资源
Factory.prototype = {
    JavaScript(content) {},
    Java(content) {},
    php(content) {},
    ...
}
  1. 抽象工厂模式

通过对类工厂抽象使其业务用于对产品类簇的创建,而不负责创建某一类产品的实例。

优势:在继承中,因为定义了一种类,并定义了该类所必备的方法,如果在子类中没有重写这些方法,那么调用时能找到这些方法但会报错。

// 抽象工厂方法
var VehicleFactory = function(SubType, SuperType) {
    // 判断抽象工厂中是否有该抽象类
    if (typeOf VehicleFactory[SuperType] === 'function') {
        // 缓存类
        function F() {}
        // 继承父类属性和方法
        F.prototype = new VehicleFactory[SuperType]()
        // 将子类 constructor 指向子类
        subType.constructor = subType
        // 子类原型继承父类
        subType.prototype = new F()
    } else {
        // 不存在该抽象类抛出错误
        throw new Error('未创建该抽象类')
    }
}
VehicleFactory.Man = function(name) {
  this.name = name
}
VehicleFactory.Man.prototype = {
  getName() {
    return new Error('抽象方法不能调用')
  }
}

VehicleFactory.Woman = function(name) {
  this.name = name
}
VehicleFactory.Woman.prototype = {
  getName() {
    return new Error('抽象方法不能调用')
  }
}
var Man = function(name) {
  this.name = name
}
VehicleFactory(Man, 'Man')
Man.prototype.getName = function() {
  return this.name
}

var man = new Man('man’s name')
man.getName() // 'man’s name'
  1. 建造者模式

将一个复杂对象的构建层与表示层相互分离,同样的构建过程可采用不同的表示。

栗子:应聘者发布自己的简历

var Human = function(params) {
  this.skill = params && params.skill || '保密'
  this.hobby = params && params.hobby || '保密'
}
Human.prototype = {
  getSkill() {
    return this.skill
  },
  getHobby() {
    return this.hobby
  }
}

var Named = function(name) {
  var that = this;
  (function(name, that) {
    that.wholeName = name
    if (name.indexOf(' ') > -1) {
      that.firstName = name.slice(0, name.indexOf(' '))
      that.lastName = name.slice(name.indexOf(' '))
    }
  })(name, that)
}
Named.prototype.changeName = function(name) {
  this.wholeName = name
  if (name.indexOf(' ') > -1) {
    this.firstName = name.slice(0, name.indexOf(' '))
    this.lastName = name.slice(name.indexOf(' '))
  }
}
var Person = function(name) {
  var _person = new Human()
  _person.name = new Named(name)
  return _person
}

var person = new Person('xiao ming')
person.hobby // '保密'
person.skill // '保密'
person.name.wholeName // 'xiao ming'
person.name.changeName('xiao gang')
person.name.wholeName // 'xiao gang'
person.name.firstName // 'xiao'
person.name.lastName // 'gang'
  1. 原型模式

用原型的实例指向创建对象的类,使用于创建新的对象的类共享原型对象的属性和方法。

栗子: 焦点图

// 图片轮播类
var LoopImgs = function(imgArr, container) {
    this.imgArray = imgArr           // 轮播图片数组
    this.container = container       // 轮播图片容器
    this.createImage = function() {} // 创建轮播图片
    this.changeImage = function() {} // 切换下一张图片
}

// 上下滑动切换类
var SlideLoopImage = function(imgArr, container) {
    // 构造函数继承图片轮播类
    LoopImgs.call(this, imgArr, container)
    // 重写 changeImage 方法
    this.changeImage = function() {
        console.log('SlideLoopImg changeImage function')
    }
}

// 渐隐切换类
var FadeLoopImage = function(imgArr, container, arrow) {
    // 构造函数继承图片轮播类
    LoopImgs.call(this, imgArr, container)
    this.arrow = arrow
    // 重写 changeImage 方法
    this.changeImage = function() {
        console.log('FadeLoopImg changeImage function')
    }
}

// 实例化一个渐隐切换类
var fadeImg = new FadeLoopImg([
  '01.jpg',
  '02.jpg',
  '03.jpg',
], 'Fade', ['left.jpg', 'right.jpg'])

fadeImg.changeImage() // 'FadeLoopImg changeImage function'

利用共享机制优化方案

// 图片轮播类
var LoopImgs = function(imgArr, container) {
    this.imgArray = imgArr           // 轮播图片数组
    this.container = container       // 轮播图片容器
}
LoopImgs.prototype = {
    // 创建轮播图片
    createImage = function() {
        console.log('LoopImg createImage function')
    },
    // 切换下一张图片
    changeImage = function() {
        console.log('eLoopImg changeImage function')
    },
}

// 上下滑动切换类
var SlideLoopImage = function(imgArr, container) {
    // 构造函数继承图片轮播类
    LoopImgs.call(this, imgArr, container)
}
SlideLoopImage.prototype = new LoopImgs()
// 重写 changeImage 方法
SlideLoopImage.prototype.changeImage = function() {
    console.log('SlideLoopImg changeImage function')
}

// 渐隐切换类
var FadeLoopImage = function(imgArr, container, arrow) {
    // 构造函数继承图片轮播类
    LoopImgs.call(this, imgArr, container)
    this.arrow = arrow
    // 重写 changeImage 方法
    this.changeImage = function() {
        console.log('FadeLoopImg changeImage function')
    }
}
FadeLoopImage.prototype = new LoopImgs()
// 重写 changeImage 方法
FadeLoopImage.prototype.changeImage = function() {
    console.log('FadeLoopImg changeImage function')
}

// 实例化一个渐隐切换类
var fadeImg = new FadeLoopImg([
  '01.jpg',
  '02.jpg',
  '03.jpg',
], 'Fade', ['left.jpg', 'right.jpg'])

fadeImg.container // 'Fade'
fadeImg.changeImage() // 'FadeLoopImg changeImage function'

在任何时候都可以对基类或者子类进性方法的扩展,而且所有被实例化的对象或者类都能获取这些方法,这样给予我们对功能扩展的自由性。

不过原型模式更多的是用在对对象的创建上。比如创建一个实例对象的构造函数比较复杂,或者耗时比较长,或者通过创建多个对象来实现。此时,我们最好不要用 new 关键字去复制这些基类,但可以通过对这些对象属性或者方法进性复制来实现创建。

function prototypeExtend() {
    var F = function() {},
        args = arguments,
        i = 0,
        len = args.length
    
    for (; i < len; i++) {
        for (var j in args[i]) {
            F.prototype[j] = args[i][j]
        }
    }
    
    return new F()
}
var penguin = prototypeExtend({
    speed: 20,
    swin() {
        console.log('游泳速度 ' + this.speed)
    }
},{
   run(speed) {
       console.log('奔跑速度 ' + speed)
   }
}, {
    jump() {
        console.log('跳跃')
    }
})

penguin.swin() // '游泳速度 20'
penguin.run(10) // '奔跑速度 10'
  1. 单例模式

只允许实例化一次的对象类。有时我们也用一个对象来规划一个命名空间,井井有条地管理对象上地属性与方法。

function g(id) {
    return document.getElementById(id)
}
function css(id, key, value) {
    g(id).style[key] = value
}
function arrt(id, key, value) {
    g(id)[key] = value
}
function html(id, value) {
    g(id).innerHTML = value
}
function on(id, type, func) {
    g(id)['on' + type] = func
}

使用单例模式创建

var namespace = {
    g(id) {
      return document.getElementById(id)
    },
    css(id, key, value) {
      this.g(id).style[key] = value
    },
    arrt(id, key, value) {
      g(id)[key] = value
    },
    html(id, value) {
      g(id).innerHTML = value
    },
    on(id, type, func) {
      g(id)['on' + type] = func
    },
}

使用单例模式实现静态变量

var Conf = (function() {
    // 私有变量
    var Conf = {
        MAX_NUM: 100,
        MIN_NUM: 100,
        COUNT: 1000
    }
    
    // 返回取值器对象
    return {
        get(name) {
            return Conf[name] ? Conf[name] : null
        }
    }
})()

Conf.get('COUNT') // 1000

惰性单例

// 惰性载入单例
var LazySingle = (function() {
    // 单例实例的引用
    var _instance = null
    function Single() {
        // 私有属性和方法
        return {
            publicMethod() {},
            publicProperty: '1.0',
        }
    }
    
    // 获取单例对象接口
    return function() {
        if (!_instance) {
            _ instance = Single()
        }
        
        // 返回单例
        return _instance
    }
})()

LazySingle().publicProperty // '1.0'