创建型设计模式(一)

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

简单工厂模式
又叫静态工厂方法,由一个工厂对象决定创建某一种产品类的实例。主要用来创建同一类对象。为了更好的体现这种模式的应用场景,我们举个栗子。
需求:写一个类要求返回篮球的一些相关信息,写一个类要求返回足球的一些相关信息,写一个类要求返回网球的一些相关信息。
分析:正常情况下,我们会选择写3个类,使用的时候调用对应的类就行,但是这种方法不利于协作开发,当别人每次创建实例时还要找到对应的类,太麻烦,而且不方便管理,所以这里最好是将这3个类封装在一个函数里,通过这个函数就可以创建需要的对象。

//篮球基类
var Basketball = function(){
    this.intro = '篮球盛行于美国';
}
Basketball.prototype = {
    getMember:function(){
        console.log('每个队伍需要5名队员');
    },
    getBallSize:function(){
        console.log('篮球很大');
    }
}
//足球基类
var Football = function(){
    this.intro = '足球在世界范围内很流行';
}
Football.prototype = {
    getMember:function(){
        console.log('每个队伍需要11名队员');
    },
    getBallSize:function(){
        console.log('足球很大');
    }
}
//网球基类
var Tennis = function(){
    this.intro = "每年有很多网球系列赛";
}
Tennis.prototype = {
    getMember:function(){
        console.log('每个队伍需要1名队员');
    },
    getBallSize:function(){
        console.log('网球很小');
    }
}

//重点:运动工厂
var SportsFactory = function(name){
    switch(name){
        case 'NBA':
            return new Basketball();
            break;
        case 'wordCup':
            return new Football();
            break;
        case 'FrenchOpen':
            return new Tennis();
            break;
    }
}
//测试代码
var football = SportsFactory('wordCup');
console.log(football);
console.log(football.intro);
football.getMember();
football.getBallSize();

这就是我们所说的简单工厂模式所应用的场景,很明显,这种模式的弊端也很容易发现。看下一个栗子。

工厂方法模式
需求:有一批广告资源需要投放,有一部分是Java的,用绿色字体,还有一部分是PHP的,用黄色字体,红色背景。
分析:就目前这个需求,可以用上面12的简单工厂模式完成。
需求:又来了一批广告资源,有一部分是JavaScript的,粉色背景。
分析:多了一个基类,那么就得新增一个基类,并且在简单工厂模式里加上一个判断。
需求:又有新的广告资源,有一部分是UI的,红色边框。
分析:需求一直在更改,这样不仅要添加类,还要修改工厂函数,很是麻烦。要想解决这个问题,我们使用工厂方法模式,但是在这之前,我们先引入安全模式。

//安全模式创建的工厂类
var Factory = function(type,content){
    if(this instanceof Factory){
        var s = new this[type](content);
        return s;
    }else{
        return new Factory(type, content);
    }
}

分析:如果这里不用new关键字实例化Factory,我们将无法访问到Factory.prototype上的属性,所以我们使用安全模式确保不管实例化时有没有使用new关键字,得到的结果都是new Factory()

Factory.prototype = {
    Java : function(content){
        //....此处具体实现省略
    },
    JavaScript : function(content){
        //....此处具体实现省略
    },
    UI : function(content){
        this.content = content;
        (function(content){
            var div = document.createElement('div');
            div.innerHTML = content;
            div.style.border = '1px solid red';
            document.getElementById('container').appendChild(div);
        })(content)
    },
    PHP : function(content){
        //....此处具体实现省略
    }
    ,
    Mysql : function(content){
        //....此处具体实现省略
    }
}

//广告数据
var data = [
    {type:'JavaScript',content:'JavaScript 哪家强'},
    {type:'Java',content:'Java 哪家强'},
    {type:'PHP',content:'PhP 世界上最好的语言'},
    {type:'UI',content:'UI 哪家强'},
    {type:'Mysql',content:'Mysql 哪家强'},
]

