设计模式

设计模式

先说明一下什么是工厂模式

  • 把创建对象的过程进行级简单封装。
  • 目的就是无脑传参

1、创建型之工厂模式

  • 构造器模式
    • 不再手写字面量 变得是个性,不变的是共性,构造器就是将将共性保存起来不变,开发个性灵活使用
  • 简单工厂
    • 我们先来看下面两段构造器代码,我们假设公司有多种不同的工种,我们需要录入name,age,career,work,等四个不同的参数
    //我们先用不同构造器构造不同的工种
    function Coder(name,age){
       this.name = name
       this.age = age 
       this.career = "Coder"
       this.work = ["写代码","改bug","掉头发"]
    }
    
    function Boss(name,age){
       this.name = name,
       this.age = age,
       this.career  = "Boss",
       this.work = ["管理","陪客户"]
    }
    
    上面这种写法可行,但是我们总不能一直人工判断工种去分配构造器,所以我们可以把中间的变交给函数处理
    function Factory(name,age,career){
    switch(career){
       case "coder":
          return new Coder(name,age)
          break;
       case "Boss":
          return new Boss(name,age)
          break;
       ...
    }
    }
    
    通过函数的方法确实可以做到自动分配,但是如果工种多了的话怎么办呢?但是如果每一个工种都写过类去区分,未免有点太蠢了?我们之前说构造器的时候提到过两个东西:个性和共性。我们继续规划我们的构造器
    function User(name,age,career,work){
       this.name =name
       this.age = age
       this.career = career
       this.work = work
    }
    
    function Factory(name,age,career){
       let work
       switch(career){
          case "Coder":
             work = ["写代码","改bug","掉头发"]
             break;
          case 'product manager':
             work = ['订会议室', '写PRD', '催更']
             break
          case "Boss":
             work = ["管理","陪客户"]
             break;
          ...
       }
       return new User(name,age,career,work)
    }
    
    通过这样的处理方式,我们只需要一种构造器就可以把多种工种分配清楚
  • 抽象工厂
    • 上面写的简单工厂例子看上去没有很大问题,但是禁不起改造,如果这时候我们想要对每个工种增加权限怎么办?赋予不同的职能怎么办?这就可能需要对Factory大肆修改,这样的操作会导致函数体变得非常庞大不好维护,为什么会出现这种问题?—— 没有遵守开放封闭原则。(对拓展开放,对修改封闭)
    • 举个例子(生产手机)
      假如生产一部手机需要两个部分:操作系统(OS)和硬件(HardWare),现在我要开一家山寨手机工厂,那么我这家工厂就需要准备以上两种东西才能实现量产,但是OS和hardWare背后又不同品牌的厂商,所以我不知道我一条具体的生产线需要生产怎么样的一种手机。已知条件只有手机组成——— 我可以先创建一个抽象类约定手机的组成。
    class MobilePhoneFactory{
       OS(){
          throw new Error("抽象工厂方法不允许直接调用,你需要将我重写")
       }
       HardWare(){
          throw new Error("抽象工厂方法不允许直接调用,你需要将我重写")
       }
    }
    
    上面的类干了啥?啥也没干,只是约定了手机流水线的能力。如果你需要它去干点啥,new一个实例还给你抛出错误!夺笋呐!!所以这个类算是始祖,处在食物链顶端的,诶!我就是啥也不干,我就是定个规矩在这。这就是一个抽象工厂
    抽象工厂不干活,那么具体工厂就需要来干活。我们已经确定了方案,明确一个流水线生产什么样的手机之后,就可以化抽象为具体。再举个栗子。
    我要生产系统A+硬件A手机,可以确定一个具体工厂
    class AMobilePhoneFactory{
       OS(){
          return new OSA()
       }
       HardWare(){
          return new HardWareA()
       }
    }
    
    这边调用了两个构造函数:OSA和HardWareA,分别生成系统和硬件。拿来用于 new 出具体对象的类,叫具体产品类(ConcreteProduct)
    具体产品类一般都有共同的功能,都有着最基本的组成,因此我们可以用一个抽象产品类(AbstractProduct)来声明这一类产品应该具有的基本功能。
    操作系统类
    //定义抽象产品类
    class OS{
       controlHardWare(){
          throw new Error("抽象产品方法不允许直接调用,你需要将我重写!")
       }
    }
    
    //定义具体产品类
    class AndroidOS extends OS{
       controlHardWare(){
          console.log("安卓系统操作硬件方法")
       }
    }
    class AppleOS extends OS{
       controlHardWare(){
          console.log("苹果系统操作硬件方法")
       }
    }
    
    硬件系统类
    // 定义手机硬件这类产品的抽象产品类
    class HardWare {
       // 手机硬件的共性方法,这里提取了“根据命令运转”这个共性
       operateByOrder() {
          throw new Error('抽象产品方法不允许直接调用,你需要将我重写!');
       }
    }
    
    // 定义具体硬件的具体产品类
    class QualcommHardWare extends HardWare {
       operateByOrder() {
          console.log('我会用高通的方式去运转')
       }
    }
    
    class MiWare extends HardWare {
       operateByOrder() {
          console.log('我会用小米的方式去运转')
       }
    }
    
    那么现在我们需要生产一台手机,只需要如下操作:
    //先实现一个具体工厂
    class myMobilePhone extends MobilePhoneFactory{
       OS(){
          //安卓系统
          return new AndroidOS()
       }
       HardWare(){
          //小米硬件
          return new MiWare()
       }
    }
    //实例一台手机
    const myPhone = new myMobilePhone()
    //先拥有操作系统
    const myOS =  myPhone.OS()
    //再拥有硬件
    const myHardWare = myPhone.HardWare()
    
    //运行结果
    myOS.controlHardWare(); //安卓系统操作硬件方法
    myHardWare.operateByOrder(); //我会用小米的方式去运转
    
    这样做的好处:如果有一天我们这样的手机了,想换个新款,全新的软件和硬件,那我们不需要对抽象工厂做任何修改,拓展新的类就可以了。
    对原有的系统没有任何潜在影响这就是之前提到的开放封闭原则。

