javaScript设计模式之----工厂模式

时间:2023-03-08 17:16:58

什么是工厂模式?我们通过一个例子了解一下:

比如我们想要弹出几个字符串

function funA(){
    alert('a');
}

function funB(){
    alert('b');
}

function funC(){
    alert('c');
}

如果我们想要弹出a,b,c我们就需要调用不同的方法funA,funB,funC。但是如果我们想弹出的东西很多呢,每次调用都需要看是需要调用哪个方法,会不会很麻烦。所以我们可以再包一个方法来做处理,如下所示:

function choose(type){
    switch(type){
        case 'funA' : funA(); break;
        case 'funB' : funB(); break;
        default : funC();
    }
}

很显然,我们在调用的时候就统一使用choose这个方法就行了,省去了很多步骤。

官方是这么定义工厂模式的:Simple Factory,又叫静态工厂方法,由一个工厂对象决定创建某一种产品对象的实例。主要用来创建同一类对象。

如果是一个工厂,必须具备以下基本条件才能成立:

    抽象: 定义一个最初的对象(告诉你有这么一个东西);

   工厂: 用于创建对象,也即对象的大本营(告诉你这个东西是一堆宝箱,你可以选择要哪个);

    具体对象: 最具象的对象,也就是某个大本营(告诉你这个宝箱中都有什么)

而工厂模式,又分为三种:简单工厂,工厂方法和抽象工厂。

1.我们先来看一下什么是简单工厂模式:

  function createPop(type,text){
      var o = new object();
      o.content = text;
       o.show = function(){
              //todo
       };

       if(type == 'alert'){
              //todo
      } else if(type == 'conform'){
             //todo
      } else if(type == 'prompt'){
             //todo
      }
 } 

我们可以简单地理解为就好比一条生产线,不同的员工有不同的职责,要生产这个产品,我们只需要把他放在生产线里就行了。

2.工厂方法

  var FoodFactory = function(type,count){
       if(this instanceof FoodFactory){
             return new this[type](count);
       }
  }

 FoodFactory.prototype = {
      Pie: function(count){
             //todo
      },
      Pizza: function(count){
             //todo
      },
      Cake: function(count){
             //todo
      }
 }

我们可以把工厂方法类比为一个大的汽车生产厂商,他可以生产轿车,也可以生产电动车,还能够生产货车,不同的车类有不同的生产线,但是又都属于这个大的厂商,只是不同的车类有不同的生产方法罢了。

3.抽象工厂

  抽象类显示定义一些功能,但没有具体实现。子类继承了抽象类还需要实现各个虚方法。就是在其他面向对象语言里的多态,创建子类赋值给父类,好处是调用方式都是一样的,运行时会自动选择子类对应的方法。如一个汽车的公司有多个品牌,它只负责设计汽车有什么功能,但真正的生产给子公司实现

var VehicleFactory = function(subType,superType){
      if(typeof VehicleFactory[superType] === 'function'){
            fucntion F(){};  //缓存类
            F.prototype = new VehicleFactory[super]();
            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('抽象方法不能调用');
      },
      // ...
 }

 //抽象类
 VehicleFactory.Truck= function(){
      this.type = 'Truck';
 };

 VehicleFactory.Truck.prototype = {
      getPrice:function(){
            return new Error('抽象方法不能调用');
      },
      // ...
 }

 //宝马子类
 var BMW = function(price,speed){
     this.price = price;
     this.speed = speed;
 };
 VehicleFactory(BMW,'Car'); //宝马继承了抽象父类Car,有了一些抽象方法,但是不能使用
 BMW.prototype.getPrice = function(){
     return this.price;
 }

 //使用
 var z4 = new BMW(50000);
 console.log(z4.type);        //Car      继承父类Car的属性
 console.log(z4.getPrice());  //50000 继承父类Car的抽象方法,自己重新实现

抽象工厂做了什么?我们需要一个新的子类时,使用抽象工厂让子类继承对应的一个抽象类,得到一些属性和虚方法,子类再实现这些虚方法,就可以使用了。

我们再来创建一个原型,就叫person吧:

var Person = function(name,methods){
    if(arguments.length > 2){
        throw new Error('参数过多');
    };
    this.name    = name;
    this.methods = [];
    for(var i=0,l=methods.length;i<l;i++){
        if(typeof methods[i] !== 'string'){
            throw new Error('参数命名需要为字符串');
        }
        this.methods.push(methods[i]);
    }
};
Person.ensureImplements = function(obj){
    for(var i=1,l=obj.length;i<l;i++){
        var interface = arguments[i];
        if(interface.constructor!=Interface){
            throw new Error('此类型接口有误');
        }
        for(var j=0,ml=interface.methods.length;j<ml;j++){
            var methods = interface.methods[j];
            if(!obj[methods] || typeof obj[methods] !== 'function'){
                throw new Error(methods + '方法不存在');
            }
        }
    }
}

这里我们创建了一个Person对象,拥有name和method属性,并做了简单处理。然后我们创建一个抽象的Person,代码如下:

var interfacePerson = new Person('Ren',['run','eat']);

然后,看看我们的具体对象,如下所示:

var PersonOne = function(){};
PersonOne.prototype = {
    run : function(){
        console.log('第一个人文文要跑了');
    },
    eat : function(){
        console.log('第一个人文文要吃饭了')
    }
}

var PersonTwo = function(){};
PersonTwo.prototype = {
    run : function(){
        console.log('第二个人瑞瑞要跑了');
    },
    eat : function(){
        console.log('第二个人瑞瑞要吃饭了')
    }
}

var PersonThree = function(){};
PersonThree.prototype = {
    run : function(){
        console.log('第三个人文瑞要跑了');
    },
    eat : function(){
        console.log('第三个人文瑞要吃饭了')
    }
}

我们定义了3个人,分别拥有run和eat的功能,并且每个人又都不一样。好了,接下来我们要开始创建我们的工厂了

var WenRui = function(){};
WenRui.prototype = {
    catchWenRui : function(whichOne){
        switch(whichOne){
            case '1' : pm = new PersonOne(); break;
            case '2' : pm = new PersonTwo(); break;
            default : pm = new PersonThree();
        }
        Interface.ensureImplements(pm);
        return pm;
    }
}

var wenRui = new WenRui();
wenRui.catchWenRui('1').run(); //第一个人文文要跑了
wenRui.catchWenRui('2').run(); //第二个人瑞瑞要跑了
wenRui.catchWenRui('3').eat(); //第三个人文瑞要吃饭了

WenRui这个构造函数充当了我们的factory的角色,我们放跑了第一个和第二个的文文和瑞瑞,留下了文瑞来吃饭。

好,我们来总结一下:其实,工厂模式的实现方法非常简单,解决了创建多个相似对象的问题,但是工厂模式却无从识别对象的类型,因为全部都是Object,不像Date、Array等,所以这个要用构造函数模式来解决。但是工厂模式的优点也很明显:他可以实现一些相同的方法,这些相同的方法我们可以放在父类中编写代码,那么需要实现具体的业务逻辑,那么可以放在子类中重写该父类的方法,去实现自己的业务逻辑;

也就是说有两点:  

 1、弱化对象间的耦合,防止代码的重复。在一个方法中进行类的实例化,可以消除重复性的代码。

 2、重复性的代码可以放在父类去编写,子类继承于父类的所有成员属性和方法,子类只专注于实现自己的业务逻辑。

好吧,工厂模式我了解的就这么多了,其实,不管是什么模式,只要我们在以后的编程过程中反复的思考我们可以怎么利用设计模式来优化我们的代码,那么,我们写出来的代码一定简而精,为更多人复用。