策略模式(Strategy)
引言
策略这个词应该怎么理解,打个比方说,我们出门的时候会选择不同的出行方式,比如骑自行车、坐公交、坐火车、坐飞机、坐火箭等等,这些出行方式,每一种都是一个策略。
再比如我们去逛商场,商场现在正在搞活动,有打折的、有满减的、有返利的等等,其实不管商场如何进行促销,说到底都是一些算法,这些算法本身只是一种策略,并且这些算法是随时都可能互相替换的,比如针对同一件商品,今天打八折、明天满100减30,这些策略间是可以互换的。
定义
定义了算法族,分别封装起来,让它们之间可以相互替换,此模式让算法的变化独立于使用算法的客户。
类型
行为型
结构类图
Context是上下文,用一个ConcreteStrategy来配置,维护一个对Strategy对象的引用;其中的doSomething() 方法会调用 behavior()。setStrategy(Strategy) 方法可以动态地改变 strategy 对象,也就是说能动态地改变 Context 所使用的算法。
Strategy是策略类,其实现类都实现了 behavior() 方法
ConcreteStrategy是具体策略类,封装了具体的算法或行为,继承于Strategy。
1.Context上下文
Context上下文角色,也叫Context封装角色,起承上启下的作用,屏蔽高层模块对策略、算法的直接访问,封装可能存在的变化。
public class Context {
Strategy strategy;
public Context(Strategy strategy) {
this.strategy = strategy;
}
//上下文接口
public void doSomething() {
strategy.behavior();
}
}
2.策略角色
抽象策略角色,是对策略、算法家族的抽象,通常为接口,定义每个策略或算法必须具有的方法和属性。
public abstract class Strategy {
//算法方法
public abstract void behavior();
}
3.具体策略角色
用于实现抽象策略中的操作,即实现具体的策略,下方用print代替。测试类共3个ConcreteStrategy,其它两个类与ConcreteStrategyA同理,就不再赘述了。
public class ConcreteStrategyA extends Strategy {
@Override
public void behavior() {
System.out.println("策略A实现");
}
}
4.Client客户端
下面依次更换策略,测试一下策略模式。
public class Client {
public static void main(String[] args) {
Context context;
context = new Context(new ConcreteStrategyA());
context.contextInterface();
context = new Context(new ConcreteStrategyB());
context.contextInterface();
context = new Context(new ConcreteStrategyC());
context.contextInterface();
}
}
适用性
-
系统中有很多类,而它们的区别仅仅是行为上的不同。使用策略模式,可以动态得在许多行为中选择一个行为。(不同的行为放到不同的类里边,有很多行为类,每种行为对应一种策略)
-
一个系统需要动态的在几种算法中选择一种(算法即策略,封装了一系列的业务逻辑,比如拿到两个数字,有加法策略,有乘法策略)
优点
- 避免使用多重条件转移语句(大量if else,switch),而是换成一个个类,替换原来if-else中的逻辑
- 符合开闭原则,能够在不修改原有系统的基础上,选择行为,并且使得行为可以易扩展(增加一个策略只需实现接口即可)
- 提高了算法的保密性和安全性(使用的时候,知道策略是干什么的就可以了,客户端不需要了解策略里头的内部实现 ,比如一个促销活动,里面封装各种各样的策略)
缺点
- 策略类数量会增多,每个策略都是一个类,复用的可能性很小。
- 客户端必须知道所有的策略类,并且自行决定使用哪个策略类。这就意味着客户端必须理解这些算法的区别,以便适时选择恰当的算法类。换言之,策略模式只适用于客户端知道所有的算法或行为的情况。
例子
这里选用head first设计模式中的例子:
设计类图:
1.Duck
public abstract class Duck {
//为行为接口类型声明两个引用变量,所有鸭子子类都继承它们。
FlyBehavior flyBehavior;
QuackBehavior quackBehavior;
public Duck(){}
public abstract void display();
public void setFlyBehavior(FlyBehavior flyBehavior) {
this.flyBehavior = flyBehavior;
}
public void setQuackBehavior(QuackBehavior quackBehavior) {
this.quackBehavior = quackBehavior;
}
public void performFly() {
//委托给行为类
flyBehavior.fly();
}
public void performQuack() {
//鸭子将呱呱叫行为委托给quackBehavior引用的对象。
quackBehavior.quack();
}
public void swim() {
System.out.println("All ducks float,even decoys!");
}
}
2.MallardDuck
public class MallardDuck extends Duck {
/// MallardDuck从Duck继承,具有flyBehavior 和quackBehavior 实例变量。
public MallardDuck() {
//Quack类处理呱呱叫。
quackBehavior = new Quack();
//FlyWithWings作为IFlyBehavior类型
flyBehavior = new FlyWithWings();
}
@Override
public void display() {
System.out.println("I'm a real Mullard Duck");
}
}
3. ModelDuck
public class ModelDuck extends Duck {
public ModelDuck() {
flyBehavior = new FlyNoWay();
quackBehavior = new Quack();
}
@Override
public void display() {
System.out.println("I'm a model duck");
}
}
4. FlyBehavior
public interface FlyBehavior {
//所有飞行行为必须实现的接口。
public void fly();
}
5.FlyNoWay
public class FlyNoWay implements FlyBehavior {
@Override
public void fly() {
System.out.println("I can't fly");
}
}
6. FlyRocketPowered
public class FlyRocketPowered implements FlyBehavior {
@Override
public void fly() {
System.out.println("I'm flying with a rocket!");
}
}
7. FlyWithWings
public class FlyWithWings implements FlyBehavior {
@Override
public void fly() {
System.out.println("I'm flying!!");
}
}
8. QuackBehavior
public interface QuackBehavior {
public void quack();
}
9.MuteQuack
public class MuteQuack implements QuackBehavior {
@Override
public void quack() {
System.out.println("<<Silence>>");
}
}
10.Quack
public class Quack implements QuackBehavior {
@Override
public void quack() {
System.out.println("Quack");
}
}
11.Squeak
public class Squeak implements QuackBehavior {
@Override
public void quack() {
System.out.println("Squeak");
}
}
12.Test
public class Test {
public static void main(String[] args) {
Duck mallard = new MallardDuck();
//调用MallardDuck继承来的perform方法,进而将绿头鸭的行为委托给quack和fly的行为类来处理。
mallard.performQuack();
mallard.performFly();
Duck model = new ModelDuck();
model.performFly();
model.setFlyBehavior(new FlyRocketPowered());
model.performFly();
}
}
引出的设计原则
1.封装变化
概念:找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起。
理解:这个概念很简单,几乎是每个设计模式背后的精神所在,所有的模式都提供了一套方法让系统中的某部分改变不会影响其它部分。
2.针对接口编程
概念:针对接口编程,而不是针对实现编程。
3.多用组合,少用继承
概念:使用组合建立系统具有很大的弹性,不仅可将算法族封装成类,更可以在运行时动态地改变行为。
模式比较
https://blog.csdn.net/weixin_44424668/article/details/103258814