1、策略模式
**定义:**定义了一组算法,将每个算法都封装起来,并且使它们之间可以互换。此模式让算法的变化独立于使用算法的客户。
类图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Pyp76E14-1586506012969)(设计模式.assets/image-20200410152738286.png)]
介绍
**意图:**定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。
**主要解决:**在有多种算法相似的情况下,使用 if…else 所带来的复杂和难以维护。
**何时使用:**一个系统有许多许多类,而区分它们的只是他们直接的行为。
**如何解决:**将这些算法封装成一个一个的类,任意地替换。
**关键实现:**实现同一个接口。
应用实例: 1、诸葛亮的锦囊妙计,每一个锦囊就是一个策略。 2、旅行的出游方式,选择骑自行车、坐汽车,每一种旅行方式都是一个策略。 3、JAVA AWT 中的 LayoutManager。
优点: 1、算法可以自由切换。 2、避免使用多重条件判断。 3、扩展性良好,增加一个策略只需实现一个接口
缺点: 1、策略类会增多。 2、所有策略类都需要对外暴露。
使用场景:
-
如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为。
-
一个系统需要动态地在几种算法中选择一种。
-
如果一个对象有很多的行为,如果不用恰当的模式,这些行为就只好使用多重的条件选择语句来实现。
**注意事项:**如果一个系统的策略多于四个,就需要考虑使用混合模式,解决策略类膨胀的问题。
实现
场景:有一个鸭子游戏,里面有各种各样的鸭子,不同的鸭子有不一样的飞行行为,也会发出不一样的叫声。如:红头鸭会飞,会嘎嘎叫,但是玩具鸭不会飞,会吱吱叫。
**context:**对应类图中的客户
//所有的鸭子都必须继承自这个超类
public abstract class Duck {
//Behaviors are instance variables
FlyBehavior flyBehavior; //飞行行为的接口
QuackBehavior quackBehavior; //发出叫声的接口
public Duck() {
}
public void setFlyBehavior (FlyBehavior fb) {
flyBehavior = fb;
}
public void setQuackBehavior(QuackBehavior qb) {
quackBehavior = qb;
}
abstract void display(); //每个鸭子都有不同的外观,所有这个方法设置成抽象,让不 同的子类自己实现
public void performFly() {
System.out.println("Duck.performFly()");
flyBehavior.fly();
}
public void performQuack() {
System.out.println("Duck.performQuack()");
quackBehavior.quack();
}
public void swim() { //每个鸭子都能游泳,所以都会继承这个方法
System.out.println("Duck.swim()");
System.out.println("All ducks float, even decoys!");
}
}
**strategyA:**策略接口
public interface QuackBehavior {
public void quack();
}
**strategyB:**策略接口
public interface FlyBehavior {
public void fly();
}
**concretestrategyA1:**策略接口实现
public class FlyNoWay implements FlyBehavior {
public void fly() {
System.out.println("FlyNoWay.fly()");
System.out.println("I can't fly");
}
}
**concretestrategyA2 :**策略接口实现
public class FlyWithWings implements FlyBehavior {
public void fly() {
System.out.println("FlyWithWings.fly()");
System.out.println("I'm flying!!");
}
}
**concretestrategyB:**策略具体实现
public class Quack implements QuackBehavior {
public void quack() {
System.out.println("Quack.quack()");
System.out.println("Quack");
}
}
**context实现:**具体的客户类型
//模型鸭,不会飞,能发出叫声
public class ModelDuck extends Duck {
public ModelDuck() {
flyBehavior = new FlyNoWay(); //组合具体的实现飞行接口的行为(这里是不会 飞)
quackBehavior = new Quack(); //组合具体的实现叫声接口的行为
}
public void display() {
System.out.println("ModelDuck.display()");
System.out.println("I'm a model duck");
}
}
//红头鸭,会飞,会叫
public class RedHeadDuck extends Duck {
public RedHeadDuck() {
flyBehavior = new FlyWithWings();//组合具体的实现飞行接口的行为
quackBehavior = new Quack();//组合具体的实现叫声接口的行为
}
public void display() {
System.out.println("RedHeadDuck.display()");
System.out.println("I'm a real Red Headed duck");
}
}
在该模式中用到的设计原则:
- 找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起:
这里的飞行行为,和叫声行为对于鸭子来说是会变化的,我们拿出来独立进行封装,而游泳是每只鸭子都会的行为,我们不进行独立
- 针对接口编程,而不是针对实现编程:
我们为飞行行为和叫声行为分别设计了一个接口,并让实现这些接口的类来实现具体的行为
- 多用组合,少用继承:
将接口组合进我们的客户类中,如 Duck类中的FlyBehavior , QuackBehavior。这样我们的代码就有很大的弹性,如果具体的行为改变,我们不需要改变duck的代码,而只要创建一个引用指向新的行为就好。