2、创建型之单例模式(Vuex的实现模式)

  • 保证一个类只有一个实例,并提供一个访问它的全局访问点,叫做单例模式。

  • 什么东西?一个类一个实例?酱紫写?一般情况下,我们一个类都可以创建任意多的实例:

    function SingleDog(){
       this.show =function(){
          console.log("我是一单例对象")
       }
    }
    const dog1 = new SingleDog()
    const dog2 = new SingleDog()
    dog1.show()
    console.log(dog1 === dog2) //false
    
  • 如何实现是个问题哈,我们创建多少次他都只返回第一次创建的示例才行,不然永远没法相等

    function SingleDog() {
      this.show = function () {
          console.log("我是一单例对象")
      }
      this.getInstance = function () {
         // 判断是否已经new过1个实例
          if (!SingleDog.instance) {
              // 若这个唯一的实例不存在,那么先创建它
              SingleDog.instance = new SingleDog()
          }
          // 如果这个唯一的实例已经存在,则直接返回
          return SingleDog.instance
      }
    }
    const dog1 = new SingleDog().getInstance()
    const dog2 = new SingleDog().getInstance()
    console.log(dog1 === dog2) //false
    

3、创建型之原型模式

  • 原型范式:利用实例来描述对象,用实例作为定义对象和继承的基础。体现:基于原型链的继承。
  • 原型
    • 每个构造函数都有一个prototype属性,指向构造函数的原型对象,这个原型对象中有一个constructor属性指回构造函数
    • 每个实例都有一个__proto__属性,构造函数创建实例时,实例的__proto__属性就会指向构造函数的原型对象。
function cuteCat(name,age){
   this.name = name;
   this.age = age;
 };

 cuteCat.prototype.eat = function(){
   console.log("可爱小猫吃东西");
 };

 const aCat = new cuteCat('小猫1',2)

 console.log(aCat.__proto__)
 aCat.eat() //可爱小猫吃东西
 aCat.toString() //"[object Object]"

整个过程会形成一个链式调用过程,最终组成原型链,几乎所有JS对象原型链顶端是一个Object,
除了Object.prototype或者Object.create(null)创建一个没有任何原型的对象,那它也不是Object实例

4、行为型之策略模式(非常有用)

举个栗子: 一个商品价格处理多个逻辑情况

  • 当价格类型为“预售价”时,满 100 - 20,不满 100 打 9 折
  • 当价格类型为“大促价”时,满 100 - 30,不满 100 打 8 折
  • 当价格类型为“返场价”时,满 200 - 50,不叠加
  • 当价格类型为“尝鲜价”时,直接打 5 折

我擦!老子直接if-else一把梭哈!

function askPrice(tag,originPrice){
   //预售价
   if(tag==='pre'){
      if(originPrice >= 100){
         return originPrice-20
      }
      return originPrice*0.9
   }
   //大促价
   if(tag==='onSale'){
      if(originPrice >= 100){
         return originPrice-30
      }
      return originPrice*0.8
   }

   //返厂价
   if(tag === 'back'){
      if(originPrice >= 200){
         return originPrice - 50
      }
      return originPrice
   }

   //尝鲜价
   if(tag ==='fresh'){
      return originPrice*0.5
   }
}

领导:一个函数体,你他妈的写4个逻辑,严重违背单一原则!快似了得了!还违背开放封闭原则,怎么添加新逻辑,继续if-else?

重构方法
先走单一原则改造

//预热价
function pre(originPrice){
   if(originPrice >= 100){
     return originPrice - 20
   }
   return originPrice * 0.9
}

