JS设计模式---6.工厂模式

工厂模式是什么

工厂模式,实现一个抽象的工厂方法并把实例化的工作推迟到子类中进行,来代替new方法创造实例的一种操作

工厂模式之利

  • 消除对象间耦合
  • 扩展性高
  • 消除重复代码,模块化

工厂模式之弊端

  • 不够直观

适用场合

动态实现

与一系列实现了同一接口、可以被同等对待的类打交道时,需要使用工厂模式

节省设置开销

如果对象需要进行复杂并且彼此相关的设置,那么使用工厂模式可以减少每种对象所需要的代码量。它可以在实例化所需要的对象之前先一次性的进行设置。
如果所用的类要求加载外部库的话,工厂方法可以对这这些库进行检查并动态加载那些未找到的库

用许多小对象组成一个大对象

工厂方法可以用来创建封装了许多小对象的对象。如果你不想让某个子系统与比较大的那个对象之间形成强耦合,而是想在运行时从许多子系统中进行挑选的话,那么使用工厂方法是比较理想的选择。

简单工厂

假设你想开几个自行车商店,每个店都有几种型号的自行车出售。这可以用一个类来表示

   // BicycleFactory  单体
    var BicycleFactory = {
      createBicycle: function (model) {
        var bicycle;
        switch (model) {
          case 'The Speedster':
            bicycle = new Speedster();
            break;
          case 'The Lowrider':
            bicycle = new Lowrider();
            break;
          case 'The Comfort Cruiser':   
          default:
            bicycle = new ComfortCruiser
            break;
        }
        interface.ensureImplements(bicycle,Bicycle); 
        return bicycle
      }
    } 
    //  BicycleShop class
    var BicycleShop = function () {}
    BicycleShop.prototype = {
      // sellBicycle根据所要求的自行车型号创建一个自行车实例,各种型号的实例可以互换使用,因为它们都实现了Bicycle接口
      sellBicycle:function(model){
        var bicycle = BicycleFactory.createBicycle(model);
        bicycle.assemble();
        bicycle.wash();
        return bicycle
      }
    }
    // Bicycle接口
    var Bicycle = new Interface('Bicycle',['assemble','wash','ride','repair']);
    // Speedster class
    var Speedster = function () {};
    Speedster.prototype = {
      assemble: function () {
        // ...
      },
      wash: function () {
        // ...
      },
      ride:function(){
        // ...
      },
      repair:function(){
        // ...
      }
    }

BicycleFactory 就是简单工厂的一个例子。这种模式把成员对象的创建工作转交给一个外部对象。这个外部对象可以像本例是一个简单的命名空间,也可以是一个类的实例。

工厂模式

真正的工厂模式与简单工厂模式的区别在于,它不是另外使用一个类或对象来创建自行车,而是使用一个子类。
按照正式定义,工厂是一个将其成员对象的实例化推迟到子类中进行的类。

   // BicycleShop class (抽象类)
   var BicycleShop = function () {};
   BicycleShop.prototype = {
      sellBicycle:function(model){
        var bicycle = this.createBicycle(model);
        bicycle.assemble();
        bicycle.wash();
        return bicycle; 
     },
     createBicycle:function(model){
       // 抛出错误 不能在抽象类中实例化
        throw new Error('Unsupported operation on an abstract class')
     }
   }
   // 设计一个经销特定自行车生产厂家产品的子类需要扩展BicycleShop 重新定义其中的createBicycle方法 下面为两个子类 其中一个子类代表的商店从Acme公司进货 另一个从General Products 公司进货
   // AcmeBicycleShop class
   var AcmeBicycleShop = function () {}
   extend(AcmeBicycleShop,BicycleShop);  // 继承
   AcmeBicycleShop.prototype.createBicycle = function (model) {
     var bicycle;
     switch (model) {
       case 'The Speedster':
         bicycle = new AmceSpeedster(); 
         break;
        case 'The Comfort Cruiser':
        default:
          bicycle =new AmceComfortCruiser();
          break;
     }
     Interface.ensureImplements(bicycle,Bicycle);
     return bicycle;
   }
   // GeneraProductsBicycleShop class
   var GeneraProductsBicycleShop = function(){};
   extend(GeneraProductsBicycleShop,BicycleShop); //继承
   GeneraProductsBicycleShop.prototype.createBicycle = function (model) {
     var bicycle;
     switch (model) {
       case 'The Speedster':
         bicycle = new GeneraProductsSpeedster(); 
         break;
        case 'The Comfort Cruiser':
        default:
          bicycle =new GeneraProductsComfortCruiser();
          break;
     }
     Interface.ensureImplements(bicycle,Bicycle);
     return bicycle;     
   }
   // 这些工厂方法生成的对象都实现了Bicycle接口,所以在其它代码眼里他们完全可以互换
   // 销售工作还是一样 只是现在所开的商店可以是Amce或者General Products自行车专卖店
   var alecsCruisers = new AcmeBicycleShop();
   var yourNewBike = alecsCruisers.createBicycle('The Speedster')

   var bobsCruisers = new GeneraProductsBicycleShop();
   var yourSecondNewBike = bobsCruisers.createBicycle('The Speedster')
   // 增加对其他生产厂家的支持也很简单,只要再创建一个BicycleShop的子类并重新定义其createBicycle方法即可。 一般性的代码被集中在一个位置,而个体性的代码则被封装在子类中

