乐趣区

关于javascript:策略模式定义一个算法族

篇来介绍策略模式(Strategy Design Pattern)。

假如咱们要为动物进行建模,比方狗,猪,兔子等,每种动物的能力是不同的。

1,应用继承

首先你可能想到用继承的形式来实现,所以咱们编写了上面这个 Animal 类:

abstract class Animal {public void run() {System.out.println("I can run.");
    }

    public void drinkWater() {System.out.println("I can drink water.");
    }

    protected abstract String type();} 

Animal 是一个抽象类,其中包含了动物的能力,每种能力用一个办法示意:

  • _run_:奔跑能力。
  • _drinkWater_:喝水能力。
  • _type_:返回动物的品种,比方“狗”,“兔子”。这是一个形象办法,子类要去实现。

而后咱们编写 DogPigRabbit

class Dog extends Animal {public String type() {return "Dog";}
}

class Pig extends Animal {public String type() {return "Pig";}
}

class Rabbit extends Animal {public String type() {return "Rabbit";}
} 

下面的三种动物都继承了 Animal 中的 rundrinkWater_,并且都实现了本人的 _type 办法。

当初咱们想给 PigRabbit 退出 吃草 的能力,最间接的方法是别离在这两个类中退出 eatGrass 办法,如下:

class Pig extends Animal {public void eatGrass() {System.out.println("I can eat grass.");
    }

    public String type() {return "Pig";}
}

class Rabbit extends Animal {public void eatGrass() {System.out.println("I can eat grass.");
    }

    public String type() {return "Rabbit";}
} 

下面代码可能达到目标,然而不够好,因为PigRabbit 中的 eatGrass 截然不同,是反复代码,代码没能复用。

为了解决代码复用,咱们能够将 eatGrass 办法放到 Animal 中,利用继承的个性,PigRabbit 中就不须要编写 eatGrass 办法,而间接从 Animal 中继承就行。

然而,这样还是有问题,因为如果将 eatGrass 放在 Animal 中,Dog 中也会有 eatGrass,而咱们并不想让 Dog 领有 吃草 的能力。

兴许你会说,咱们能够在 Dog 中将 eatGrass 笼罩重写,让 eatGrass 不具备理论的能力,就像这样:

class Dog extends Animal {public void eatGrass() {// 什么都不写,就没有了吃草的能力}

    public String type() {return "Rabbit";}
} 

这样做尽管能达到目标,然而并不优雅。如果 Animal 的子类特地多的话,就会有很多子类都得这样笼罩 eatGrass 办法。

所以,将 eatGrass 放在 Animal 中也不是一个好的计划。

2,应用接口

那是否能够将 eatGrass 办法提取进去,作为一个接口?

就像这样:

interface EatGrassable {void eatGrass();
} 

而后,让须要有 吃草 能力的动物都去实现该接口,就像这样:

class Rabbit extends Animal implements EatGrassable {public void eatGrass() {System.out.println("I can eat grass.");
    }

    public String type() {return "Rabbit";}
} 

这样做能够达到目标,然而,毛病是每个须要 吃草 能力的动物之间就会有反复的代码,仍然没有达到代码复用的目标。

所以,这种形式还是不能很好的解决问题。

3,应用行为类

咱们能够将 吃草 的能力看作一种“行为”,而后应用“行为类”来实现。那么须要有 吃草 能力的动物,就将 吃草类 的对象,作为本人的属性。

这些行为类就像一个个的组件,哪些类须要某种性能的组件,就间接拿来用。

上面咱们编写“吃草类”:

interface EatGrassable {void eatGrass();
}

class EatGreenGrass implements EatGrassable {
    // 吃绿草
    public void eatGrass() {System.out.println("I can eat green grass.");
    }
}

class EatDogtailGrass implements EatGrassable {
    // 吃狗尾草
    public void eatGrass() {System.out.println("I can eat dogtail grass.");
    }
}

class EatNoGrass implements EatGrassable {
    // 不是真的吃草
    public void eatGrass() {System.out.println("I can not eat grass.");
    }
} 

首先创立了一个 EatGrassable 接口,然而不必动物类来实现该接口,而是咱们创立了一些行为类 EatGreenGrassEatDogtailGrassEatNoGrass,这些行为类实现了 EatGrassable接口。

这样,须要吃草的动物,岂但可能吃草,而且能够吃不同品种的草。

那么,该如何应用 EatGrassable 接口呢?须要将 EatGrassable 作为 Animal 的属性,如下:

abstract class Animal {