//大促价
function onSale(originPrice){
    if(originPrice >= 100 ){
       return originPrice - 30
    }
    return originPrice * 0.8
}

//返厂价
function back(originPrice){
   if(originPrice >= 100 ){
     return originPrice - 20
   }
   return originPrice * 0.9
}

//尝鲜价
function fresh(originPrice){
   return originPrice * 0.5
}

function askPrice(tag,originPrice){
     // 处理预热价
  if(tag === 'pre') {
    return pre(originPrice)
  }
  // 处理大促价
  if(tag === 'onSale') {
    return onSale(originPrice)
  }

  // 处理返场价
  if(tag === 'back') {
    return back(originPrice)
  }

  // 处理尝鲜价
  if(tag === 'fresh') {
     return fresh(originPrice)
  }
}

虽然做到了一个函数只做一件事,但是如果新增别的功能时候拓展起来就有点麻烦了,我们找找共性整合一下。
让我们拿出法宝:对象映射!

const priceProcessor = {
    pre: (originPrice) => {
        if (originPrice >= 100) {
            return originPrice - 20;
        }
        return originPrice * 0.9;
    },
    onSale: (originPrice) => {
        if (originPrice >= 100) {
            return originPrice - 30;
        }
        return originPrice * 0.8;
    },
    back: (originPrice) => {
        if (originPrice >= 200) {
            return originPrice - 50;
        }
        return originPrice;
    },
    fresh: (originPrice) => {
        return originPrice * 0.5;
    },
}

/**问价函数 */
const askPrice = (tag,originPrice) =>{
   return priceProcessor[tag](originPrice);
}

/**进行拓展 */
priceProcessor.newUser = (originPrice) => {
   if...
}

5、行为型之状态模式(非常有用)

咖啡机生产咖啡的过程,一个状态模式的诞生

  • 美式咖啡态(american):只吐黑咖啡
  • 普通拿铁态(latte):黑咖啡加点奶
  • 香草拿铁态(vanillaLatte):黑咖啡加点奶再加香草糖浆
  • 摩卡咖啡态(mocha):黑咖啡加点奶再加点巧克力

好!if-else继续梭哈 (梭哈你个棒棒锤!给你一个脑瓜嘣)

const CoffeeMaker = function () {
    this.state = 'init'; //咖啡机初始状态
    this.changeState = (state) => {
        this.state = state
        if (state === 'american') {
            // 这里用 console 代指咖啡制作流程的业务逻辑
            console.log('我只吐黑咖啡');
        } else if (state === 'latte') {
            console.log(`给黑咖啡加点奶`);
        } else if (state === 'vanillaLatte') {
            console.log('黑咖啡加点奶再加香草糖浆');
        } else if (state === 'mocha') {
            console.log('黑咖啡加点奶再加点巧克力');
        }
    }
}
/**好家伙这他妈真能行 */

const maker = new CoffeeMaker()
maker.changeState('latte') //给黑咖啡加点奶

if-else侠是吧,赶紧似了得了!拆!

const CoffeeMaker = function () {
    this.state = 'init'; //咖啡机初始状态
    this.americanProcess = () => {
        console.log('吐黑咖啡')
    }
    this.latteProcess = () => {
        this.americanProcess();
        console.log('加点奶')
    }
    this.vanillaLatteProcess = () => {
        this.latteProcess();
        console.log('再加香草糖浆');
    }

    this.mochaProcess = () => {
        this.latteProcess();
        console.log('再加巧克力');
    }

    this.changeState = (state) => {
        this.state = state
        if (state === 'american') {
            // 这里用 console 代指咖啡制作流程的业务逻辑
            this.americanProcess();
        } else if (state === 'latte') {
            this.latteProcess();
        } else if (state === 'vanillaLatte') {
            this.vanillaLatteProcess();
        } else if (state === 'mocha') {
            this.mochaProcess();
        }
    }
}

const maker = new CoffeeMaker()
maker.changeState('latte') //吐黑咖啡  加点奶

先符合单一原则,在处理开放封闭

const stateToProcessor = {
    american: function () {
        console.log('吐黑咖')
    },
    latte: function () {
        this.american();
        console.log('加点奶')
    },
    vanillaLatte: function () {
        this.latte();
        console.log('加香草糖浆')
    },
    mocha: function () {
        this.latte();
        console.log('加巧克力')
    }
}

const CoffeeMaker = function () {
    this.state = 'init'; //咖啡机初始状态
    this.changeState = (state) => {
        this.state = state
        if (!stateToProcessor[state]) {
            return
        }
        stateToProcessor[state]();
    }
}

const maker = new CoffeeMaker()
maker.changeState('latte') //吐黑咖啡  加点奶
posted @ 2022-10-29 15:38  春天游泳。  阅读(41)  评论(0编辑  收藏  举报