顿搜
策略模式(Strategy)——23种设计模式之行为型模式
在软件构建过程中,某些对象使用的算法可能 多种多样,经常改变,如果将这些算法都编码 到对象中,将会使对象变得异常复杂;而且有 时侯支持不使用的算法也是一个性能负担。如何在运行时根据需要透明地更改对象的 算法?将算法与对象本身解耦,从而避免上述 问题?
策略模式定义了一系列算法,并将每个算法封装起来,使他们可以相互替代。本模式使得算法可独立于使用它的客 户而变化。简言之,就是“准备一组算法,并将每一个算法封 装起来,使得它们可以互换”
策略模式分别封装行为接口,实现算法族,超类中放行为接口对象,在子类里具体设定行为对象。原则就是:分离变化部分,封装接口,基于接口编程各种功能,此模式让行为算法的变化独立于算法的使用者。
一、类图表示
策略模式涉及到Strategy(抽象策略类),ConcreteStrategy(具体策略类),Context(环境类)等角色
- ①、Strategy(抽象策略类):定义所有支持的算法的公共接口。Context使用这个接来调用某ConcreteStrategy定义的算法。
- ②、ConcreteStrategy(具体策略类):Strategy接口实现某具体算法。
- ③、Context(环境类):用一个ConcreteStrategy对象来配置。维护一个对Strategy对象的引用。可定义一个接口来让Strategy访问它的数据。
二、代码示例
//行为接口
public interface BehaviorModel{
public void behavior();
}//具体行为A
public Class BehaviorModel_A implements BehaviorModel{
@override
public void behavior(){
System.out.println("I am BehaviorModel_A");
}
}//具体行为B
public Class BehaviorModel_B implements BehaviorModel{
@override
public void behavior(){
System.out.println("I am BehaviorModel_B");
}
}public abstract Class Person{
BehaviorModel be ;
public Person(){}
public void behavior (){
be.behavior();
}
public void setBe(BehaviorModel be){
this.be = be;
}
}//第一个实例
public Class Person_A extends Person{
public Person_A(){
be = new BehaviorModel_A();
}
}//第二个实例
public Class Person_B extends Person{
public Person_B(){
be = new BehaviorModel_B();
}
}public Class Test{
public statci void main(String[] args){
Person p_A = new Person_A();
Person p_B = new Person_B();
p_A.behavior();
p_B.behavior();
p_A.setBe(new BehaviorModel_B);
p_A.behavior();
}
}三、模式分析
在策略模式中,应当由客户端自己决定在什么情况下使用什么具体策略角色。
- ①、Strategy和Context相互作用以实现选定的算法。当算法被调用时, Context可以将该算法所需要的所有数据都传递给该Strategy 。或者,Context可以将自身作为一个参数传递给Strategy操作。这就让Strategy在需要时可以回调Context 。
- ②、Context将它的客户的请求转发给它的Strategy 。客户通常创建并传递一个ContextStrategy对象给该Context ;这样, 客户仅与Context交互。通常有一系列的 ContextStrategy类可供客户从中选择。
四、优缺点分析
1、优点
- ①、策略模式提供了管理相关的算法族的办法。策略类的等级结构定义了一个算法或行为族。恰当使用继承可以吧公共的代码移到父类里面,从而避免重复的代码
- ②、策略模式提供了可以替换继承关系的办法。继承可以处理多种算法或行为。如果不使用策略模式,那么使用算法或行为的环境类就可能会有一些子类,每一个子类提供一个不同的算法行为。但是,这样一来算法或行为的使用者就和算法或行为本身混在一起。决定使用哪一种算法或采取哪一种行为的逻辑就和算法或行为的逻辑混合在一起,从而不可能独立演化。继承使得动态改变算法或者行为变得不可能。
- ③、使用策略可以避免使用多重条件转移语句。多重转移语句不易维护,它把采取哪一种算法或采取哪一种行为的逻辑与算法或行为的逻辑混合在一起,统统列在一个多重转移语句里面,比使用继承的办法还要原始和落后。消除条件判断语句,就是在解耦。含有许多条件判 断语句的代码通常都需要策略模式。
- ④、封装算法,支持算法的变化,策略模式及其子类为组件提供了一系列可重用的算法,从而可以使得类型在运行时方便地根据需要在各个算法之间进行切换。
2、缺点
- ①、客户端必须知道所有策略类,并自行决定使用哪一个策略类。这就意味着客户端必须理解这些算法的区别,以便适时选择恰当的算法类。换言之,策略模式只适用于客户端知道所有的算法或行为的情况。
- ②、策略模式造成很多的策略类。有时候可以通过把依赖于环境的状态保存到客户端里面,而将策略类设计成可共享的,这样策略类实例可以被不同客户端使用。换言之,可以使用享元模式来减少对象的数量。
五、设计原则分析
1、“开-闭”原则
策略模式是“开-闭”原则的极好应用范例。在采用策略模式之前,设计师必须从“开-闭原则”出发,考虑是否有可能在将来引入新的算法。如果有可能,那么就应当将所有的算法封装起来,因为他们是可变化的因素。系统必须能够在新的算法出现时,很方便地将新的算法插入到已有的系统中,而不必修改已有的系统。
2、里氏替换原则
策略模式要求所有的策略对象都是可以互换的,因此他们都必须是一个抽象策略角色的子类。在客户端则仅知道抽象策略角色类型,虽然变量的真实类型可以是任何一个具体策略角色的实例。
六、模式间关系
1、与享元模式的关系
- ①、Strategy对象经常是很好的 Flyweight对象。如果有多个客户端对象需要调用同样的一些策略类的话,就可以使它们实现享元模式,这样客户端可以共享这些策略类。
2、与模板方法模式的关系:两种模式都可以分离通用的算法和具体的上下文,在软件设计中经常有这种需求。我们有一个通用的算法,为了遵循依 赖倒臵原则,我们想确保这个通用的算法不要依赖于具体的 实现,我们希望通用的算法和具体的实现都依赖于抽象。
- ①、模板方法给了面向对象编程中诸多经典重用形式中的一种。 其中通用算法被放臵在基类里,而且通过继承在不同的具 体上下文中实现该通用算法。由于继承关系很强,派生类 不可避免地要和它们的基类绑定在一起。
- ②、策略模式采用不同的方法倒置算法与具体实现间的依赖关 系。不是将通用算法放在一个抽象基类中,而是将它放进 一个具体类中,将通用算法必须要调用的抽象方法定义在 一个接口中,从接口中派生出具体策略。
- ③、两种模式都可以用来分离高层的算法和低层的具体实现细节, 都允许高层的算法独立于它的具体实现细节重用。策略模式 比模板方法模式涉及更多数量的类和间接层次,减小了通用 算法和该算法所控制的具体细节之间的耦合,不过要以一些额外的复杂性、内存以及时间开销作为代价。
七、应用场景
如果一个系统里面有许多类,他们之间的区别在于他们的行为,那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为。
- ①、一个系统需要动态的几种算法中选择一种。那么这些算法可以包装到一个个的具体算法类里面,而这些具体算法类都是一个抽象算法类的子类。换言之,这些具体算法类均有统一的接口,由于多态性原则,客户端可以选择使用任何一个具体算法类,并只持有一个数据类型是抽象算法类的对象。
- ②、一个系统的算法使用的数据不可以让客户端知道。策略模式可以避免让客户端涉及到不必要接触到的复杂的和只与算法有关的数据。
- ③、如果一个对象有很多行为,如果不用恰当的模式,这些行为就只好使用多重的条件选择语句来实现。此时,使用策略模式,把这些行为转移到相应的具体策略里面,就可以避免使用难以维护的多重条件选择语句,并体现面向对象设计的概念。
八、总结
策略模式提供了管理相关的算法族的办法
- ①、策略模式的决定权在用户,系统本身提供不同算法的实现,
- ②、多用组合少用继承,用行为类组合优于行为的继承。
- ③、策略模式是一个比较容易理解和使用的设计模式, 策略模式是对算法的封装,它把算法的责任和算法 本身分割开,委派给不同的对象管理。策略模式通 常把一个系列的算法封装到一系列的策略类里面, 作为一个抽象策略类的子类。
- ④、策略模式仅仅封装算法,提供新算法插入到已有系统 中,以及老算法从系统中“退休”的方便,策略模式 并不决定在何时使用何种算法,算法的选择由客户端 来决定。这在一定程度上提高了系统的灵活性,但是 客户端需要理解所有具体策略类之间的区别,以便选 择合适的算法,这也是策略模式的缺点之一,在一定 程度上增加了客户端的使用难度。