一个栗子 (XHR工厂)

  • 简单工厂
    // AjaxHandler 接口
    var AjaxHandler = new Interface('AjaxHandler',['request','createXhrObject']);
    // SimpleHandler classs
    var SimpleHandler = function () {};
    SimpleHandler.prototype = {
      // reuqest 负责执行发出请求和处理响应结果所需要的一系列操作
      request:function(method,url,callback,postVars){
        var xhr = this.createXhrObject;
        xhr.onreadystatechange = function () {
          if (xhr.readyState !== 4) return;
          (xhr.status ===200 )?
          callback.success(xhr.reponseText,xhr.responseXML):
          callback.failure(xhr.status)
        };
        xhr.open(method,url,true);
        if (method !== 'POST') postVars = null;
        xhr.send(postVars);
      },
      // caeateXhrObject 根据当前环境的具体情况返回一个XHR对象。在首次执行时,它会一次尝试三种用于创建XHR对象的不同方法,一旦遇到一种管用的它就回返回所创建的对象并将自身改为可以用以创建的哪个对象的函数。
      //这种技术叫做memoizing,它可以提高代码的效率,因为所有设置的检测代码都只会执行一次。
      caeateXhrObject:function(){
        var methods = [
          function () {
            return new XMLHttpRequest();
          },
          function () {
            return new ActiveXObject('Msxml2.XMLHttpRequest');
          },
          function () {
            return new ActiveXObject('Microsoft.XMLHTTP');
          },
        ];
        for (let i = 0; i < methods.length; i++) {
          try{
            methods[i]()
          }
          catch{
            continue
          }
          this.caeateXhrObject = methods[i];
          return methods[i]
        }
        throw new Error('SimpleHandler: Could not create an XHR objece')
      }
    }
    // 使用
    var myHandler = new SimpleHandler();
    var callback = {
      success:function(reponseText){ 
        // ...
      },
      failure:function(statusCode){
        // ...  
      }
    };
    myHandler.request('GET','script.php',callback)
  • 专用型连接对象

