初识策略模式

从一个例子开始吧。

function Duck(type){
    this.type = type;
}
Duck.prototype.swim = function(){
    console.log("I'm swimming");
}
Duck.prototype.display = function(){
    var type = this.type;
    if(type === "mallard"){
        console.log("I'm a mallard duck");
    }else if(type === "redhead"){
        console.log("I'm a redhead duck");
    }else if(type === "rubber"){
        console.log("I'm a rubber duck");
    }else if(type === "decoy"){
        console.log("I'm a decoy duck");
    }else{
        console.log("I'm a duck");
    }
}
Duck.prototype.quack = function(){
    switch(this.type){
        case "mallard":
        case "redhead":console.log("I can quack");break;
        case "rubber":console.log("I can squeak");break;
        case "decoy":console.log("I am a mute");break;
        default:console.log("I can quack");
    }
}
Duck.prototype.fly = function(){
    switch(this.type){
        case "mallard":
        case "redhead":console.log("I'm flying");break;
        case "rubber":
        case "decoy":console.log("I cannot fly");break;
        default:console.log("I'm flying");
    }
}
//测试代码
const duck = new Duck();
duck.display();
duck.swim();
duck.quack();
duck.fly();
console.log("-----------------分割线-----------------");

const mallardDuck = new Duck("mallard");
mallardDuck.display();
mallardDuck.swim();
mallardDuck.quack();
mallardDuck.fly();
console.log("-----------------分割线-----------------");

const redheadDuck = new Duck("redhead");
redheadDuck.display();
redheadDuck.swim();
redheadDuck.quack();
redheadDuck.fly();
console.log("-----------------分割线-----------------");

const rubberDuck = new Duck("rubber");
rubberDuck.display();
rubberDuck.swim();
rubberDuck.quack();
rubberDuck.fly();
console.log("-----------------分割线-----------------");

const decoyDuck = new Duck("decoy");
decoyDuck.display();
decoyDuck.swim();
decoyDuck.quack();
decoyDuck.fly();

在这里插入图片描述

  • display(),用了if...else,除了普通鸭(Duck)之外,还有绿头鸭(mallardDuck)、红头鸭(redheadDuck),橡皮鸭(rubberDuck)和诱饵鸭(decoyDuck)。
  • swim()还好,不论啥鸭,不怕水淹就算会游泳了,所以正享受着呢,“I’m swimming”。
  • quack(),用了个switch,活鸭能嘎嘎叫(quack),捏着橡皮鸭(rubberDuck)能发出吱吱叫(squeak),而诱饵鸭(decoyDuck)根本不会叫,所以时刻保持沉默(mute)。
  • fly(),也用了个 switch,橡皮鸭(rubberDuck)和诱饵鸭(decoyDuck)不是活鸭,当然不会飞。

鸭子们swim()一样,display()quack()fly()却各有各的表现,这其实很容易让我们想到另一种实现方式:继承
超类拥有统一的swim()display()quack()fly()子类如果具有自己独特的表现,覆盖掉就好了。
那就试试吧。

继承

class Duck{
    display(){
        console.log("I'm a duck");
    }
    swim(){
        console.log("I'm swimming");
    }
    quack(){
        console.log("I can quack");
    }
    fly(){
        console.log("I'm flying");
    }
}

class MallardDuck extends Duck{
    display(){
        console.log("I'm a mallard duck");
    }
}
class RedHeadDuck extends Duck{
    display(){
        console.log("I'm a redhead duck");
    }
}
class RubberDuck extends Duck{
    display(){
        console.log("I'm a rubber duck");
    }
    quack(){
        console.log("I can squeak");
    }
    fly(){
        console.log("I cannot fly");
    }
}
class DecoyDuck extends Duck{
    display(){
        console.log("I'm a decoy duck");
    }
    quack(){
        console.log("I am a mute");
    }
    fly(){
        console.log("I cannot fly");
    }
}
//测试代码
const duck = new Duck();
duck.display();
duck.swim();
duck.quack();
duck.fly();
console.log("-----------------分割线-----------------");

