本文探讨初学使用策略模式时遇到的一些疑惑,以及在工作中慢慢解决之前遇到的疑惑,借此与大家分享。比如说本文谈到策略模式中环境角色Context的用处,为什么一定要用,可不可以将此取消。这些都是在学习和工作的实践总结中慢慢体会到的。
首先,我们来看下策略模式的概念。一般的解释如下:
策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换。策略模式让算法独立于使用它的客户而独立变化。(原文:The Strategy Pattern defines a family of algorithms,encapsulates each one,and makes them interchangeable. Strategy lets the algorithm vary independently from clients that use it.)
一般的,策略模式主要分为以下三个角色:
1. 环境角色(Context):持有一个策略类引用
2. 抽象策略(Strategy):定义了多个具体策略的公共接口,具体策略类中各种不同的算法以不同的方式实现这个接口;Context使用这些接口调用不同实现的算法。一般的,我们使用接口或抽象类实现。
3. 具体策略(ConcreteStrategy):实现抽象策略类中的相关的算法或操作。
我们首先来写一个简单的策略模式,然后再结合实际应用进行扩展,进而思考其在实际开发中的使用方法。
(给大家推荐前辈写的一篇不错的博文,研磨设计模式之 策略模式
http://www.uml.org.cn/sjms/201009092.asp)
我们这个简单的策略假设就是想让不同的策略来实现某个算法(algorithm),抽象类如下:
package com.icecode.demo.strategy;
/**
* 抽象策略
* @author zhanche
*
*/
public abstract class AbstractStrategy {
/**
* 某个希望有不同策略实现的算法
*/
public abstract void algorithm();
}
算法algorithm的具体实现策略类ConcreteStrategy1和ConcreteStrategy2如下:
package com.icecode.demo.strategy.impl;
import com.icecode.demo.strategy.AbstractStrategy;
/**
* 对算法的第一种具体实现策略
* @author zhanche
*
*/
public class ConcreteStrategy1 extends AbstractStrategy {
@Override
public void algorithm() {
System.out.println("----------------我是策略一算法----------------");
}
}
package com.icecode.demo.strategy.impl;
import com.icecode.demo.strategy.AbstractStrategy;
/**
* 对算法的第二种具体实现策略
* @author zhanche
*
*/
public class ConcreteStrategy2 extends AbstractStrategy {
@Override
public void algorithm() {
System.out.println("----------------我是策略二算法----------------");
}
}
环境角色的实现如下:
package com.icecode.demo.context;
import com.icecode.demo.strategy.AbstractStrategy;
/**
* 环境角色,主要完成对特定策略的调用
* @author zhanche
*
*/
public class Context {
private AbstractStrategy strategy;
public Context(AbstractStrategy strategy) {
this.strategy = strategy;
}
public void algorithm() {
this.strategy.algorithm();
}
}
下面简单写一个客户端测试的代码:
package com.icecode.demo;
import com.icecode.demo.context.Context;
import com.icecode.demo.strategy.impl.ConcreteStrategy1;
import com.icecode.demo.strategy.impl.ConcreteStrategy2;
/**
* 策略模式测试类
* @author zhanche
*
*/
public class Client {
/**
* @param args
*/
public static void main(String[] args) {
Context context = new Context(new ConcreteStrategy1());
context.algorithm();
context = new Context(new ConcreteStrategy2());
context.algorithm();
}
}
输出结果如下:
----------------我是策略一算法----------------
----------------我是策略二算法----------------
好吧,到此为止,一个简单的策略模式就写完了。但是,大家肯定有所疑惑,Context完全可以没有嘛,既然抽象策略AbstractStrategy已经持有algorithm这个接口,我们完全可以如下去写代码,让系统根据不同的实现执行不同的策略不就完了。代码可以如下:
package com.icecode.demo;
import com.icecode.demo.strategy.AbstractStrategy;
import com.icecode.demo.strategy.impl.ConcreteStrategy1;
import com.icecode.demo.strategy.impl.ConcreteStrategy2;
/**
* 策略模式测试类
* @author zhanche
*
*/
public class Client {
/**
* @param args
*/
public static void main(String[] args) {
AbstractStrategy strategy = new ConcreteStrategy1();
strategy.algorithm();
strategy = new ConcreteStrategy2();
strategy.algorithm();
}
}
输出结果如下:
----------------我是策略一算法----------------
----------------我是策略二算法----------------
可见,2种方案都实现了同一个引用根据不同的实现执行特定的算法。是的,分析发现,在上面简单的应用中,Context的确可以取消。那么,Context这个角色为什么还要存在呢?
让我们考虑以下几种情况:
1、如果我们需要对不同策略中相同算法的参数,执行相同的安全性检查,我们如果没有环境角色Context,则只能在每个实现的开始部分,调用安全性检查代码;而有了Context这个角色,我们可以在调用Context的构造器时,统一进行安全性检查。这在我们的实现策略比较多的时候,比如说7、8个的时候,特别有用,可以大量减少冗余的代码量。
2、如果我们需要改变原有算法时,需要引进新的参数,如果没有Context,我们怎么办?一种办法是重载该算法,增加新的函数接口;另外一种办法是完全废弃原有的函数接口,重新写新的函数接口。毋庸置疑,这2种办法的代价都很大,尤其是如果这个新的参数只有部分实现策略中的该算法实现用到的时候。而我们使用Context就可以完全解决这个问题。
下面我们改造下上面那个基本策略模式,我们让策略模式也持有对Context的引用,这样的优点是可以在策略类里回调的Context里的所有可用的变量或函数等信息。此外,我们也增加一个新的实现策略类ConcreteStrategy3,具体代码如下所示:
package com.icecode.demo.strategy;
import com.icecode.demo.context.Context;
/**
* 抽象策略
* @author zhanche
*
*/
public abstract class AbstractStrategy {
/**
* 某个希望有不同策略实现的算法
*/
public abstract void algorithm(Context context);
}
package com.icecode.demo.strategy.impl;
import com.icecode.demo.context.Context;
import com.icecode.demo.strategy.AbstractStrategy;
/**
* 对算法的第一种具体实现策略
* @author zhanche
*
*/
public class ConcreteStrategy1 extends AbstractStrategy {
@Override
public void algorithm(Context context) {
System.out.println("----------------我是策略一算法----------------");
}
}
package com.icecode.demo.strategy.impl;
import com.icecode.demo.context.Context;
import com.icecode.demo.strategy.AbstractStrategy;
/**
* 对算法的第二种具体实现策略
* @author zhanche
*
*/
public class ConcreteStrategy2 extends AbstractStrategy {
@Override
public void algorithm(Context context) {
System.out.println("----------------我是策略二算法----------------");
}
}
package com.icecode.demo.strategy.impl;
import com.icecode.demo.context.Context;
import com.icecode.demo.strategy.AbstractStrategy;
/**
* 对算法的第三种具体实现策略
* @author zhanche
*
*/
public class ConcreteStrategy3 extends AbstractStrategy {
@Override
public void algorithm(Context context) {
System.out.println("----------------我是策略三算法----------------");
}
}
package com.icecode.demo.context;
import com.icecode.demo.strategy.AbstractStrategy;
/**
* 环境角色,主要完成对特定策略的调用
* @author zhanche
*
*/
public class Context {
/**
* 持有对策略的引用
*/
private AbstractStrategy strategy;
/**
* 算法入口
*/
public void algorithm() {
this.strategy.algorithm(this);
}
}
好了,现在我们想这样改变需求,ConcreteStrategy2和ConcreteStrategy3里对algorithm算法的实现,需要统计两个新的信息,分别用parameter1和parameter2来表示,同时要统计所有实现策略类里,对algorithm算法调用的次数。
如果没有Context这个角色,又需要做到客户端调用的时候代码改动尽量少,相信大家的做法只好是改抽象策略类和实现策略类里的algorithm算法。但是这样实现策略ConcreteStrategy1可能不愿意了,因为他并不需要新增加的参数;此外,对所有实现类里algorithm算法调用的统计也没有一个统一的入口,需要在每个algorithm实现中,插入一个计数代码。但是如果有了环境角色Context,一切就变得很简单了,我们不需要改动抽象策略类,和实现策略类ConcreteStrategy1,只需要改需求发生变化相关的类,且看下面的代码:
package com.icecode.demo.context;
import com.icecode.demo.strategy.AbstractStrategy;
/**
* 环境角色,主要完成对特定策略的调用
* @author zhanche
*
*/
public class Context {
/**
* 持有对策略的引用
*/
private AbstractStrategy strategy;
/**
* parameter1、parameter2只是ConcreteStrategy2ConcreteStrategy3需要使用的参数,
* 而ConcreteStrategy1不使用
*/
private int parameter1;
private int parameter2;
//count用来统计所有策略的算法algorithm调用的总次数
public static int count = 0;
public Context(AbstractStrategy strategy) {
this.strategy = strategy;
}
public Context(AbstractStrategy strategy, int parameter1, int parameter2) {
super();
this.strategy = strategy;
this.parameter1 = parameter1;
this.parameter2 = parameter2;
}
public int getParameter1() {
return parameter1;
}
public int getParameter2() {
return parameter2;
}
/**
* 算法入口
*/
public void algorithm() {
count++;
System.out.println("------------这是第"+count+"次调用algorithm算法--------");
this.strategy.algorithm(this);
}
}
package com.icecode.demo.strategy.impl;
import com.icecode.demo.context.Context;
import com.icecode.demo.strategy.AbstractStrategy;
/**
* 对算法的第一种具体实现策略
* @author zhanche
*
*/
public class ConcreteStrategy1 extends AbstractStrategy {
@Override
public void algorithm(Context context) {
System.out.println("----------------我是策略一算法----------------");
}
}
package com.icecode.demo.strategy.impl;
import com.icecode.demo.context.Context;
import com.icecode.demo.strategy.AbstractStrategy;
/**
* 对算法的第二种具体实现策略
* @author zhanche
*
*/
public class ConcreteStrategy2 extends AbstractStrategy {
@Override
public void algorithm(Context context) {
System.out.println("----------------我是策略二算法----------------");
System.out.println("------------------我需要的参数parameter1="+context.getParameter1());
System.out.println("------------------我需要的参数parameter2="+context.getParameter2());
}
}
package com.icecode.demo.strategy.impl;
import com.icecode.demo.context.Context;
import com.icecode.demo.strategy.AbstractStrategy;
/**
* 对算法的第三种具体实现策略
* @author zhanche
*
*/
public class ConcreteStrategy3 extends AbstractStrategy {
@Override
public void algorithm(Context context) {
System.out.println("----------------我是策略二算法----------------");
System.out.println("------------------我需要的参数parameter1="+context.getParameter1());
System.out.println("------------------我需要的参数parameter2="+context.getParameter2());
}
}
客户端测试的代码如下:
package com.icecode.demo;
import com.icecode.demo.context.Context;
import com.icecode.demo.strategy.impl.ConcreteStrategy1;
import com.icecode.demo.strategy.impl.ConcreteStrategy2;
import com.icecode.demo.strategy.impl.ConcreteStrategy3;
/**
* 策略模式测试类
* @author zhanche
*
*/
public class Client {
/**
* @param args
*/
public static void main(String[] args) {
Context context = new Context(new ConcreteStrategy1());
context.algorithm();
context = new Context(new ConcreteStrategy2(),100, 200);
context.algorithm();
context = new Context(new ConcreteStrategy3(), 100, 200);
context.algorithm();
}
}
测试输出结果如下:
------------这是第1次调用algorithm算法--------
----------------我是策略一算法----------------
------------这是第2次调用algorithm算法--------
----------------我是策略二算法----------------
------------------我需要的参数parameter1=100
------------------我需要的参数parameter2=200
------------这是第3次调用algorithm算法--------
----------------我是策略三算法----------------
------------------我需要的参数parameter1=100
------------------我需要的参数parameter2=200
由以上分析可见,策略模式中,各个角色的功能都非常重要,虽然环境角色Context可以在某些简单的策略模式中不去使用,但是如果无法预测到各个实现策略功能和需求的变化,以及实现灵活性更好的策略模式,在使用策略模式进行架构时,一定要充分利用所有角色的功能。
分享到:
相关推荐
今天我们将深入探讨其中的一种——策略模式。策略模式是一种行为设计模式,它使你能在运行时改变对象的行为。在Java编程语言中,尤其是在Android平台上,这种模式能够使我们的代码更加灵活、可扩展且易于维护。 ...
行为型模式如观察者(Observer)、策略(Strategy)、模板方法(Template Method)和访问者(Visitor),关注对象的行为和交互。 在Java中,设计模式的应用有助于实现松耦合,提高代码的可扩展性和可维护性。例如,...
“策略模式”(Strategy Pattern)定义了一系列算法,并将每个算法封装起来,使它们可以相互替换。这样可以让算法的变化独立于使用它的客户。在GUI应用中,可能有一个策略类用于决定不同的用户界面布局或交互逻辑。 ...
21. **策略模式(Strategy)**:定义一系列算法,并将每个算法封装起来,使它们可以互相替换,策略对象改变算法。 这个CHM文档不仅涵盖了这些设计模式的定义,还可能包括示例代码、应用场景以及如何在实际项目中...
让我们深入探讨这个"java-Simulation-strategy-game-.rar"压缩包文件所涵盖的知识点。 1. **Java编程**:Java是一种广泛使用的面向对象的编程语言,它具有跨平台性、安全性以及丰富的类库。在这个项目中,Java被...
【标题】"sprintboot-strategy-pattern-demo-master.zip" 是一个使用Spring Boot技术实现的策略模式示例项目,它提供了一种快速理解并应用策略模式的开箱即用的体验。 【描述】"基于springboot做了一个策略模式的...
设计模式之 Strategy(策略) 不同算法各自封装,用户端可随意挑选需要的算法. 设计模式之 Chain of Responsibility(责任链) 各司其职的类串成一串,好象击鼓传花,当然如果自己能完成,就不要推委给下一个. 设计模式...
其次,策略模式(Strategy Pattern)可能体现在坦克的行为策略上,每种类型的坦克都有不同的攻击和移动策略。通过将这些策略封装成独立的类,可以方便地增加新的坦克类型或者修改现有坦克的行为,实现了代码的可复用...
20、STRATEGY—追 MM 有各种策略,根据不同情况选择合适的方法,Strategy 策略模式:定义一系列的算法,把它们一个个封装起来,并且使它们可以互相替换。策略模式让算法的变化独立于使用算法的客户。 21、TEMPLATE ...
开发者可能会使用面向对象的设计模式,例如状态模式(State Pattern)来管理角色的不同行为状态,策略模式(Strategy Pattern)来定义不同的攻击策略,以及观察者模式(Observer Pattern)来实现游戏事件的监听和...
3. 行为型模式:这一类模式涉及到对象之间的通信和职责分配,如观察者模式(Observer)、责任链模式(Chain of Responsibility)、命令模式(Command)以及策略模式(Strategy)。这些模式提供了一种灵活的方式来...
本项目“CatAndDog-RTSgame-master”是一个基于Java开发的即时策略(Real-Time Strategy, RTS)小游戏,非常适合初学者进行课程设计或毕业设计,同时也可作为提升编程技能的实践平台。 一、项目概述 “CatAndDog-...
2. **策略(Strategy)**:除了简单的布尔值,FF4j还支持基于策略的功能切换。例如,你可以定义一个基于用户角色的策略,只有特定角色的用户才能看到新功能。 3. **存储(Store)**:FF4j中的功能开关状态需要持久...
7. 设计模式:在实现ATM系统时,开发者可能会应用到一些常见的设计模式,比如单例模式(Singleton)用于确保只有一个Bank实例,工厂模式(Factory)用于创建Account对象,策略模式(Strategy)用于定义不同的取款...
《帝国时代III》是一款深受策略游戏爱好者喜爱的经典之作,它基于Java源代码开发,为玩家提供了丰富的历史背景和深度的战术策略体验。在这款游戏中,玩家将扮演一个欧洲列强的领袖,引领自己的国家在新世界中扩张、...
全书共分为14章,全面覆盖了四人组(Gang of Four,GoF)定义的23种经典设计模式,包括但不限于Strategy(策略)、Observer(观察者)、Decorator(装饰器)、Abstract Factory(抽象工厂)、Factory Method(工厂...
- **设计模式**:如工厂模式(Factory)、单例模式(Singleton)、策略模式(Strategy)、观察者模式(Observer)等,提高代码复用性和可维护性。 #### 2. Java Web开发 - **数据库操作**:包括Oracle、MySQL、SQL ...
6. **设计模式**:在开发过程中,可能会运用到一些设计模式,如单例模式(Singleton)用于管理游戏资源,工厂模式(Factory)用于创建棋子对象,策略模式(Strategy)用于定义不同的游戏策略等。 7. **软件工程**:...
开发者可能使用了面向对象的设计模式,如状态模式(State Pattern)来管理角色的不同动作状态,以及策略模式(Strategy Pattern)来定义不同的敌人AI行为。 为了实现游戏的动画效果,开发者可能会利用帧动画(Frame...