这个栗子可以进一步扩展,把工厂模式用在两个地方,以便根据网络条件创建专门的请求对象。
QueueHandler 会在发起新的请求之前先确保所有的请求都已经成功处理,而OfflineHandler则会在用户处于离线状态时把请求缓存起来。

    // QueueHandler class 在发起新的请求之前先确保所有请求已经成功处理
    var QueueHandler = function () {
      this.queue = [];
      this.requestInProgress = false;
      this.retryDelay = 5 // 秒
    }
    extend(QueueHandler,SimpleHandler);
    QueueHandler.prototype.request = function (method,url,callback,postVars,override) {
      if (this.requestInProgress && !override) {  // 如果有请求没有处理成功,把本次请求暂存
          this.queue.push({
            method,
            url,
            callback,
            postVars
          })
      }
      else{  // 如果所有请求都已成功 发送本次请求
        this.requestInProgress = true;
        var xhr = this.createXhrObject();
        var that = this;
        xhr.onreadystatechange = function () {
          if (xhr.readyState !== 4) return;
          if (xhr.status === 200) {
            callback.success(xhr.reponseText,xhr.responseXML);
            that.advanceQueue();
          }
          else{
            callback.failure(xhr.status);
            // 5s后再次发起请求
            setTimeout(() => {
              that.request(method,url,callback,postVars,true)
            }, that.retryDelay*1000);
          }
        }
        xhr.open(method,url,true);
        if (method !== 'POST') postVars = null;
        xhr.send(postVars)
      }
    }
    QueueHandler.prototype.advanceQueue = function () {
      if (this.queue.length !== 0) {  // 如果暂存列表为空 结束本次函数
        this.requestInProgress = true;
        return;
      }
      var req = this.queue.shift();   // 如果不为空 发起暂存列表的第一个请求
      this.request(req.method,req.url,req.callback,req.postVars,true)
    }
   
    // OfflineHandler calss
    var OfflineHandler = function () {
      this.storedRequests = [];
    };
    extend(OfflineHandler,SimpleHandler);
    OfflineHandler.prototype.request = function (method,url,callback,postVars) {
      if (XhrManager.isOffline()) {   // XhrManager.isOffline方法判断是否离线
        this.storedRequests.push({
          method,
          url,
          callback,
          postVars
        })
      }
      else{
        this.flusStoredRequests();
        // 调用超类(也就是父类)的request方法
        OfflineHandler.superclass.request(method,url,callback,postVars)
      }
    };
    OfflineHandler.prototype.flusStoredRequests = function () {
      for (let i = 0; i < storedRequests.length; i++) {
        var req = storedRequests[i];
        OfflineHandler.superclass.request(req.method,req.url,req.callback,req.postVars)
      }
    }

  • 在运行时选择连接对象(工厂模式)

因为程序员根本不可能知道各个最终用户实际面临的网络条件,所以不可能要求他们在开发中选择使用那个处理器类,而是应该用一个工厂在运行时选择最合适的类

   // XhrManager 单例
   var XhrManager = {
     createXhrHandler:function(){
       var xhr;
       if (this.isOffline()) {
         xhr = new OfflineHandler();
       }
       else if (this.isHighLatency()) {
         xhr = new QueueHandler();
       }
       else{
         xhr = new SimpleHandler();
       }
       Interface.ensureImplements(xhr,AjaxHandler);
       return xhr
     },
     isOffline:function(){
       //...   判断是否离线
     },
     isHighLatency:function(){
       //..  判断是否高延迟 
     }
   }
   // 使用
   var myHandler = XhrManager.createXhrHandler();
   var callback = {
     success:function(responseText){
      // ...
     },
     failure:function(statusCode){
      // ...
     }
   }
   myHandler.request('GET','script.php',callback)
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,651评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,468评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,931评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,218评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,234评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,198评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,084评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,926评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,341评论 1 311
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,563评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,731评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,430评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,036评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,676评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,829评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,743评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,629评论 2 354

推荐阅读更多精彩内容

  • 设计模式概述 在学习面向对象七大设计原则时需要注意以下几点:a) 高内聚、低耦合和单一职能的“冲突”实际上,这两者...
    彦帧阅读 3,741评论 0 14
  • 设计模式汇总 一、基础知识 1. 设计模式概述 定义:设计模式(Design Pattern)是一套被反复使用、多...
    MinoyJet阅读 3,939评论 1 15
  • 工厂模式类似于现实生活中的工厂可以产生大量相似的商品,去做同样的事情,实现同样的效果;这时候需要使用工厂模式。简单...
    舟渔行舟阅读 7,752评论 2 17
  • 设计模式基本原则 开放-封闭原则(OCP),是说软件实体(类、模块、函数等等)应该可以拓展,但是不可修改。开-闭原...
    西山薄凉阅读 3,792评论 3 14
  • 链接:https://github.com/WiKi123/DesignPattern作者: WiKi123(gi...
    树懒啊树懒阅读 3,504评论 0 2