const mallardDuck = new MallardDuck();
mallardDuck.display();
mallardDuck.swim();
mallardDuck.quack();
mallardDuck.fly();
console.log("-----------------分割线-----------------");

const redheadDuck = new RedHeadDuck();
redheadDuck.display();
redheadDuck.swim();
redheadDuck.quack();
redheadDuck.fly();
console.log("-----------------分割线-----------------");

const rubberDuck = new RubberDuck();
rubberDuck.display();
rubberDuck.swim();
rubberDuck.quack();
rubberDuck.fly();
console.log("-----------------分割线-----------------");

const decoyDuck = new DecoyDuck();
decoyDuck.display();
decoyDuck.swim();
decoyDuck.quack();
decoyDuck.fly();

在这里插入图片描述
可以看到,继承能够避免部分重复代码。嗯,继承能帮我们实现代码复用
呀!还得知道小鸭子喜欢吃什么,那就给超类Duck添加eat()方法吧。

class Duck{
//新增eat方法
	eat(){
		console.log("I eat fish and shrimps");
	}
}

呃,绿头鸭和红头鸭喜欢吃小鱼小虾,橡皮鸭和诱饵鸭什么也不吃。所以,绿头鸭和红头鸭直接继承超类的eat()就好,橡皮鸭和诱饵鸭就得覆写超类的eat()了。

class RubberDuck extends Duck{
//覆写Duck的eat
    eat(){
        console.log("I eat nothing");
    }
}
class DecoyDuck extends Duck{
//覆写Duck的eat
    eat(){
        console.log("I eat nothing");
    }
}

如果再给超类Duck添加一个sleep()呢?哦,我们又得想了:啊,红头鸭绿头鸭会睡,但橡皮鸭和诱饵鸭不用睡。
是不是发现一点问题了?每给超类Duck添加一个行为,我们都必须考虑下子类们是不是也具备同样的行为,如果一样是最好了,如果不一样,就得一个个覆写。好在本例只有4个子类,如果有40个呢?是不是每天都可以领加班夜宵了。
所以,继承不是最好的选择。
接口试试?试试就试试。

接口

swim()大家都有,且一样,可以称作是不变的部分
display()大家都必须有,只是不同而已。
quack()有“嘎嘎叫”,有“吱吱叫”,还有根本不会出声的。所以quack()时有时无。
fly(),要么能飞,要么不能飞,所以fly()时有时无。
eat(),要么吃小鱼小虾,要么根本不用吃,所以eat()时有时无。
“大家必须有”的放在超类Duck中,“时有时无”的用接口。“有”的话,子类就需要去实现接口。像这样:

//TypeScript
class Duck{
    display(){
        console.log("I'm a duck");
    }
    swim(){
        console.log("I'm swimming");
    }
}
interface Flyable{
    fly();
}
interface Quackable{
    quack();
}
interface Eatable{
    eat();
}
class MallardDuck extends Duck implements Flyable,Quackable,Eatable{
    display(){
        console.log("I'm a mallard duck");
    }
    quack(){
        console.log("I can quack");
    }
    fly(){
        console.log("I can fly");
    }
    eat(){
        console.log("I eat fish and shrimps");
    }
}
class RedHeadDuck extends Duck implements Flyable,Quackable,Eatable{
    display(){
        console.log("I'm a redhead duck");
    }
    quack(){
        console.log("I can quack");
    }
    fly(){
        console.log("I can fly");
    }
    eat(){
        console.log("I eat fish and shrimps");
    }
}
class RubberDuck extends Duck implements Quackable{
    display(){
        console.log("I'm a rubber duck");
    }
    quack(){
        console.log("I can squeak");
    }
}
class DecoyDuck extends Duck{
    display(){
        console.log("I'm a decoy duck");
    }
}

