让前端飞Vue

JavaScript设计模式——工厂模式

2019-08-02  本文已影响16人  你看到我的小熊了吗

前言

今天开始学习JavaScript设计模式,每天学一点,希望有所收获。
今天主要学习工厂模式,包括:简单工厂模式、工厂方法模式、抽象工厂模式。

设计模式分类

1、创建型

创建型设计模式专注于处理对象创建机制 ,以适合给定情况的方式来创建对象。创建对象的基本方法可能导致项目复杂性增加,而这些模式旨在通过控制创建过程来解决这种问题。
* Constructor (构造器)
* Factory(工厂)
* Abstract(抽象)
* Prototype(原型)
* Singleton(单例)
* Builder(生成器)

2、结构型

结构型模式与对象组合有关,通常可以用于在找出在不同对象之间建立关系的简单方法。这种模式有助于确保在系统某一部分发生变化时,系统的整个结构不需要同是改变。同时对于不适合因某一特定目的而改变的系统部分,这种模式也能够帮助它们完成重组。
* Decorator(装饰者)
* Facade(外观)
* Flyweight(享元)
* Adapter(适配器)
* Proxy(代理)

3、行为

行为模式专注于改善或简化系统中不同对象之间的通信。
* Interator(迭代器)
* Mediator(中介者)
* Observer(观察者)
* Visitor(访问)

工厂模式 🏭

1、简单工厂模式

又叫静态工厂模式,就是创建对象,并赋予属性和方法。主要用来创建同一类对象。

栗子🌰:
生产球的工厂有各种球,我们只需告诉它,你想要的球的名字,它便会把球生产出来:

let Basketball = () => {
    this.intro = "🏀篮球盛行于美国";
}
Basketball.prototype = {
    getNumber: function () {
        console.log("每个队伍需要5名队员");
    },
    getBallSize: function () {
        console.log("🏀篮球很大");
    }
}
let Football = () => {
    this.intro = "⚽️足球在世界范围内都很流行";
}
Football.prototype = {
    getNumber: function () {
        console.log("每个队伍需要11名队员");
    },
    getBallSize: function () {
        console.log("⚽️足球很大");
    }
}
let Tennis = () => {
    this.intro = "每年有很多网球🎾比赛";
}
Tennis.prototype = {
    getNumber: function () {
        console.log("每个队伍需要1名队员");
    },
    getBallSize: function () {
        console.log("🎾网球很小");
    }
}

// 体育用品工厂
let SportsFactory = function (name) {
    switch(name) {
        case 'basketball':
            return new Basketball();
        case 'football':
            return new Football();
        case 'tennis':
            return new Tennis();
    }
}

当我们想要足球时,你可以告诉工厂:football,它便会将你想要的给你:

let football = SportsFactory('football');
console.log(football.intro); // ⚽️足球在世界范围内都很流行
football.getMember();   // 每个队伍需要11名队员

这个时候,你会发现,上面的三种球的内部结构很相似,为了减少代码的重复,我们对它进行优化:

let SportsFactory = function (name) {
    function Balls(option) {
        this.intro = option.intro;
        this.getNumber = function () {
            console.log(option.number)
        }
        this.getSize = function () {
            console.log(option.size)
        }
    }
    switch(name) {
        case 'basketball':
            return new Balls({
                intro: "🏀篮球盛行于美国",
                number: "每个队伍需要5名队员",
                size: "🏀篮球很大"
            });
        case 'football':
            return new Balls({
                intro: "⚽️足球在世界范围内都很流行",
                number: "每个队伍需要11名队员",
                size: "⚽️足球很大"
            });
        case 'tennis':
            return new Balls({
                intro: "🏀篮球盛行于美国",
                number: "每个队伍需要5名队员",
                size: "🏀篮球很大"
            });
    }
}

显而易见,我们无需了解,这个工厂是怎么把球给造出来的,只要给到相应的参数,它便给我们相应的球,简单又方便。但是,当我们需要更多种类的球时,如:橄榄球🏈、羽毛球🏸️、台球🎱...balabala...球的种类越多,工厂如果还是按照这种方式去生产球,它会变得越来越臃肿,变得难以维护。所以说,简单工厂模式,只适用于,对象较少,对象逻辑简单的情况。

工厂需要创新,才能获得更多的利润:

2、工厂方法模式

通过对产品类的抽象,使其创建业务主要负责用于创建多类产品的实例。
也就是说,将实际创建对象的工作,放在子类中,把核心类抽离出来,形成抽象类。

我们可以将工厂方法看作是一个实例化对象的工厂类。按照工厂方法模式,我们对上面的代码进行改造。安全起见,我们采用安全模式类,而我们将创建的基类放在工厂方法类的原型中即可。

// 安全模式创建的工厂类
let SportsFactory = function(name) {
    if(this instanceof SportsFactory){
        let s = new this[name]();
        return s
    } else {
        return new SportsFactory(name);
    }
}

// 工厂原型中设置创建所有类型数据对象的基类
SportsFactory.prototype = {
    football : function() {
        this.intro = "⚽️足球在世界范围内都很流行";
        this.getNumber = function () {
            console.log("每个队伍需要11名队员");
        },
        this.getBallSize = function () {
            console.log("⚽️足球很大");
        }
    },
    basketball : function() {
        this.intro = "🏀篮球盛行于美国";
        this.getNumber = function () {
            console.log("每个队伍需要5名队员");
        },
        this.getBallSize = function () {
            console.log("🏀篮球很大");
        }
    },
    tennis: function() {
        this.intro = "每年有很多网球🎾比赛";
        this.getNumber = function () {
            console.log("每个队伍需要1名队员");
        },
        this.getBallSize = function () {
            console.log("🎾网球很小");
        }
    }
}

