最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • Design Pattern | 策略模式( Strategy Pattern )前端 TypeScript

    正文概述 掘金(makuta)   2021-02-24   355

    前言

    先从简单的模拟鸭子应用做起

    假设有个公司做了一套相当成功的模拟鸭子的游戏。游戏中出现各种鸭子,一边游泳戏水,一边呱呱叫,所有鸭子的区别在于外观,比如有白色,黑色,黄色等,所以我们需要设计一个父类(也可以叫超类),然后让各种鸭子去继承这个父类。

    Design Pattern | 策略模式( Strategy Pattern )前端 TypeScript

    上面这图体现出了所有鸭子都有鸭叫和游泳的方法,唯一的区别在于外观不一样,所以这时候外观需要使用抽象,然后在各个子类上实现自己的行为。

    现在我们得让鸭子飞起来

    有一天呢,公司内部头发风暴后产生了一个新的创意,需要有会飞的鸭子,有人肯定会说我花一下功夫就可以解决了,在鸭子类上加个会飞的方法就行了,这有什么困难?

    Design Pattern | 策略模式( Strategy Pattern )前端 TypeScript

    可怕的问题发生了......

    某天游戏中出现了很多“橡皮鸭子”在屏幕上飞来飞去,到底是怎么回事呢?A经理忽略了一件事,并非所有的鸭子都会飞,A直接在父类上加了个飞行的方法,导致本不应该存在的行为发生了。我们在对类处理时,尤其涉及“维护” 时,为了“复用”目的而使用继承,结局并不完美。

    那该如何解决呢?

    A经理想到那就从继承上来解决吧,既然橡皮鸭子不能飞,那就在橡皮鸭子的子类上去覆盖变成什么都不做,可是,如果以后加入了别的鸭子比如说木头鸭子那该怎么办呢,不会叫也不会飞,难道又要再一次覆盖吗?所以说继承也有一些缺点:

    • 代码在多个子类中重复
    • 运行时的行为不容易改变
    • 很难知道所有鸭子的全部行为
    • 改变会影响全身,造成其他鸭子不想要的改变

    那利用接口如何呢?

    Design Pattern | 策略模式( Strategy Pattern )前端 TypeScript

    我们知道,并非“所有”的子类都具有飞行和鸭叫的行为,所以继承并不是适当的解决方式。虽然飞行接口和鸭叫接口可以解决“一部分问题”,但是却造成代码无法复用,因为接口不具有实现代码,所以继承接口无法达到代码的复用。意味着,无论何时你需要修改某个行为的时候,你必须追踪每一个定义此行为的类中修改它,一不小心,可能会造成新的错误!

    这只能算是从一个噩梦跳到另一个噩梦里。甚至,在会飞的鸭子中,飞行的动作可能还有多种变化....?但幸运的是,有一个设计原则,恰好适用于此状况。(终于有救了)

    设计原则

    重要的事说三遍!!!

    找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起。

    找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起。

    找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起。

    换句话来说就是每次有新的需求时,都会使某方面的代码发生变化,那么你可以确定,这部分的代码需要被抽出来,和其他稳定的代码有所区分。好,接下来我们开始把鸭子的行为从鸭子类中取出的时候了!

    从哪里开始呢?就我们目前所知,除了飞行和鸭叫的问题之外,鸭子类还算一切正常,似乎没有特别需要经常变化或修改的地方。所以,除了某些小改变之外,我们不打算对鸭子类做太多处理。

    接下来我们准备建立两组类(完全远离鸭子的类),一个是飞行相关的的,一个是鸭叫相关的,每一组类将实现各自的动作

    Design Pattern | 策略模式( Strategy Pattern )前端 TypeScript

    设计鸭子的行为

    如何设计那组实现飞行和鸭叫的行为的类呢?

    我们希望一切能有弹性,毕竟,正是因为一开始鸭子行为没有弹性,才让我们走上现在这条路。我们还想指定行为到鸭子的实例,比如说,我们想产生一个新的绿头鸭实例,并指定特定类型的飞行行为给它。干脆让鸭子的行为可以动态变更好了。换句话来说,我们应该在鸭子类中包含设定行为的方法,这样就可以在运行时去动态的改变绿头鸭的飞行行为。(前面说过继承在运行时不容易动态的改变),有了这些目标要实现,接着看下第二个设计原则

    重要!!! (不了解接口和实现的同学,可以自行google了解下

    针对接口编程,而不是针对实现编程!

    针对接口编程,而不是针对实现编程!

    针对接口编程,而不是针对实现编程!

    我们利用接口代表每个行为,比方说,FlyBehavior 和 QuackBehavior,而行为的每个实现都将实现其中的一个接口。

    所以这次鸭子类不会负责实现 Flying 和 Quacking 接口,反而是由我们制造一组其它类专门实现,这就称为“行为”类。由行为类而不是鸭子类来实现接口。

    这样的迥异于以往,以前的做法是:行为来自鸭子父类的具体实现,或是继承某个接口并由子类自行实现而来,这两种做法都是依赖于“实现”。

    在我们新的设计中,鸭子的子类将使用接口 FlyBehavior 和 QuackBehavior 表示行为,所以实现不会绑定在鸭子的类中。

    两个接口具体实现

    Design Pattern | 策略模式( Strategy Pattern )前端 TypeScript

    // 鸭子行为接口
    interface FlyBehavior {
      fly(): void
    }
    
    // 鸭子会飞实现
    class FlyWithWings implements FlyBehavior {
      fly() {
         console.log('i am flying')
      }
    }
    
    // 鸭子不会飞的接口和实现
    class FlyNoWay implements FlyBehavior {
      fly() {
         console.log("i can't flying")
      }
    }
    

    Design Pattern | 策略模式( Strategy Pattern )前端 TypeScript

    // 鸭叫接口
    interface QuackBehavior {
      quack(): void
    }
    
    // 嘎嘎叫
    class Quack implements QuackBehavior {
      quack() {
        console.log('quack')
      }
    }
    
    // 不叫
    class MuteQuack implements QuackBehavior {
      quack() {
        console.log('slient')
      }
    }
    
    // 吱吱叫
    class Squeak implements QuackBehavior {
      quack() {
        console.log('squeak')
      }
    }
    

    整合鸭子的行为

    关键在于,鸭子现在会将飞行和鸭叫的动作委托别人处理,而不是使用定义在鸭类或者子类的鸭叫和飞行方法。

    1. 首先,在父类中加入两个实例的变量,分别为 flyBehavior 与 quackBehavior,那个为接口类型(而不是具体类实现类型),每个鸭子对象都会动态设置这些行为(FlyWithWings, Squeak等)。 在父类下删除 fly 和 quack 两个方法,并且用一个替代方法去替代 fly 和 quack,比如 performFly() 和 performQuack()

    2. 实现 performFly()

      class Duck {
         abstract flyBehavior: FlyWithThings
         abstract quackBehavior: QuackBehavior
          
         public performfly() {
           this.flyBehavior.fly()
         }
          
         public performquack() {
           this.quackBehavior.quack()
         }
          
         public swim() {
           console.log('all swim')
         }
          
         public abstract display(): void
      }
      
    3. 将子类继承父类(也叫抽象类)

      class ModelDuck extends Duck {
          flyBehavior = new FlyNoWay()
          quackBehavior = new Squeak()
          
          public display() {
            console.log('display')
          }
      }
      

    动态设定行为

    1. 在鸭子类中,加入两个新方法

      class ModelDuck extends Duck {
          flyBehavior = new FlyNoWay()
          quackBehavior = new Squeak()
          
          public display() {
            console.log('display')
          }
              
          
          public setflyBehavior(fb: FlyBehavior) {
            this.flyBehavior = fb
          }
          
          public setQuackBehavior(qb: QuackBehavior) {
            this.quackBehavior = qb
          }
      }
          
      
    2. 建立一个新的FlyBehavior模型

      class FlyRocketPowered implements FlyBehavior {
      	public fly() {
      		console.log("I'm flying with a rocket! ")
      	}
      }
      
    3. 加上火箭动力

      var modelduck = new ModelDuck();
      modelduck.performfly();
      modelduck.setflyBehavior(new FlyRocketPowered());
      modelduck.performfly();
      

    总结

    有一个关系相当有趣,每一个鸭子都有一个 FlyBehavior 和 一个 QuackBehavior,好将飞行和鸭叫委托给它们代为处理。

    当你讲两个类结合起来使用,如同本例一般,就是组合(composition)。这种做法和继承不同的地方在于行为不是继承来的,而是和适当的行为对象“组合”来的。

    这是一个很重要的技巧。其实使用了我们的第三个设计原则

    重要!!!

    多用组合,少用继承

    多用组合,少用继承

    多用组合,少用继承

    如你所见,使用组合建立系统具有很大的弹性,不仅可将算法封装成类,更可以“在运行时动态地改变行为”,只要组合的行为对象符合正确的接口标准即可。

    最后恭喜你,学会了策略模式。

    ???????

    源码地址:

    codesandbox.io/s/typescrip…


    起源地下载网 » Design Pattern | 策略模式( Strategy Pattern )前端 TypeScript

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元