文章目录
一、策略设计模式简介
Strategy模式也叫策略模式是行为模式之一,它对一系列的算法加以封装,为所有算法定义一个抽象的算法接口,并通过继承该抽象算法接口对所有的算法加以封装和实现,具体的算法选择交由客户端决定(策略)。Strategy模式主要用来平滑地处理算法的切换 。
二、策略模式结构
三、策略模式角色与职责
- Strategy:策略(算法)抽象。
- ConcreteStrategy: 各种策略(算法)的具体实现。
- Context :策略的外部封装类,或者说策略的容器类。根据不同策略执行不同的行为。策略由外部环境决定。
四、策略模式具体实现
考虑有如下类:
// An highlighted block
package design.strategy.gys;
public abstract class Duck {
public void swim() {
System.out.println("swim");
}
abstract void desc();
abstract void gaga();
}
有一个具体的方法swim(),三个抽象方法desc(),fly(),gaga(),分别用于对Duck类的描述。
1、不使用策略模式时
考虑两个子类:
// An highlighted block
public class Redduck extends Duck{
@Override
void desc() {
// TODO Auto-generated method stub
System.out.println("red head");
@Override
void gaga() {
// TODO Auto-generated method stub
System.out.println("gaga");
}
}
// An highlighted block
package design.strategy.gys;
public class Blackduck extends Duck{
@Override
void desc() {
// TODO Auto-generated method stub
System.out.println("black head");
}
@Override
void gaga() {
// TODO Auto-generated method stub
System.out.println("guagua");
}
测试程序如下:
// An highlighted block
package design.strategy.gys;
public class Testduck {
public static void main(String[] args) {
// TODO Auto-generated method stub
Redduck d=new Redduck();
Blackduck b=new Blackduck();
d.desc();d.swim();d.gaga();
System.out.println("------------");
b.desc();b.gaga();b.swim();
}
}
该测试程序输出:
// An highlighted block
red head
i can swim
gaga
------------
black head
guagua
i can swim
若此时需要对Duck类进行扩展,增加fly()方法,当不使用策略设计模式时,我们需要对每种继承的子类重写fly()方法,当继承子类较多时,工程量大,代码复用性差。考虑策略设计模式。
2、使用策略设计模式
方法论:(不仅限于策略模式)提取变化的东西,抽象成接口并在子类中实现。
我们在这里分别封装行为接口,实现算法族,超类中放行为接口的对象,子类中具体设定行为对象。
原则:基于接口编程功能
我们将行为抽象成如下两个接口:
// An highlighted block
package design.flystrategy.gys;
public interface Fly {
void fly();
}
// An highlighted block
package design.gagastrategy.gys;
public interface Gaga {
void gaga();
}
接着定义接口的实现类:
// An highlighted block
package design.flystrategy.gys;
public class Canfly implements Fly{
@Override
public void fly() {
System.out.println("i can fly");
}
}
// An highlighted block
package design.flystrategy.gys;
public class Cannotfly implements Fly{
@Override
public void fly() {
System.out.println("i cannot fly");
}
}
// An highlighted block
package design.gagastrategy.gys;
public class Cangaga implements Gaga{
@Override
public void gaga() {
System.out.println("gaga!");
}
}
// An highlighted block
package design.gagastrategy.gys;
public class Cannotgaga implements Gaga{
@Override
public void gaga() {
System.out.println("guagua!");
}
}
至此,我们的Duck类的行为算法族已经定义完成,接下来定义Duck类,此时定义与上次不同,需要在Duck类内部定义行为接口对象的域,为了表现策略模式的好处,我们定义了set()方法,代码如下:
// An highlighted block
package design.duckstrategy.gys;
import design.flystrategy.gys.Fly;
import design.gagastrategy.gys.Gaga;
public abstract class Duck {
private Fly f;
private Gaga g;
public Duck(Fly f, Gaga g) {
super();
this.f = f;
this.g = g;
}
abstract void desc();
public void swim() {
System.out.println("i can swim");
}
public void fly() {
f.fly();
}
public void gaga() {
g.gaga();
}
public void setF(Fly f) {
this.f = f;
}
public void setG(Gaga g) {
this.g = g;
}
}
下面定义两个Duck的子类:
// An highlighted block
package design.duckstrategy.gys;
import design.flystrategy.gys.Fly;
import design.gagastrategy.gys.Gaga;
public class Redduck extends Duck{
public Redduck(Fly f, Gaga g) {
super(f, g);
}
@Override
void desc() {
System.out.println("red head");
}
}
// An highlighted block
package design.duckstrategy.gys;
import design.flystrategy.gys.Fly;
import design.gagastrategy.gys.Gaga;
public class Blackduck extends Duck{
public Blackduck(Fly f, Gaga g) {
super(f, g);
}
@Override
void desc() {
System.out.println("black head");
}
}
定义的算法族和类都已定义完成,接着进行代码的测试:
// An highlighted block
package design.duckstrategy.gys;
import design.flystrategy.gys.Canfly;
import design.gagastrategy.gys.Cangaga;
import design.gagastrategy.gys.Cannotgaga;
public class Testduck {
public static void main(String[] args) {
Redduck d=new Redduck(new Canfly(), new Cangaga());
Blackduck b=new Blackduck(new Canfly(), new Cannotgaga());
d.desc();d.fly();d.gaga();d.swim();
System.out.println("--------");
b.desc();b.fly();b.gaga();b.swim();
System.out.println("--------");
b.setG(new Cangaga());
b.gaga();
}
}
测试结果:
// An highlighted block
red head
i can fly
gaga!
i can swim
--------
black head
i can fly
guagua!
i can swim
--------
gaga!
可以看到,我们很容易的实现了Duck类的行为组合与变换,并且很容易的改变子类的行为。让行为算法的变化独立于算法的使用者。
五、策略模式的优缺点
优点
- 策略模式提供了管理相关的算法族的办法。策略类的等级结构定义了一个算法或行为族。恰当使用继承可以把公共的代码移到父类里面,从而避免重复的代码。
- 策略模式提供了可以替换继承关系的办法。继承可以处理多种算法或行为。如果不是用策略模式,那么使用算法或行为的环境类就可能会有一些子类,每一个子类提供一个不同的算法或行为。但是,这样一来算法或行为的使用者就和算法或行为本身混在一起。决定使用哪一种算法或采取哪一种行为的逻辑就和算法或行为的逻辑混合在一起,从而不可能再独立演化。继承使得动态改变算法或
行为变得不可能。 - 使用策略模式可以避免使用多重条件转移语句。多重转移语句不易维护,它把采取哪一种算法或采取哪一种行为的逻辑与算法或行为的逻辑混合在一起,统统列在一个多重转移语句里面,比使用继承的办法还要原始和落后。
缺点
- 客户端必须知道所有的策略类,并自行决定使用哪一个策略类。这就意味着客户端必须理解这些算法的区别,以便适时选择恰当的算法类。换言之,策略模式只适用于客户端知道所有的算法或行为的情况。
- 策略模式造成很多的策略类。有时候可以通过把依赖于环境的状态保存到客户端里面,而将策略类设计成可共享的,这样策略类实例可以被不同客户端使用。换言之,可以使用享元模式来减少对象的数量。