这段时间沉迷于学习JavaScript的设计模式,于是想着与之前所学的设计模式联系起来,对比学习。因为之前有了Java设计模式的部分基础,所以在学习JavaScript设计模式时会进行一些简单的比较记忆,主要的内容安排是模式的定义、模式对应的类图以及模式的简单实现。此篇博文主要记录创建型设计模式的学习过程,如果在某些地方表述不正确的,还希望大家能够指出,共同进步~
创建型设计模式
创建型设计模式是一类处理对象创建的设计模式,主要应用于创建对象。包括简单工厂模式、工厂方法模式、抽象工厂模式、建造者模式、原型模式以及单例模式。
简单工厂模式
简单工厂模式,又叫静态工厂方法,由一个工厂对象决定创建某一种产品对象类的实例。
Java语言简单工厂模式的类图表达为:
我们可以仿照Java语言的类图绘制类图为: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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70// 简单工厂模式
var Ball = function(){}
Ball.prototype = {
getMember: function(){
throw new Error("抽象方法不能调用")
},
getBallSize: function(){
console.log("抽象方法不能调用");
}
}
var Basketball = function(){
this.intro = "篮球盛行于美国";
}
Basketball.prototype = new Ball();
Basketball.prototype = {
getMember: function(){
console.log("每个队伍需要5名队员");
},
getBallSize: function(){
console.log("篮球很大");
}
}
var Football = function(){
this.intro = "足球在世界范围内流行";
}
Football.prototype = new Ball();
Football.prototype = {
getMember: function(){
console.log("每个队伍需要11名队员");
},
getBallSize: function(){
console.log("足球很大");
}
}
var Tennis = function(){
this.intro = "每年有很多网球系列赛";
}
Tennis.prototype = new Ball();
Tennis.prototype = {
getMember: function(){
console.log("每个队伍需要1名队员");
},
getBallSize: function(){
console.log("网球很小");
}
}
// 运动工厂
var SportsFactory = function(){}
SportsFactory.prototype = {
getSport: function(name){
switch(name){
case 'NBA':
return new Basketball();
case 'wordCup':
return new Football();
case 'FrenchOpen':
return new Tennis();
}
}
}
// Test
var factory = new SportsFactory();
var footnall = factory.getSport("wordCup");
console.log(footnall);
console.log(footnall.intro);
footnall.getMember();
简单工厂模式最主要的是所创建的工厂对象SportsFactory,工厂函数getSport()根据传入参数的不同返回不同类的实例。在此例中Football、Basketball、Tennis都继承了抽象类Ball,Ball中定义了两个抽象方法getMember()和getBallSize(),在具体类中重写这两个方法,若子类为重写父类中的抽象方法则会抛出异常:
当然因为JavaScript的特性,我们可以不需要Ball类,在Java语言中引入抽象类,主要是在客户端使用过程中,可以使用父类对象来替代具体的子类对象(里氏代换原则),如:1
2Ball ball;
ball = factory.getSport("wordCup");
在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// 寄生方式
// 使用一个对象代替多个类,提取相似
function createSports(type, intro, member, size){
var o = new Object();
o.intro = intro;
if(type == 'football'){
console.log("foolball")
}
else if(type == 'basketball'){
console.log("basketball")
}
else if(type == 'tennis'){
console.log("tennis")
}
o.getMember = function(){
console.log(member);
}
o.getBallSize = function(){
console.log(size);
}
return o;
}
// Test
var basketball = createSports("basketball", "This is a Basketball", "5", "big");
console.log(basketball);
console.log(basketball.intro);
basketball.getMember();
这种方式将相似的部分提出,不相似的部分进行针对性的处理。
这两种方式存在一定的差异,第一种方式是通过类实例化对象创建的,第二种方式是通过创建一个新对象然后包装增强其属性和功能来实现的。选择哪种工厂方式来实现你的需求还要看你是如何分析你的需求的。
工厂方法模式
工厂方法模式,通过对产品类的抽象使其创建业务主要负责用于创建多类产品的实例。
Java语言工厂方法模式类图表示为:
工厂方法模式让子类决定将哪一种产品实例化,让一个类的实例化延迟到其子类。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
46
47
48
49
50
51
52
53
54
55// 工厂方法模式
// 使用安全模式创建工厂类
var Factory = function(type, content){
if(this instanceof Factory){
var s = new this[type](content);
return s;
}
else{
return new Factory(type, content);
}
}
// 工厂原型中设置创建所有类型数据对象的基类
Factory.prototype = {
Java: function(content){
// 具体创建产品的过程
console.log(content);
},
JavaScript: function(content){
console.log(content);
},
UI: function(content){
console.log(content);
},
PHP: function(content){
console.log(content);
}
}
// Test
var data = [
{
type: 'JavaScript',
content: 'javascript'
},
{
type: 'Java',
content: 'java'
},
{
type: 'UI',
content: 'UI'
},
{
type: 'PHP',
content: 'PHP'
},
{
type: 'JavaScript',
content: 'other javascript'
}
]
for(var d of data){
Factory(d.type, d.content)
}
在实现工厂方法模式时,采用了安全模式创建工厂类,安全模式可以避免用户使用类时缺少new关键词而导致的错误。
JavaScript中使用安全模式创建工厂类,在工厂原型中创建所有类型数据对象的基类,通过使用时向工厂类传递数据类型和数据内容两个参数,来动态的实例化相应的产品类。
使用工厂方法模式可以避免使用者和对象类之间的耦合。
抽象工厂模式
抽象工厂模式,通过对类的工厂抽象使其业务用于对产品类簇的创建,而不负责创建某一类产品的实例。
JavaScript语言虽然保留abstract关键字,但目前来说还不能像Java语言那样轻松地创建。我们可以在类的方法中手动地抛出错误来模拟抽象类。
抽象工厂模式涉及的对象比较多,使用UML类图可以表示为: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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134// 抽象工厂模式
var Vehicle = function(subType, superType){
// 判断抽象产品类中是否有该抽象类
if(typeof Vehicle[superType] === 'function'){
function F(){};
// 继承父类的属性和方法
F.prototype = new Vehicle[superType]();
// 将子类constructor属性指向子类
subType.constructor = subType;
// 子类原型继承父类
subType.prototype = new F();
}
else{
throw new Error("未创建该抽象类")
}
}
// 小汽车抽象类
Vehicle.Car = function(){
this.type = 'car';
}
Vehicle.Car.prototype = {
getPrice: function(){
return new Error("抽象方法不能调用")
}
}
// 公共汽车抽象类
Vehicle.Bus = function(){
this.type = 'bus';
}
Vehicle.Bus.prototype = {
getPrice: function(){
return new Error("抽象方法不能调用")
}
}
// 汽车具体类——宝马汽车
var BMWCar = function(price){
this.price = price;
}
Vehicle(BMWCar, 'Car');
BMWCar.prototype.getPrice = function(){
return this.price;
}
// 汽车具体类——兰博基尼汽车
var LamborghiniCar = function(price){
this.price = price;
}
Vehicle(LamborghiniCar, 'Car');
LamborghiniCar.prototype.getPrice = function(){
return this.price;
}
// 公共汽车具体类——宝马公共汽车
var BMWBus = function(price){
this.price = price;
}
Vehicle(BMWBus, 'Bus');
BMWBus.prototype.getPrice = function(){
return this.price;
}
// 公共汽车具体类——兰博基尼公共汽车
var LamborghiniBus = function(price){
this.price = price;
}
Vehicle(LamborghiniBus, 'Bus');
LamborghiniBus.prototype.getPrice = function(){
return this.price;
}
// 抽象工厂
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.BMW = function(type){
this.factory = '宝马'
}
VehicleFactory.BMW.prototype = {
create: function(type){
return new Error("抽象方法不能调用")
}
}
// 兰博基尼工厂
VehicleFactory.Lamborghini = function(type){
this.factory = '兰博基尼'
}
VehicleFactory.Lamborghini.prototype = {
create: function(type){
return new Error("抽象方法不能调用")
}
}
// 具体工厂——宝马工厂
var BMWFactory = function(){}
VehicleFactory(BMWFactory, 'BMW');
BMWFactory.prototype = {
create: function(type){
console.log("宝马工厂生产" + type)
}
}
// 具体工厂——兰博基尼工厂
var LamborghiniFactory = function(){}
VehicleFactory(LamborghiniFactory, 'Lamborghini');
LamborghiniFactory.prototype = {
create: function(type){
console.log("兰博基尼工厂生产" + type)
}
}
// Test
var testCar = new BMWCar(10000);
var testBus = new BMWBus(100);
var testBMW = new BMWFactory();
testBMW.create(testCar.type);
testBMW.create(testBus.type)
首先定义了抽象汽车类Car和抽象公共汽车类Bus,创建了Car抽象汽车的具体产品汽车子类宝马汽车子类BMWCar和兰博基尼汽车子类LamborghiniCar,Bus抽象公共汽车的具体产品公共汽车子类宝马公共汽车子类BMWBus和兰博基尼公共汽车子类LamborghiniBus,然后定义了抽象工厂类VehicleFactory,创建了具体工厂子类宝马工厂BMWFactory和兰博基尼工厂VehicleFactory。
一个工厂负责创建一个产品族中的产品,BMWCar和LamborghiniCar同属于Car的产品等级结构中,一个工厂等级结构可以创建出分属于不同产品等级结构的一个产品族中的所有对象,
建造者模式
建造者模式,将一个复杂对象的构建层与其表示层相互分离,同样的构建过程可采用不同的表示。
建造者模式关注的是对象创建的细节,使用UML类图表示类之间的关系如下: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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79// 建造者模式——关心对象创建的整个过程
// 应聘者类
var Human = function(param){
this.skill = param && param.skill || '保密';
this.hobby = param && param.hobby || '保密';
}
Human.prototype = {
getSkill: function(){
return this.skill;
},
getHobby: function(){
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.secondName = name.slice(name.indexOf(' ') + 1);
}
})(name, that);
}
// 实例化职位类
var Work = function(work){
var that = this;
(function(work, that){
switch(work){
case 'code':
that.work = '工程师';
that.workDescript = '每天沉醉于编程';
break;
case 'UE':
that.work = '设计师';
that.workDescript = '设计更似一种艺术';
break;
case 'teach':
that.work = '教师';
that.workDescript = '分享也是一种快乐';
break;
default:
that.work = work;
that.workDescript = 'not know';
break;
}
})(work, that);
}
Work.prototype = {
changeWork: function(work){
this.work = work;
},
changeDescript: function(setence){
this.workDescript = setence;
}
}
// 应聘者建造者
var Person = function(name, work){
var _person = new Human();
_person.name = new Named(name);
_person.work = new Work(work);
return _person;
}
// Test
var person = new Person('xiao ming', 'code');
console.log(person.skill);
console.log(person.name.firstName);
console.log(person.name.secondName);
console.log(person.work.work);
console.log(person.work.workDescript);
person.work.changeDescript('更改职位描述!');
console.log(person.work.workDescript);
在事例代码中应聘者Human类充当UML类图中的Product类,应聘者建造者Person类充当UML类图中的ConcreteBUilder类,测试代码部分可以看作是UML类图中的Director类,实例化职位类Work和实例化姓名类Named是Human的创建过程。
建造者模式可以看作是为一个对象添加新的信息后返回新对象的过程。
原型模式
原型模式,用原型实例指向创建对象的类,使用于创建新的对象的类共享原型对象的属性以及方法。
原型模式在于通过复制这个原型来创建新的对象,可以复制原型对象的属性及方法来创建。
下例代码实现了使用原型模式来复制多个原型对象来对新对象的创建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// 对象复制方法
function prototypeExtend(){
var F = function(){},
args = arguments,
len = args.length,
i = 0;
for(; i < len; i++){
// 遍历每个模版对象中的属性
for(var j in args[i]){
// 将属性复制到缓存类原型中
F.prototype[j] = args[i][j];
}
}
return new F();
}
// Test
var penguin = prototypeExtend(
{
speed: 20,
swim: function(){
console.log("游泳速度:" + this.speed);
}
},
{
run: function(){
console.log("奔跑速度:" + this.speed);
}
},
{
jump: function(){
console.log("跳跃动作");
}
}
)
penguin.swim();
penguin.run(10);
penguin.jump();
单例模式
单例模式,又称为单体模式,是只允许实例化一次的对象类。
用途一: 定义命名空间,下例代码中Qiu即为命名空间,使用其中方法时需Qiu.xx()
1
2
3
4
5
6
7
8
9// 单例模式
var Qiu = {
g: function(id){
return document.getElementById(id);
},
css: function(id, key, value){
this.g(id).style[key] = value;
}
}用途二: 管理静态变量——只能访问不能修改
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16var Conf = (function(){
// 私有变量
var conf = {
MAX_NUM: 100,
MIN_NUM: 1,
COUNT: 1000
}
// 返回取值器对象
return {
get: function(name){
return conf[name]? conf[name]: null;
}
}
})();
var count = Conf.get('COUNT');
console.log(count);
- 用途三: 惰性单例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19// 惰性单例
var LazySingle = (function(){
var _instance = null;
function Single(){
// 定义私有属性和方法
return {
publicMethod: function(){},
publicProperty: '1.0'
}
}
// 获取单例对象接口
return function(){
if(!_instance){
_instance = Single();
}
return _instance;
}
})();
console.log(LazySingle().publicProperty);
总结
JavaScript设计模式之创建型设计模式到此也就告一段落了,因为其中有部分设计模式之前因为时间不足未曾深入学习,所以可能有些理解的不够透彻,如果有存在描述不当的地方,欢迎大家指出~共同进步~
下一篇我将记录JavaScript设计模式中结构型设计模式的学习过程~加油加油~