设计模式 - 笔记 - 创造型设计模式 (JavaScript实现)
创造型设计模式
关注怎样创建对象?降低系统的耦合度,使用者不需要关注对象的创建细节,对象的创建由相关的工厂来完成,关注于将对象的创建与使用分离
简单工厂模式
由一个工厂对象决定创建某一种产品对象类的实例。主要是用来创建同一类对象。通过创建一个新对象包装增强属性后返回对象,主要用来创建同一类对象
- 优点
- 缺点
- 种类单一
- 增加类个数,会增加复杂度和理解难度
- 扩展困难
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| const PopFactory = function(type, content) { const o = new Object() o.content = content o.show = function(){ console.log(`content: ${this.content}`) } switch(type){ case 'alert': o.hide = function(){ console.log('alert-hide') } break; case 'prompt': o.hide = function(){ console.log('prompt-hide') } break; case 'confirm': o.hide = function(){ console.log('confirm-hide') } break; } return o } const alert = PopFactory('alert', 'alert-content') const prompt = PopFactory('prompt', 'prompt-content') const confirm = PopFactory('confirm', 'confirm-content')
alert.show() prompt.show() confirm.show() alert.hide() prompt.hide() confirm.hide()
|
工厂方法模式
通过对产品类的抽象,使其可以创建多类产品的实例,创建属于同一类,但是又有些许差异性的情况
- 优点
- 只需要知道具体工厂的名称就可得到所要的产品
- 无须知道产品的具体创建过程
- 灵活性增强
- 典型的解耦框架。高层模块只需要知道产品的抽象类,无须关心其他实现类,满足迪米特法则、依赖倒置原则和里氏替换原则
- 缺点
- 个数过多,增加复杂度
- 增加抽象性和理解难度
- 抽象产品只能生产一种产品
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| const Factory = function (type, content) { if(this instanceof Factory){ return new this[type](content) }else{ return new Factory(type, content) } } Factory.prototype.A = function(content){ this.content = content this.type = 'A' } Factory.prototype.B = function(content){ this.content = content this.type = 'B' } Factory.prototype.C = function(content){ this.content = content this.type = 'C' }
const a = new Factory('A', 'test') const b = Factory('B', 'test') const c = Factory('C', 'test')
|
抽象工厂模式
通过抽象,使其业务用于对产品类簇的创建,而不负责创建某一类产品,显性的去定义一些功能,而不做具体实现,子类继承后子类负责实现,用于制定类的结构
- 优点
- 可以在类的内部对产品族中相关联的多等级产品共同管理,而不必专门引入多个新的类来进行管理
- 当需要产品族时,抽象工厂可以保证客户端始终只使用同一个产品的产品组
- 抽象工厂增强了程序的可扩展性,当增加一个新的产品族时,不需要修改原代码,满足开闭原则
- 缺点
- 当产品族中需要增加一个新的产品时,所有的工厂类都需要进行修改。增加了系统的抽象性和理解难度
建造者模式
将一个复杂对象的构建层与其表示层分离,同样的构建过程采取不一样的表示,关注实例的创造过程,一般用于创建符合对象
- 优点
- 封装性好,构建和表示分离
- 扩展性好,各个具体的建造者相互独立,有利于系统的解耦
- 客户端不必知道产品内部组成的细节,建造者可以对创建过程逐步细化,而不对其它模块产生任何影响,便于控制细节风险
- 缺点
- 产品的组成部分必须相同,这限制了其使用范围
- 如果产品的内部变化复杂,如果产品内部发生变化,则建造者也要同步修改,后期维护成本较大
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| const VehicleFactory = function(subType, superType) { if(typeof VehicleFactory[superType] === 'function'){ function F() {} F.prototype = new VehicleFactory[superType]() subType.prototype = new F() subType.prototype.constructor = subType }else{ throw new Error('未创建抽象类') } } VehicleFactory.Car = function(){ this.type = 'Car' } VehicleFactory.Car.prototype.getPrice = function(){ throw new Error('抽象方法不可调用') } VehicleFactory.Car.prototype.getSpeed = function(){ throw new Error('抽象方法不可调用') }
VehicleFactory.Bus = function(){ this.type = 'Bus' } VehicleFactory.Bus.prototype.getPrice = function(){ throw new Error('抽象方法不可调用') } VehicleFactory.Bus.prototype.getSpeed = function(){ throw new Error('抽象方法不可调用') }
const BMW = function(price, speed){ this.price = price this.speed = speed } VehicleFactory(BMW, 'Car') BMW.prototype.getPrice = function(){ return this.type + ' price ' + this.price } BMW.prototype.getSpeed = function(){ return this.type + ' speed ' + this.speed }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| const Human = function (params) { this.skill = params.skill || '无' this.hobby = params.hobby || '无' } Human.prototype.getSkill = function() { return this.skill } Human.prototype.getHobby = function() { return this.hobby }
const Name = function(name) { this.wholeName = name }
const Work = function(work) { switch (work) { case 'code': this.work = '搬砖师' this.workDescription = '996警告' break; case 'ui': this.work = 'P图师' this.workDescription = '想要五彩斑斓的黑' break; default: this.work = '无业游民' this.workDescription = '干一天玩三天' break; } }
Work.prototype.changeWork = function(work) { this.work = work } Work.prototype.changeWorkDescription = function(workDescription) { this.workDescription = workDescription }
const Person = function(name, work, skill, hobby){ const _person = new Human(skill, hobby) _person.name = new Name(name) _person.work = new Work(work) return _person }
|
原型模式
用原型实例指向创建对象的类,使用创建新对象的类共享对象的属性及方法,可以让多个对象分享同一个原型对象上的属性方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| const Person = function(name, age) { this.name = name this.age = age } Person.prototype.getName = function() { return this.name } Person.prototype.getAge = function() { return this.age }
const Woman = function(name, age) { Person.call(this, name, age) } Woman.prototype = new Person() Woman.prototype.constructor = Woman Woman.prototype.getAge = function() { return `女人的年龄是秘密` } const Man = function(name, age) { Person.call(this, name, age) } Man.prototype = new Person() Man.prototype.constructor = Man
const woman = new Woman('小红', 24) const man = new Man('李白', 24)
|
单例模式
只允许实例化一个对象类,通过不同的命名空间来组织区分各个模块
- 优点
- 内存里只有一个实例,减少了内存的开销
- 可以避免对资源的多重占用
- 设置全局访问点,可以优化和共享资源的访问
- 缺点
- 单例模式一般没有接口,扩展困难。如果要扩展,则除了修改原来的代码,没有第二种途径,违背开闭原则
- 在并发测试中,单例模式不利于代码调试。在调试过程中,如果单例中的代码没有执行完,也不能模拟生成一个新的对象
- 单例模式的功能代码通常写在一个类中,如果功能设计不合理,则很容易违背单一职责原则
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| const LazySingle = (function (){ let _instance = null function _Single(){ const STATIC_COUNT = 2 return { getStaticCount: function (){ return `静态变量 STATIC_COUNT = ${STATIC_COUNT}` }, publicCount: 3, } } return function (){ if(!_instance){ _instance = _Single() } return _instance } })()
|
源码地址