for(var i=0; i<data.length; i++){
    Factory(data[i].type,data[i].content);
}

这样我们就解决了简单工厂模式的痛点。

抽象工厂模式
抽象类:抽象类是一种声明但不能使用的类,当你使用时就会报错。
这种抽象类的应用场景是哪里?

//汽车抽象类,当使用其实例对象的方法时会抛出错误
var Car = function(){};
Car.prototype = {
    getPrice : function(){
        return new Error('抽象方法不能调用');
    },
    getSpeed : function(){
        return new Error('抽象方法不能调用');
    }
}

分析:我们看到创建的Car类其实什么都不能做,创建时没有任何属性,然而原型prototype上的方法也不能使用,否则会报错。但是在继承上却是很有用的,因为定义了一种类,并定义了该类所必备的方法,如果子类没有重写这些方法,那么调用时能找到这些方法便会报错。这一特点在大型应用中很有用。

//抽象工厂模式
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.Car = function(){
    this.type = 'car';
}
VehicleFactory.Car.prototype = {
    getPrice : function(){
        return new Error('抽象方法不能调用');
    },
    getSpeed : function(){
        return new Error('抽象方法不能调用');
    }
}

//公交车抽象类
VehicleFactory.Bus = function(){
    this.type = 'bus';
}
VehicleFactory.Bus.prototype = {
    getPrice : function(){
        return new Error('抽象方法不能调用');
    },
    getSpeed : function(){
        return new Error('抽象方法不能调用');
    }
}

//货车抽象类
VehicleFactory.Truck = function(){
    this.type = 'truck';
}
VehicleFactory.Truck.prototype = {
    getPrice : function(){
        return new Error('抽象方法不能调用');
    },
    getSpeed : function(){
        return new Error('抽象方法不能调用');
    }
}

分析:可以看到,抽象工厂其实是一个实现子类继承父类的方法,在这个方法中我们需要通过传递子类以及要继承父类(抽象类)的名称,并且在抽象工厂方法中又增加了一次对抽象类存在性的一次判断,如果存在,则将子类继承父类的方法。然后子类通过寄生式继承。

//宝马汽车子类
var BMW = function(price, speed){
    this.price = price;
    this.speed = speed;
}
//抽象工厂实现对Car抽象类的继承
VehicleFactory(BMW,'Car');
BMW.prototype.getPrice = function(){
    return this.price;
}
BMW.prototype.getSpeed = function(){
    return this.speed;
}

//兰博基尼汽车子类
var Lamborghini = function(price, speed){
    this.price = price;
    this.speed = speed;
}
//抽象工厂实现对Car抽象类的继承
VehicleFactory(Lamborghini,'Car');
Lamborghini.prototype.getPrice = function(){
    return this.price;
}
Lamborghini.prototype.getSpeed = function(){
    return this.speed;
}

//宇通汽车子类
var YUTONG = function(price, passenger){
    this.price = price;
    this.passenger = passenger;
}
//抽象工厂实现对Bus抽象类的继承
VehicleFactory(YUTONG,'Bus');
YUTONG.prototype.getPrice = function(){
    return this.price;
}
YUTONG.prototype.getPassengerNum = function(){
    return this.passenger;
}

//奔驰汽车子类
var BenzTruck = function(price, trainLoad){
    this.price = price;
    this.trainLoad = trainLoad;
}
//抽象工厂实现对Truck抽象类的继承
VehicleFactory(BenzTruck,'Truck');
BenzTruck.prototype.getPrice = function(){
    return this.price;
}
BenzTruck.prototype.getTrainLoad = function(){
    return this.trainLoad;
}

//测试代码
var truck = new BenzTruck(1000000, 1000);
console.log(truck.getPrice());
console.log(truck.type);
console.log(truck.getSpeed());

创建型设计模式还有几种模式下一章介绍。