简单工厂模式
概述
简单工厂模式:简单工厂模式是属于创建型模式,又叫做静态工厂方法(Static Factory Method)模式,但不属于23种GOF设计模式之一。它定义一个工厂类,它可以根据参数的不同返回不同的实例,被创建的实例通常都具有共同的父类。
简单工厂模式的要点在于:当你需要什么,只需要传入一个正确的参数,就可以获取你所需要的对象,而无须知道其创建细节。简单工厂模式结构比较简单,其核心是工厂类的设计。
运用
简单工厂模式的角色:
- Product(抽象产品类):一般是一个抽象类或是接口
- ConcreteProduct(具体产品类):实现或是继承 Product
- Factory(工厂类):用来创建具体的产品
我去商店买水果,老板很懒,让我自己去拿(工厂类)
Product 类
// 抽象水果类
class Fruits {
describe(){
throw new Error("This method must be overwritten!");
}
}
ConcreteProduct 类
// 苹果
class Apple extends Fruits{
describe(){
console.log('我是苹果');
}
}
// 香蕉
class Banana extends Fruits{
describe(){
console.log('我是香蕉');
}
}
Factory 类
class FruitsFactory {
create(type) {
let fruit;
if( type == 'apple' ){
fruits = new Apple();
}else if( type == 'banana' ){
fruit = new Banana();
}
return this.fruit;
}
}
商店
class Shop {
constructor( fruitsFactory ) {
this.fruitsFactory = fruitsFactory ;
}
buyFruit( type ){
let fruit = this.fruitsFactory.create(type);
fruit.describe();
}
}
测试一下
let fruit = new FruitsFactory();
let shop = new Shop(fruit);
shop.buyFruit('apple');
# 输出:我是苹果
let shop = new Shop(fruit);
shop.buyFruit('banana');
# 输出:我是香蕉
总结
简单工厂模式中工厂类的职责太重,一旦不能工作将影响整个系统,且如果添加过多的产品,工厂类中的逻辑就过于复杂。所以简单工厂模式适用于逻辑简单,且需要创建对象较少的情况。
工厂方法模式
概述
上面是简单工厂模式,若果此时增加一个新的水果梨子,那么我们不得不修改工厂类 FruitsFactory中的 create 函数。这是违反开放封闭原则的,这样一直修改也是说明代码弹性不够。所以我们提出工厂方法模式,来解决这些问题。
工厂方法模式与简单工厂不同地方在于,把工厂类抽象出一个父类。创建对象的事情交给其具体子类工厂完成。
运用
比简单工厂新增一个角色:
- ConcreteFactory(具体工厂):它是抽象工厂类的子类,实现了抽象工厂中定义的工厂方法,并可由客户端调用,返回一个具体产品类的实例。
对上述例子做简单修改:
FruitsFactory 抽象成父类,提供create函数,但是不参与具体逻辑
class FruitsFactory{
create() {
throw new Error("This method must be overwritten!");
}
}
ConcreteFactory 类
// 新增Apple工厂
class AppleFactory extends FruitsFactory{
create() {
return new Apple();
}
}
// 新增Banana工厂
class BananaFactory extends FruitsFactory{
create() {
return new Banana();
}
}
修改 shop 类
class Shop {
constructor( fruitsFactory ) {
this.fruitsFactory = fruitsFactory ;
}
buyFruit(){
let fruit = this.fruitsFactory.create();
fruit.describe();
}
}
测试一下
let appleFactory = new AppleFactory();
let shop = new Shop(appleFactory);
shop.buyFruit();
# 输出苹果
let bananaFactory = new BananaFactory();
let shop = new Shop(bananaFactory);
shop.buyFruit();
# 输出香蕉
总结
工厂方法模式是对简单工厂模式的升级应用,它解决了简单工厂模式中工厂类职责繁重的问题,且更符合设计模式的开闭原则,增加新的产品不需要修改原先的代码,只需要增加对应的产品实例及工厂实例即可,同时这也是工厂方法模式的一个缺点,添加新产品,系统中的类就需要成对的增加,一定程度上增加了系统的复杂度。
抽象工厂模式
概述
工厂方法模式通过引入工厂等级结构,解决了简单工厂模式中工厂类职责太重的问题,但由于工厂方法模式中的每个工厂只生产一类产品,可能会导致系统中存在大量的工厂类,势必会增加系统的开销。此时,我们可以考虑将一些相关的产品组成一个“产品族”,由同一个工厂来统一生产,这就是我们本文将要学习的抽象工厂模式的基本思想。
抽象工厂模式定义是提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。抽象工厂模式又称为Kit模式,它是一种对象创建型模式。
光看定义相信大部分小伙伴跟我一样是一脸懵逼,与简单工厂或工厂方法模式相比,抽象工厂模式中的具体工厂不只是创建一种产品,它负责创建一族产品。
如上图所示,我们之前学习的简单工厂模式及工厂方法模式解决的都只是一种产品的创建过程,而在现实中,一个工厂往往不仅仅生产一种产品,例如惠普的工厂可能同时生产打印机、洗衣机、冰箱等产品,这里我们就需要对于一个产品族创建一个抽象工厂,在这个在具体的某一个产品族的工厂中创建具体的产品。
如上例中,若用工厂方法模式,则需要创建12个工厂类;而抽象工厂模式依据产品族来创建工厂,仅需4个工厂类,极大减少了系统中类的个数。
运用
抽象工厂的角色:
AbstractFactory(抽象工厂):它声明了一组用于创建一族产品的方法,每一个方法对应一种产品。
ConcreteFactory(具体工厂):它实现了在抽象工厂中声明的创建产品的方法,生成一组具体产品,这些产品构成了一个产品族,每一个产品都位于某个产品等级结构中。
AbstractProduct(抽象产品):它为每种产品声明接口,在抽象产品中声明了产品所具有的业务方法。
ConcreteProduct(具体产品):它定义具体工厂生产的具体产品对象,实现抽象产品接口中声明的业务方法。
假设Sunny软件公司欲推出一款新的手机游戏软件,该软件能够支持Symbian、Android和Windows Mobile等多个智能手机操作系统平台,针对不同的手机操作系统,该游戏软件提供了不同的游戏操作控制(OperationController)类和游戏界面控制(InterfaceController)类,并提供相应的工厂类来封装这些类的初始化过程。软件要求具有较好的扩展性以支持新的操作系统平台,为了满足上述需求,试采用抽象工厂模式对其进行设计。
根据需求我们知道,当该游戏在不同的操作系统时,需要分别使用不同的操作控制器和界面控制器,所以我们针对不同操作系统设计一个抽象工厂,该抽象工厂中定义两个方法,分别生产操作控制器和界面控制器。然后生产不同系统的工厂类,在工厂类中生成对应的操作控制器和界面控制器。
操作控制器代码
/**
* AbstractProduct 类
* 操作控制器接口
*/
class OperationController {
controlOperation(){
throw new Error("This method must be overwritten!");
}
}
/**
* ConcreteProduct 类
* 生产三种操作控制器
*/
class SymbianOperationController extends OperationController {
controlOperation() {
console.log("使用Symbian系统操作控制");
}
}
class AndroidOperationController extends OperationController {
controlOperation() {
console.log("使用Android系统操作控制");
}
}
class WindowsOperationController extends OperationController {
controlOperation() {
console.log("使用Windows Mobile系统操作控制");
}
}
界面控制器代码
/**
* AbstractProduct 类
* 界面控制器接口
*/
class InterfaceController {
controlInterface(){
throw new Error("This method must be overwritten!");
}
}
/**
* ConcreteProduct 类
* 分别生成三种界面控制器
*/
class SymbianInterfaceController extends InterfaceController {
controlInterface() {
console.log("使用Symbian系统游戏界面控制");
}
}
class AndroidInterfaceController extends InterfaceController {
controlInterface() {
console.log("使用Android系统游戏界面控制");
}
}
class WindowsInterfaceController extends InterfaceController {
controlInterface() {
console.log("使用Windows Mobile系统游戏界面控制");
}
}
抽象工厂代码
/**
* AbstractFactory 类
* 抽象工厂接口
*/
class SysFactory {
createInterface(){
throw new Error("This method must be overwritten!");
}
createOperation(){
throw new Error("This method must be overwritten!");
}
}
/**
* ConcreteFactory 类
* 生产三种不同系统的抽象工厂
*/
class SymbianSysFactory extends SysFactory {
createInterface() {
return new SymbianInterfaceController();
}
createOperation() {
return new SymbianOperationController();
}
}
class AndroidSysFactory extends SysFactory {
createInterface() {
return new AndroidInterfaceController();
}
createOperation() {
return new AndroidOperationController();
}
}
class WindowsSysFactory extends SysFactory {
createInterface() {
return new WindowsInterfaceController();
}
createOperation() {
return new WindowsOperationController();
}
}
测试一下
let sysFactory = new SymbianSysFactory();
let interfaceController = sysFactory.createInterface();
let operationController = sysFactory.createOperation();
interfaceController.controlInterface();
operationController.controlOperation();
// 输出如下结果
# 使用Symbian系统游戏界面控制
# 使用Symbian系统操作控制
// 当我们需要切换不同的操作系统时,只需要修改
sysFactory = new AndroidSysFactory();
总结
抽象工厂模式是对工厂方法模式的进一步升级使用,使得工厂方式更符合实际开发需求,在实际开发中往往需要一个产品族来完成工作。抽象工厂模式在新增产品族时很方便,无需修改已有系统,符合“开闭原则”。但抽象工厂模式最大的缺点是对于新增产品很麻烦,需要对原有系统进行较大的修改,不符合“开闭原则”。所以在实际开发中,我们需要衡量具体的需求,选择对应的设计模式,对于需要客户端使用同一产品族的需求时,可以选择抽象工厂模式进行设计。
全部工厂模式
适用场景
- 一个系统不应当依赖于产品类实例如何被创建、组合和表达的细节,这对于所有类型的工厂模式都是很重要的,用户无须关心对象的创建过程,将对象的创建和使用解耦。
优点
- 屏蔽产品的具体实现,调用者只关心产品的接口。
- 扩展性高,如果想增加一个产品,只要扩展一个工厂类就可以。
缺点
- 工厂类剧增,增加了系统的复杂度。