基本概念
什么是策略模式
策略模式(Strategy),定义了一组算法,将每个算法都封装起来,通过调用同一个父类达到不同策略之间可以的互换。
使用案例
打个通俗的比方,在商场中有打折,满减,返利等不同类型的优惠活动,我们把每一种优惠活动称之为策略。所谓的策略模式其实就是让客户更好的切换这些策略,而不影响整个项目。
再举个开发中经常遇到的例子。在微服务中负载均衡算法有轮询和随机两种。我们是如何切换选择使用哪种模式呐?当然是通过配置文件切换不同的算法。这里就是使用了设计模式中的策略模式。开发人员在配置文件中配置相关算法的配置信息,当程序运行时,会去读取配置文件,然后选择使用哪一种算法,从而在上下文中创建该种模式。
代码示例
示例内容
思维结构
代码所需的类和接口
- 创建一个所有飞机类型(策略)的父类接口。
- 创建不同种类飞机的model(表示不同的策略的model),实现父类策略接口的方法。
- 创建一个环境类,用于调用和管理使用策略模式。
- 创建一个xml配置文件,用于输入选择策略的信息。
- 创建一个xml读取工具。
- 创建客户端,用于调用环境类。
策略模式对应的结构图
具体代码
- 创建一个所有飞机类型(策略)的父类接口。
public interface Plane {
String VERTICAL_TAKE = "垂直起飞";
String LONG_DISTANCE_TAKE = "长距离起飞";
//飞行特征
String SUBSONIC_FLY = "亚音速飞行";
String SUPERSONIC_FLY = "超音速飞行";
public String getPlaneInfo();
}
- 创建不同种类飞机的model(表示不同的策略的model),实现父类策略接口的方法。
/**
* @program: airplane
* @description: 鹞式战斗机
* @author: lichunkai
* @create: 2020-02-15 23:33
**/
public class Harrier implements Plane {
@Override
public String getPlaneInfo() {
String info = new StringBuilder("起飞特征:").append(Plane.VERTICAL_TAKE)
.append(",")
.append("飞行特征:").append(Plane.SUPERSONIC_FLY)
.toString();
return info;
}
}
/**
* @program: airplane
* @description: 客机
* @author: lichunkai
* @create: 2020-02-15 23:33
**/
public class AirPlane implements Plane {
@Override
public String getPlaneInfo() {
String info = new StringBuilder("起飞特征:").append(Plane.LONG_DISTANCE_TAKE)
.append(",")
.append("飞行特征:").append(Plane.SUBSONIC_FLY)
.toString();
return info;
}
}
/**
* @program: airplane
* @description: 歼击机
* @author: lichunkai
* @create: 2020-02-15 23:33
**/
public class Fighter implements Plane {
@Override
public String getPlaneInfo() {
String info = new StringBuilder("起飞特征:").append(Plane.LONG_DISTANCE_TAKE)
.append(",")
.append("飞行特征:").append(Plane.SUPERSONIC_FLY)
.toString();
return info;
}
}
/**
* @program: airplane
* @description: 直升机
* @author: lichunkai
* @create: 2020-02-15 23:32
**/
public class Helicopter implements Plane {
@Override
public String getPlaneInfo() {
String info = new StringBuilder("起飞特征:").append(Plane.VERTICAL_TAKE)
.append(",")
.append("飞行特征:").append(Plane.SUBSONIC_FLY)
.toString();
return info;
}
}
- 创建一个环境类,用于调用和管理使用策略模式。
/**
* @program: airplane
* @description: 特征类:环境类
* @author: lichunkai
* @create: 2020-02-15 23:15
**/
public class TraitEnv {
private Plane plane;
public String getTraitInfo() {
//省略实际开发中可能需要用到的共用代码
if(null == plane){
return "未获取到相关信息";
}
return plane.getPlaneInfo();
}
public void setPlane(Plane plane) {
this.plane = plane;
}
/* public void setTraitInfo(String traitInfo) {
this.traitInfo = traitInfo;
}*/
}
- 创建一个xml配置文件,用于输入选择策略的信息。
<?xml version="1.0" encoding="GBK" ?>
<config>
<!--
<Plane>strategy.lck.model.AirPlane</Plane>
<Plane>strategy.lck.model.Fighter</Plane>
<Plane>strategy.lck.model.Harrier</Plane>
-->
<Plane>strategy.lck.model.Helicopter</Plane>
</config>
- 创建一个xml读取工具。
/**
* xml工具类
*/
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import strategy.lck.model.Plane;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.File;
public class XMLUtil {
//该方法用于从XML配置文件中提取具体类类名,并返回一个实例对象
public static Object getBean(String className) {
try {
//创建文档对象
DocumentBuilderFactory dFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = dFactory.newDocumentBuilder();
Document doc;
//doc = builder.parse(new File("config.xml"));
doc = builder.parse(new File(Plane.class.getResource("/config_strategy.xml").getPath()));
//获取包含类名的文本节点
NodeList nl = doc.getElementsByTagName(className);
Node classNode = nl.item(0).getFirstChild();
String cName = classNode.getNodeValue();
//通过类名生成实例对象并将其返回
Class c= Class.forName(cName);
Object obj = c.newInstance();
return obj;
}
catch(Exception e) {
e.printStackTrace();
return null;
}
}
}
- 创建客户端,用于调用环境类。
/**
* @program: airplane
* @description:客户端
* @author: lichunkai
* @create: 2020-02-15 23:56
**/
public class Client {
public static void main(String[] args) {
//直升机(Helicopter)
//客机(AirPlane)
//歼击机(Fighter)
//鹞式战斗机(Harrier)
TraitEnv traitEnv = new TraitEnv();
Plane airPlane = (Plane) XMLUtil.getBean("Plane");
traitEnv.setPlane(airPlane);
System.out.println(traitEnv.getTraitInfo());
}
}
运行结果如下
结论
策略模式的总结:
策略模式用户算法的自由切换和扩展,他是应用较为广泛的设计模式之一。策略模式对应解决某一问题的一个算法族,允许用户从该算法族中任选一个来解决某一问题,同时可以方便的更换或者增加新的算法。只要涉及到算法的封装、复用和切换都可以考虑使用策略模式。
主要优点:
1、对开闭原则的完美支持
2、复用性强
3、可以多中继承方式实现
4、避免多条件选择语句
主要缺点:
1、客户端必须知道所有的算法逻辑以及却别
2、产生很多的具体策略类
3、无法同时在客户端使用多个策略类