在这里插入图片描述
有没有感觉代码变多了?
在这里插入图片描述
使用继承时,继承超类Duck的子类拥有了Duck的所有方法,display()swim()quack()fly(),我们更关注子类要覆盖的方法:quack()fly()
现在,将quack()fly()从超类Duck中剥离而成为独立接口QuackableFlyable,子类无法通过继承来享有这些方法,要想有,可以,实现接口QuackableFlyable吧。此时,不但没有解决上面遇到的问题,反而失去了继承达到的代码复用的优势。
怎么办呢?记住下面的原则吧。

  • 把不变的部分和变化的部分分开,并封装变化的部分
  • 少用继承,多用组合
  • 不要针对实现编程,要针对接口编程
策略模式

为了避免意大利面条,分块放代码。

interface FlyBehavior{
    fly();
}
class FlyWithWings implements FlyBehavior{
    fly(){
        console.log("I'm flying");
    }
}
class FlyNoWay implements FlyBehavior{
    fly(){
        console.log("I cannot fly");
    }
}
interface QuackBehavior{
    quack();
}
class Quack implements QuackBehavior{
    quack(){
        console.log("I can quack");
    }
}
class Squeak implements QuackBehavior{
    quack(){
        console.log("I can squeak");
    }
}
class Mute implements QuackBehavior{
    quack(){
        console.log("I am a mute");
    }
}
abstract class Duck{
    flyBehavior:FlyBehavior;
    quackBehavior:QuackBehavior;
    constructor(f:FlyBehavior,q:QuackBehavior){
        this.flyBehavior = f;
        this.quackBehavior = q;
    }
    setFlyBehavior(f:FlyBehavior){
        this.flyBehavior = f;
    }
    setQuackBehavior(q:QuackBehavior){
        this.quackBehavior = q;
    }

    performFly(){
        this.flyBehavior.fly();
    }
    performQuack(){
        this.quackBehavior.quack();
    }

    abstract display();

    swim(){
        console.log("I'm swimming");
    }
}
class MallardDuck extends Duck{
    constructor(){
        var f:FlyBehavior = new FlyWithWings();
        var q:QuackBehavior = new Quack();
        super(f,q);
    }
    display(){
        console.log("I'm a mallard duck");
    }
}

class RedHeadDuck extends Duck{
    constructor(){
        var f:FlyBehavior = new FlyWithWings();
        var q:QuackBehavior = new Quack();
        super(f,q);
    }
    display(){
        console.log("I'm a redhead duck");
    }
}

class RubberDuck extends Duck{
    constructor(){
        var f:FlyBehavior = new FlyNoWay();
        var q:QuackBehavior = new Squeak();
        super(f,q);
    }
    display(){
        console.log("I'm a rubber duck");
    }
}

class DecoyDuck extends Duck{
    constructor(){
        var f:FlyBehavior = new FlyNoWay();
        var q:QuackBehavior = new Mute();
        super(f,q);
    }
    display(){
        console.log("I'm a decoy duck");
    }   
}

下面则是测试代码。

var mallardDuck = new MallardDuck();
mallardDuck.display();
mallardDuck.swim();
mallardDuck.performQuack();
mallardDuck.performFly();
console.log("-----------------分割线-----------------");

var redheadDuck = new RedHeadDuck();
redheadDuck.display();
redheadDuck.swim();
redheadDuck.performQuack();
redheadDuck.performFly();
console.log("-----------------分割线-----------------");

var rubberDuck = new RubberDuck();
rubberDuck.display();
rubberDuck.swim();
rubberDuck.performQuack();
rubberDuck.performFly();
console.log("-----------------分割线-----------------");

var decoyDuck = new DecoyDuck();
decoyDuck.display();
decoyDuck.swim();
decoyDuck.performQuack();
decoyDuck.performFly();

补充:举例和主要思想来自<HeadFirst设计模式>

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值