let football = SportsFactory('football')
football.getBallSize()  // ⚽️足球很大

这样以后添加其他类时, 只要写在SportsFactory这个工厂类的原型里就可以了。
通过工厂方法模式,我们可以轻松的创建多个类的实例对象,这样工厂方法对象在创建对象的方式,避免了使用者与对象类之间的耦合,用户不必关心创建该对象的具体类,只需调用工厂方法即可。

3、抽象工厂模式

通过对类的工厂抽象使其业务对于产品类簇的创建,而不负责某一类产品的实例。

工厂如果只生产代工一家品牌的球类,注定会被其它工厂淘汰,因此,它不能过于依赖一家品牌的订单,需要接受各种品牌的订单,才能形成自己的竞争力。例如: adidasnicklining等等。不同的品牌使用的材质、做工可能是不一样的,像上面这三个品牌就是对应的类簇。类簇一般用父类定义,并在父类中定义一些抽象方法,再通过抽象工厂让子类继承父类。因此,抽象工厂其实就是一个实现子类继承父类的方法。

抽象类是一种声明但不能使用的类,JavaScriptabstract还是保留字,不能像传统面向对象语言那样轻松的创建抽象类。不过,我们可以手动地抛出错误来模拟抽象类。

// 抽象工厂方法
let SportsFactory = function() {}
SportsFactory.prototype = {
    getNumber: function () {
        return new Error('抽象方法不可调用');
    }
}

以上代码就是一个抽象类,啥也做不了。但是在继承上却很有用,如果在子类中没有重写这些方法,那当子类调用改方法的时候,便会报错。这对于子类忘记重写这些方法时,父类的提示显得非常友好。

抽象类中定义的方法只是显性地定义一些功能,但没有具体的实现,而一个对象是要具有一套完整的功能的,所以,用抽象类创建的对象当时也是“抽象的”,因此,我们不能用它来创建一个真实的对象。

也就是说,工厂虽然拿到了订单,但是还没有可以生产不同品牌产品的设备,下面我们便来实现这些设备:

let SportsFactory = function (subType, superType) {
    // 判断抽象工厂中是否有该抽象类
    if (typeof SportsFactory[superType] === 'function') {
        // 缓存
        function F(){};
        // 继承父类属性和方法
        F.prototype = new SportsFactory[superType] ();
        // 将子类 constructor 指向子类
        subType.constructor = subType;
        // 子类原型继承 “父类”
        subType.prototype = new F ();
    } else { 
        // 不存在该抽象类抛出错误
        throw new Error('未创建该抽象类');
    }
}

//阿迪达斯抽象类
SportsFactory.AdidasBall = function() {
  this.type = 'adidas';
}
SportsFactory.AdidasBall.prototype = {
  getNumber: function() {
    return new Error('抽象方法不能调用');
  }
}

//耐克抽象类
SportsFactory.NickBall = function() {
  this.type = 'nick';
}
SportsFactory.NickBall.prototype = {
  getNumber: function() {
    return new Error('抽象方法不能调用');
  }
}

//李宁抽象类
SportsFactory.LiNingBall = function() {
  this.type = 'lining';
}
SportsFactory.LiNingBall.prototype = {
  getNumber: function() {
    return new Error('抽象方法不能调用');
  }
}

SportsFactory就是一个抽象工厂方法,该方法在参数中传递子类和父类,在方法体内部,通过寄生式继承,实现了子类对父类的继承。对抽象工厂方法添加抽象类的方法我们是通过点语法进行添加的。

好了,设备买回来了,我们便开始生产你想要的产品啦:

function BallsOfAdidas(option) {
    this.intro = "⚽️足球在世界范围内都很流行";
}

//抽象工厂实现WechatUser类的继承
SportsFactory(BallsOfAdidas, 'AdidasBall');
//子类中重写抽象方法
BallsOfAdidas.prototype.getNumber = function() {
  console.log("每个队伍需要11名队员");
}

BallsOfAdidas通过SportsFactory工厂类继承了AdidasBall,并且重写了父类AdidasBall中的getNumber方法。

生产完成后,我们便可以去买自己想要的品牌的运动器材了:

let adidasFootball = new BallsOfAdidas();
console.log(adidasFootball.type);   //adidas
adidasFootball.getNumber();    //每个队伍需要11名队员

抽象工厂模式是设计模式中最抽象的一种,也是创建模式中唯一一种抽象化创建模式。我们可以看到,抽象工厂创建出一个个类簇,固定了类的结构,它不直接创建实例,而是通过类的继承进行类簇的管理。

总结

1、简单工厂模式中,工厂Factory类集中了所有产品创建的逻辑,一旦要拓展新产品时,就不得不修改工厂类,这就违反了开闭原则(对拓展开放,对修改封闭),并且会造成工厂的逻辑过于复杂。

2、工厂方法模式中,在新增一个新产品时,就要新增一个具体工厂和一个具体产品类,这样程序的拓展性就有了提高,符合了开闭原则,避免了简单工厂模式的缺点,但是呢,新增产品时需要新增两个类,会增加代码量,可谓是有舍有得,具体如何要结合具体情况来使用。

3、抽象工厂模式是所有工厂模式的一般形式,当抽象工厂模式退化到只有一个产品等级结构时,就变成了工厂方法模式。当工厂方法模式的工厂类只有一个时,且工厂方法为静态方法时,则又变成了简单工厂模式。与工厂方法模式相似,抽象工厂模式隔离了具体类的生成,让客户端不清楚具体什么样的对象被创建。

参考

上一篇下一篇

猜你喜欢

热点阅读