JS 设计模式之工厂模式

工厂模式

现实场景解读

插畫│綠野悠

首先我们构建这样一个场景,宝贝与妈妈,当宝贝想要商店里面的一样东西的时候,他是不能直接拿的,因为商品是需要付费的。

此时我们的妈妈就是我们宝宝的工厂提款机。宝宝想要好吃的,好玩的,他得向妈妈申请。妈妈是理性的,妈妈通常会判断该不该买这样东西。

比如说小孩生病了,不适合吃一些刺激性的事物,此时妈妈就会回绝宝宝的要求。

而且有的时候,宝宝太小,他有的时候没法说明具体想要什么,比如宝宝可能咿呀学语的说着:“糖糖”。而其实妈妈可能了解他的需求,他想要吃果冻了,所以妈妈就把果冻嚼碎喂给宝宝。

思考:

  • 宝宝不能直接拿商品,因为宝宝没有购买能力。

  • 对应着 ---- 我不想让谁都可以 new 某个对象,就像银行金卡只发行给资金量、信用级别都优秀的人。

  • 宝宝想要东西,得妈妈同意。

  • 对应着 ---- 在返回一个对象之前,我可以做一些校验逻辑,前置逻辑等。

  • 宝宝对应的需求描述不清楚,只有妈妈懂孩子,而且孩子吃果冻,妈妈会有一定的保护机制,嚼碎再给孩子吃。

  • 对应着 ---- 编写代码的人,可能要做一些危险操作,比如抛出异常,此时工厂会内部解决这个错误。

// 妈妈类
class Monther {
    // 向妈妈申请所有你想要的
    static giveMe(whatYouWant){
        switch(whatYouWant){
            // 糖糖
            case "tangtang":
                console.log("宝宝,你一个人吃果冻是非常危险的哦,妈妈喂你~~"); 
                return Month.chewJelly(new Jelly);
            // 飞飞
            case "feifei":
                console.log("宝宝,家里已经买了很多飞机了哦,")
                return null;
            default: return null;
        }
    }

    static chewJelly(jelly){
        jelly.chewed = true;
        return jelly;
    }
}

// 果冻类
class Jelly{
    // 是否咀嚼过的果冻
    chewed = false;
}

// 玩具飞机
class ToyPlane {}


// 宝宝要开始提需求了
// 宝宝要糖糖
let jelly = Monther.giveMe('tangtang');

// 宝宝要飞飞
let toyPlane = Monther.giveMe('feifei');

console.log(jelly instanceof Jelly ); // 妈妈给宝宝果冻
console.log(jelly.chewed === true ); // 宝宝得到的果冻是经过处理的
console.log(toyPlane === null) // 妈妈不同意买飞机

实际运用解读

使用过 Redux 开发 Reac t的朋友可能比较清楚 Reducer 。

export default (state = {
    isPicking: false,
    newAppleId: 1,
    apples: [
        {
            id: 0,
            weight: 235,
            isEaten: false
        }
    ]
}, action) => {
    
    let newState ;
    

    switch (action.type) {
        case 'apple/BEGIN_PICK_APPLE':
            newState = Object.assign({}, state, {
                isPicking: true
            });
            return newState;

        case 'apple/DONE_PICK_APPLE':
            newState = Object.assign({}, state, {
                apples: [
                    ...state.apples,
                    {
                        id: state.newAppleId,
                        weight: action.payload,
                        isEaten: false
                    }
                ],
                newAppleId: state.newAppleId + 1,
                isPicking: false
            })
            return newState;

        case 'apple/FAIL_PICK_APPLE':
            //这里只是简单处理
            newState = Object.assign({}, state, {
                isPicking: false
            });
            return newState;

        case 'apple/EAT_APPLE':
            newState = Object.assign({}, state, {
                apples: [
                    ...state.apples.slice(0, action.payload),
                    Object.assign({}, state.apples[action.payload], { isEaten: true }),
                    ...state.apples.slice(action.payload + 1)
                ]
            })
            return newState;

        default:
            return state;
    }

};

此段代码来自于实例讲解基于 React+Redux 的前端开发流程

这段代码的作用就是,对原有的状态,和新的提交信息组合成一个新的状态。

注意: 返回的对象并不一定都是 new 出来的,也可以是 {} 这种字面量的形式。当然也没人规定一定要返回对象,你也可以返回你所需要的字符串、数字、正则等等。

我们传入了一些参数,或者说是需求给工厂,工厂根据我们的需求返回给我们所需的东西。

接下来,我们写一个可以运行的例子


// 正则工厂
function fieldTest(fieldName){
    switch(fieldName){
        case 'phone':
            return /^1[3|4|5|8][0-9]\d{4,8}$/;
        case 'email':
            return /\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*/;
        case 'character': // 是否为汉字
            return /^[\u4e00-\u9fa5]{0,}$/;
    }
}

let email = 'belovedyogurt@gmail.com';
let name = "陈大明";

console.log(fieldTest('email').test(email));
console.log(fieldTest('character').test(name));

我们发现了一个规律,那就是工厂模式为了生产更多的东西,里面通常会用到 if 或者 switch 来进行判断。

其实仔细想一想,JS的模块何尝不是一种工厂呢?要什么就从模块里面申请什么。

对工厂模式再次进行抽象,工厂的工厂

现实场景解读

药丸
药丸
药丸
药丸

小朋友们一起玩过家家,这个过家家有2个流程,一个是把果实变成药瓶,而另一个是对药进行研磨混合。

对于大的厂商,它可能有很多个子产商,就比如手机行业的公司,它的一个分部公司专门为用户生产手机屏幕,而另外一个则专门用于生产手机电池。

思考:

  • 过家家和大的手机厂商厂

  • 对应着 ---- 对应着工厂的工厂。

  • 从工厂中得到工厂

  • 对应着 ---- 从过家家中得到其中一个流程,从大的手机厂商中得到专门生产手机屏幕的厂

function XiaoMi(factoryName) {
    switch(factoryName){
        case 'battery': return MobileBatteryFactory;
        case 'screen': return MobileScreenFactory;
    }
}

class Battery{};

class MobileBatteryFactory {
    static getBattery(){
        return new Battery();
    }
}

class MobileScreenFactory {
    static getScreen(){
        return { name: 'screen' }
    }
}

console.log(XiaoMi('battery').getBattery() instanceof Battery);
console.log(XiaoMi('screen').getScreen().name === 'screen');

实例解读

class AllFactory {
    static getFieldTest = () => fieldTest;
    static getMother = () => Mother
}

let mom = AllFactory.getMother();

let test = AllFactory.getFieldTest();

console.log(mom === Mother);
console.log(test('email').test(email)); 

我们就直接基于上面的例子进行,封装成一个新的工厂,尽管这2个工厂并不怎么搭调。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 工厂模式类似于现实生活中的工厂可以产生大量相似的商品,去做同样的事情,实现同样的效果;这时候需要使用工厂模式。 简...
    JSUED阅读 288评论 0 1
  • 关键词:函数,封装, 定义 把实现相同功能的代码放在一个函数中,以后想实现这个功能,只需要执行这个方法就行了-->...
    RomainLiu阅读 230评论 0 2
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 173,076评论 25 708
  • 再见,旧时光!(目录) 这个世界上,有千万种爱你的方式,可是我却选择了最决绝的一种。这样的我,你还喜欢吗? “二姐...
    w雨木阅读 449评论 0 2
  • 今天还是和往常一样5.50分起床,听课,下楼慢跑。因为有时外出,环境影响没有坚持运动,这周又接上去,感觉很累,心里...
    滴滴雨阅读 273评论 0 0