    // EatGrassable 对象作为 Animal 的属性
    protected EatGrassable eg;

    public Animal() {eg = null;}

    public void run() {System.out.println("I can run.");
    }

    public void drinkWater() {System.out.println("I can drink water.");
    }

    public void eatGrass() {if (eg != null) {eg.eatGrass();
        }
    }

    protected abstract String type();} 

能够看到,Animal 中减少了 eg 属性和 eatGrass 办法。

其它动物类在 构造函数 中,要初始化 eg 属性:

class Dog extends Animal {public Dog() {
        // Dog 不能吃草
        eg = new EatNoGrass();}
    
    public String type() {return "Dog";}
}

class Pig extends Animal {public Pig() {eg = new EatGreenGrass();
    }
    
    public String type() {return "Pig";}
}

class Rabbit extends Animal {public Rabbit() {eg = new EatDogtailGrass();
    }
    
    public String type() {return "Rabbit";}
} 

对代码测试:

public class Strategy {public static void main(String[] args) {Animal dog = new Dog();
        Animal pig = new Pig();
        Animal rabbit = new Rabbit();

        dog.eatGrass();    // I can not eat grass.
        pig.eatGrass();    // I can eat green grass.
        rabbit.eatGrass(); // I can eat dogtail grass.}
} 

4,策略模式

实际上,下面的实现形式应用的就是 策略模式 。重点在于 EatGrassable 接口与三个行为类 EatGreenGrassEatDogtailGrassEatNoGrass。在策略模式中,这些行为类被称为 算法族,所谓的“策略”,能够了解为“算法”,这些算法能够相互替换。

策略模式定义了一系列算法族,并封装在类中,它们之间能够相互替换,此模式让算法的变动独立于应用算法的客户

我将残缺的代码放在了这里,供大家参考,类图如下:

5,继承与组合

在一开始的设计中,咱们应用的是 继承(Is-a) 的形式,然而成果并不是很好。

最终的计划应用了策略模式,它是一种 组合(Has-a) 关系,即 AnimalEatGrassable 之间的关系。

这也是一种设计准则:多用组合,少用继承,组合关系比继承关系有更好的弹性。

6,动静设定行为

策略模式不仅重在创立一组算法(行为类),可能动静的让这些算法相互替换,也是策略模式典型利用。

所谓的“动静”是指,在程序的运行期间,依据配置,用户输出等形式,动静的设置算法。

只须要在 Animal 中退出 setter 办法即可,如下:

abstract class Animal {
    // 省略了其它代码
    
    public void setEatGrassable(EatGrassable eg) {this.eg = eg;}
} 

应用 setter 办法:

Animal pig = new Pig();
pig.eatGrass();    // I can eat green grass.

pig.setEatGrassable(new EatDogtailGrass()); // 设置新的算法
pig.eatGrass();    // I can eat dogtail grass. 

原本 pig 吃的是 绿草 ,咱们通过 setter 办法将 绿草 换成了 狗尾草,能够看到,算法的切换十分不便。

7,总结

策略模式定义了一系列 算法族 ,这些算法组也能够叫作 行为类 。策略模式应用了 组合而非继承 来构建类之间的关系,组合关系比继承关系更加有弹性,应用组合也比拟容易动静的扭转类的行为。

退出移动版